排序

一、冒泡排序和插入排序(简单的排序)

冒泡排序原理:依次比较相邻的两个数,将更大的数移到右侧,这样进行一轮,被移到最右边的数肯定是这组数字里最大的。再进行一轮,倒数第二位置放的一定是第二大的数字...以此类推。

时间复杂度分析:顺序 T=O(n)  逆序 T=O(n^{2})

JAVA语言:

 public static void bubbleSort(int[] arr){
        //一定要记住判断边界条件
        if(arr==null||arr.length<2){
            return;
        }
        //需要进行arr.length趟比较

        for(int i = 0 ;i<arr.length-1;i++){
            //第i趟比较
            for(int j = 0 ;j<arr.length-i-1;j++){
                //开始进行比较,如果arr[j]比arr[j+1]的值大,那就交换位置
                if(arr[j]>arr[j+1]){
                    int temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }

        }
}

插入排序:每一步将一个待排序的数据插入到前面已经排好序的有序序列中,直到插完所有元素为止。

时间复杂度:平均情况:N^2/4 次比较,N^2/4次交换;

最坏情况:N^2/2次比较,N^2/2次交换;

最好情况:N-1次比较,0次交换;

平均时间复杂度: O(N^2)

最好时间复杂度: O(N)

JAVA代码实现:

public class InsertionSorted extends AbstractSorted {

    public <T extends Comparable<T>> void sort(T[] arrays) {

        // 开始一趟有序列扫描,当前i之前的数据视为有序序列
        for (int i = 1; i < arrays.length; i ++) {

            // 寻找位置,从后向前与i之前的有序序列一一比较,找到比当前元素小的位置
            for (int j = i; j >= 0 && less(arrays[j], arrays[j - 1]; j --)) {
                exch(arrays, j, j - 1);
            }

            String msg = String.format("第%2d次 ->: %s", i, Arrays.toString(arrays));
            System.out.println(msg);
        }

    }
}

二、希尔排序(shell sort)

希尔排序有时也被叫做缩减增量排序,有一个增量序列来逐步排序。例如下面的例子,先进行间隔5排序,即81、35、41三个数。然后进行3排序,最后进行1排序,逐步地完成排序。

JAVA代码实现:

    public static void Shellsort(int [] a){

        for(int gap = a.length /2;gap > 0;gap /= 2){
            //这里gap指的就是增量序列,这里选择的是不断除以2,这样增量序列其实并不好
            for(int i=gap;i<a.length;i++){
                int tmp = a[i];//新建一个临时变量用于交换
                for(int j=i;j >= gap && tmp.compareTo(a[j-gap]);j-=gap){
               //将间隔gap两个元素比较
                    a[j] = a[j-gap]; //交换
                }
                a[j] = tmp; //不调换
            }
        }
    }

使用希尔排序依赖于增量序列的选择,最坏的时间复杂度也是T=O(n^{^{2}})

Hibbard增量:1、3、7...2^{_{k}}-1  ,这样最坏时间复杂度T=O(n^{\frac{3}{2}})

Sedgewick增量:9*4^{i}-9*2^{i}+1 或者4^{i}-3*2^{i}+1 这样最坏的时间复杂度是T=O(n^{\frac{4}{3}})

三、堆排序

在堆排序前,先来看选择排序

算法思想:选择排序,从头至尾扫描序列,找出最小的一个元素,和第一个元素交换,接着从剩下的元素中继续这种选择和交换方式,最终得到一个有序序列。

举个例子方便理解:

 

JAVA实现:

public static void selectSort(int [] arr,int n){
        for (int i = 0; i < n - 1; i++) {
            int index = i;
            int j;
            // 找出最小值得元素下标
            for (j = i + 1; j < n; j++) {
                if (arr[j] < arr[index]) {
                    index = j;
                }
            }
            int tmp = arr[index];
            arr[index] = arr[i];
            arr[i] = tmp;
            System.out.println(Arrays.toString(arr));
        }
}

堆排序:

堆本身就有最小堆和最大堆,所以这里就直说最小堆。用DeleteMin()弹出最小元素,赋值给有序序列中第一个元素,再在无序序列里弹出最小元素,赋值给有序序列中第二个元素,依次类推。

时间复杂度 T(N) = O(NlogN),但需要O(N)的空间。

JAVA实现:

public void Heapsort(int [] a){
        int [] arr = Arrays.copyOf(a,a.length);
        int len = a.length;
        buildMaxHeap(arr,len);
        for(int i = len - 1;i>0;i--){
            swap(arr,0,i);
            len--;
            heapify(arr,0,len);
        }

        }
    private void buildMaxHeap(int[] arr,int len){//创建一个最大堆
        for(int i = (int)Math.floor(len/2);i>=0;i--){
//第一个非叶子结点 arr.length/2-1
            heapify(arr,i,len);
        }
    }
    private void heapify(int[] arr,int i,int len){//堆化,就是乱序堆变成最大堆
//第一个参数是数组,第二个参数你要讨论节点,第三个参数是待处理需要排序的长度
        int left = 2*i+1;
        int right = 2*i+2;
        int largest = i;
        if(left <len && arr[left] > arr[largest]){
            largest = left;
        }
        if(right <len && arr[right] > arr[largest]){
            largest = right;
        }
        if(largest != i){
            swap(arr,largest,i);
            heapify(arr,largest,len);
        }

    }
    private void swap(int[] arr,int i,int j ){//用于交换的方法
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

四、归并排序

归并排序基本排序原理:典型的基于分治的递归算法。它不断地将原数组分成大小相等的两个子数组,最终当划分的子数组大小为1时,将划分的有序子数组合并成一个更大的有序数组。先把两个子数组排成有序后,再将其合并成一个有序序列。

JAVA实现:

    public void Mergesort(int [] a,int [] tmp,int left,int right){
        if(left < right){
            int center = (left+right)/2;
            Mergesort(a,tmp,left,center);
            Mergesort(a,tmp,center+1,right);
            merge(a,tmp,0,center+1,right);
        }

        }
    private void merge (int[] a,int[] tmp,int leftPos,int rightPos,int rightEnd){//创建一个最大堆
        int leftEnd = rightPos - 1;
        int temPos =leftPos;
        int numElements = rightEnd - leftPos + 1;
        while (leftPos<=leftEnd && rightPos<=rightEnd){
            if(a[leftPos].compareTo(a[rightPos]) <= 0){
                tmp[temPos++] = a[leftPos];
            }else {
                tmp[temPos++] = a[rightPos];
            }
        }
        while (leftPos<=leftEnd){
            tmp[temPos++] = a[leftPos];
        }
        while (rightPos<=rightEnd){
            tmp[temPos++] = a[rightPos];
        }
        //tmp数组就已经按从小到大排序好了
    }

五、快速排序(面试重点)

快速排序的基本思想原理:

1、先从数列中取出一个数作为基准数

2、分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边

3、再对左右区间重复第二步,直到各区间只有一个数

这里选取的主元也就是基准数的大小就很关键,会直接影响到时间复杂度,一般选取方法是:使用左端、右端、和中心位置上的三个元素的中值作为主元。

举个例子,这里的例子就用书上的例子了:

之后依次对6左边和右边两堆数字再次递归调用快速排序算法。

JAVA实现:

public static void main(String[] args) {
	
	    int [] arr = {65,58,95,10,57,62,13,106,78,23,85};
	    System.out.println("排序前:"+Arrays.toString(arr));
	    quickSort(arr,0,arr.length-1);
	    System.out.println("排序后:"+Arrays.toString(arr));
		
}
	
public static void quickSort(int [] arr,int left,int right) {
	      int pivot = 0;
	      if(left < right) {
	         pivot = partition(arr,left,right);
	         quickSort(arr,left,pivot-1);
	         quickSort(arr,pivot+1,right);
	      }
}
	
private static int partition(int[] arr,int left,int right) {
 
	      int key = arr[left];
	      while(left < right) {
	         while(left < right && arr[right] >= key) {
	            right--;
	         }
	         arr[left] = arr[right];
	         while(left < right && arr[left] <= key) {
	            left++;
	         }
	         arr[right] = arr[left];
	      }
	      	 arr[left] = key;
	         return left;
 
}

这里有一种情况:如果元素正好等于pivot怎么办?

我们选择停下来交换这两个元素

tips:当需要排序的数据规模充分小的时候,直接使用插入排序更快;可以使用一个if语句进行判定一下数据规模。

六、基数排序

1.桶排序

基数排序建立在桶排序的基础上,先说说桶排序。

假设我们有N个学生,他们的成绩是0到100之间的整数,如何在线性时间内将学生按成绩排序?

我们准备0到100,共101个桶

将学生成绩对应的放入桶中,然后依照桶的顺序将学生成绩依次输出。

2.基数排序

但是,如果我们有10个整数,每个整数值在0到999之间(M),M>>N,我们不可能去建立1000个桶来放置这10个数。

那我们怎么做呢?

只建立10个桶,0,1,2,3...9

分别按照个位,十位,百位的数字分别插入桶中进行排序。

JAVA实现:


public class BasicSort {
    public static void basicSort(int[] array) {
        //创建叠加数组
        List<ArrayList> dyadic = new ArrayList<>();
        //给大数组dyadic添加子数组
        for(int i = 0; i < 10; i++) {
            ArrayList<Integer> arr = new ArrayList<>();
            dyadic.add(arr);
        }
 
        //找出数组中的最大值
        int max = 0;
        for(int i = 0; i <array.length; i++) {
            if(array[i] > max) {
                max = array[i];
            }
        }
 
        //判断最大值为几位数,其位数就是应该循环的次数
        int times = 0;
        while(max > 0) {
            max /= 10;
            times++;
        }
 
        //循环times次,每次将对应位的数分配到相应的自数组中
        for(int i = 0; i < times; i++) {
            for(int j = 0; j < array.length; j++) {
                //找出每个数对应的位的数值
                int x = array[j] % (int)Math.pow(10, i + 1) / (int)Math.pow(10, i);
                //将该数组作为下标,找到对应的子数组
                ArrayList arr = dyadic.get(x);
                //将该元素添加到子数组中
                arr.add(array[j]);
                //因为子数组改变,因此更新大数组
                dyadic.set(x, arr);
            }
 
            //将重新排好的子数组的值依次将需要被排序的数组的值覆盖
            int index = 0;   //用index作为数组array的下标
            //将子数组依次遍历,将每个子数组中的元素添加到array中
            for(int k = 0; k < 10; k++) {
                //当下标为k的子数组中有元素时
                while(dyadic.get(k).size() > 0) {
                    //得到该数组
                    ArrayList arr = dyadic.get(k);
                    ///将该数组的第一个元素添加到array中
                    array[index] = (int)arr.get(0);
                    //移除子数组中的第一个元素,这样就能在第一个元素被使用之后,后面元素替换
                    arr.remove(0);
                    //将array数组中下标也后移一位
                    index++;
                }
            }
        }
    }

七、排序算法比较

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值