排序下篇(快速排序、并归排序、堆排序、桶排序/基数排序)

5.快速排序

(1)原理:

在要排序的一组数中,通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

2)图解这第一次循环5

小黑框是已确定位置,大黑框中使用递归

(3)代码

public static void quickSort1(int[] arr) {

        quickSort2(arr, 0, arr.length - 1);

    }

    public static void quickSort2(int[] a, int low, int high) {

        int start = low;

        int end = high;

        int key = a[low];

        while (end > start) {

            //从后往前比较

            while (end > start && a[end] >= key)  //如果没有比关键值小的,比较下一个,直到有比关键值小的交换位置,然后又从前往后比较

                end--;

            if (a[end] <= key) {

                int temp = a[end];

                a[end] = a[start];

                a[start] = temp;

            }

            //从前往后比较

            while (end > start && a[start] <= key)//如果没有比关键值大的,比较下一个,直到有比关键值大的交换位置

                start++;

            if (a[start] >= key) {

                int temp = a[start];

                a[start] = a[end];

                a[end] = temp;

            }

        }

        //此时第一次循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,右边的值都比关键值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用

        if (start > low) quickSort2(a, low, start - 1);//左边序列。第一个索引位置到关键值索引-1

        if (end < high) quickSort2(a, end + 1, high);//右边序列。从关键值索引+1到最后一个

}

 

6.并归排序

(1)原理:

该算法是采用分治法的一个非常典型的应用。首先我们使用分割的办法将这个序列分割成一个个已经排好序的子序列。然后再利用归并的方法将一个个的子序列合并成排序好的序列,称为2-路归并。 

2)图解

3)代码

public static void MergeSort(int[] arr) {

        sort(arr,0,arr.length-1);

    }

        public static void sort(int[] data, int left, int right) {

        if(left<right){

            //找出中间索引

            int center=(left+right)/2;

            //对左边数组进行递归

            sort(data,left,center);

            //对右边数组进行递归

            sort(data,center+1,right);

            //合并

            merge(data,left,center,right);

 

        }

    }

    public  static void merge(int[] data, int left, int center, int right) {

        int [] tmpArr=new int[data.length];

        int mid=center+1;

        int third=left;

        int tmp=left;

        while(left<=center&&mid<=right){

            if(data[left]<=data[mid]){

                tmpArr[third++]=data[left++];

            }else{

                tmpArr[third++]=data[mid++];

            }

        }

        //剩余部分依次放入中间数组

        while(mid<=right){

            tmpArr[third++]=data[mid++];

        }

        while(left<=center){

            tmpArr[third++]=data[left++];

        }

        //将中间数组中的内容复制回原数组

        while(tmp<=right){

            data[tmp]=tmpArr[tmp++];

        }

           }

 

7.堆排序

(1)原理:

指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。数组可以根据索引直接获取元素,时间复杂度为O(1),也就是常量,因此对于取值效率极高。 

  1. 图解

初始序列:46,79,56,38,40,84

建堆:

交换,从堆中踢出最大数

依次类推:最后堆中剩余的最后两个结点交换,踢出一个,排序完成。

按照索引便可取出数字

 

  1. 代码

 

 public static void heapSort(int[] arr){

            buildMaxHeapify(arr);

            heapSort1(arr);

            print(arr);

        }

 

        private static void buildMaxHeapify(int[] data){

//没有子节点的才需要创建最大堆,从最后一个的父节点开始

            int startIndex=getParentIndex(data.length-1);

//从尾端开始创建最大堆,每次都是正确的堆

            for(int i=startIndex;i>=0;i--){

                maxHeapify(data,data.length,i);

            }

        }

 

       //创建最大堆

        private static void maxHeapify(int[] data,int heapSize,int index){

//当前点与左右子节点比较

            int left=getChildLeftIndex(index);

            int right=getChildRightIndex(index);

 

            int largest=index;

            if(left<heapSize&&data[index]<data[left]){

                largest=left;

            }

            if(right<heapSize&&data[largest]<data[right]){

                largest=right;

            }

//得到最大值后可能需要交换,如果交换了,其子节点可能就不是最大堆了,需要重新调整

            if(largest!=index){

                int temp=data[index];

                data[index]=data[largest];

                data[largest]=temp;

                maxHeapify(data,heapSize,largest);

            }

        }

 

       //排序,最大值放在末尾,data虽然是最大堆,在排序后就成了递增的

        private static void heapSort1(int[] data){

//末尾与头交换,交换后调整最大堆

            for(int i=data.length-1;i>0;i--){

                int temp=data[0];

                data[0]=data[i];

                data[i]=temp;

                maxHeapify(data,i,0);

            }

        }

 

        //父节点位置

        private static int getParentIndex(int current){

            return(current-1)>>1;

        }

//左子节点position注意括号,加法优先级更高

        private static int getChildLeftIndex(int current){

            return(current<<1)+1;

        }

 

       //右子节点position

        private static int getChildRightIndex(int current){

            return(current<<1)+2;

        }

 

        private static void print(int[] data){

            int pre=-2;

            for(int i=0;i<data.length;i++){

                if(pre<(int)getLog(i+1)){

                    pre=(int)getLog(i+1);

                    System.out.println();

                }

                System.out.print(data[i]+"|");

            }

        }

 

        //以2为底的对数

        private static double getLog(double param){

            return Math.log(param)/Math.log(2);

        }

    }

8.桶排序/基数排序

(1)原理:

是将阵列分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的阵列内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。

 

2)图解

3)代码

 public static void radixSort(int[] A){

        sort(A,A.length);

    }

    public static void sort(int[] A, int n){

        int max=A[0]; int min=A[0]; for(int i=0;i<n;i++){ if(A[i]>max) max=A[i];

            if(A[i]<min) min=A[i]; } //定义桶数组B并初始化

         int[] B= new int[max-min+1]; for(int i=0;i<max-min+1;i++) B[i]=0;

        // 把数组A的元素装到对应桶里

         for(int i=0;i<n;i++){ B[A[i]-min]++; }

        // 把所有桶倒出来

         for(int i=0,j=0;j<max-min+1;j++){

         for(int k=B[j];k>0;k--){ A[i++]=j+min; } } }

总结

猜你喜欢

转载自blog.csdn.net/weixin_42500488/article/details/84102189