1. 程式人生 > >排序下篇(快速排序、並歸排序、堆排序、桶排序/基數排序)

排序下篇(快速排序、並歸排序、堆排序、桶排序/基數排序)

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; } } }

總結