1. 程式人生 > >排序演算法(二)

排序演算法(二)

4、希爾排序 原理:希爾排序是直接插入排序的增強,希爾排序是將整個序列分成若干個組,然後進行分組插入排序,分組的增量gap從大到小,最終必須為1,當增量為1時,也就是插入排序(gap一般是gap = gap/2不斷縮小到1或者gap = (gap/3+1),逐步縮小至1)。 舉例如下: 希爾排序 程式碼實現如下:

public class ShellSort {
    public void shellSort(int[] arr) {
        int gap = arr.length;//增量
        int i,j,temp;
        while (gap > 0) {
            gap = gap/2;//逐步縮小到1
            for (i = gap; i < arr.length; i++) {
                if (arr[i] < arr[i-gap]){
                    temp = arr[i];
                    for(j = i-gap;j >= 0;j -= gap){//將大於temp的元素放到後面
                        if(arr[j] > temp) {
                            arr[j + gap] = arr[j];
                        }else{
                            break;
                        }
                    }
                    arr[j+gap] = temp;
                }
            }
        }
    }
    public static void main(String[] args){
        ShellSort m = new ShellSort();
        int[] arr = new int[]{3,6,2,8,5,7,3,8,5,8,3,7,0};
        m.shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

結果如下: 在這裡插入圖片描述 5、快速排序 快速排序是排序演算法中非常重要的一種,也是經常會考到的一種,同時它有多種改進演算法,需要細細研究。 原理:快速排序是選定一個基準,將大於基準的放在右邊,將小於基準的放在左邊,依次遞迴,最終達到整個序列的有序,一般改進的演算法主要是在選定基準時進行改進。 步驟: (1) 選定基準值後,從後往前進行比較,找到第一個小於基準值的進行交換; (2) 然後,再從前往後找,找到第一個大於基準的進行交換; (3) 直到從前往後的比較陣列下標大於從後往前的比較陣列下標,完成一次迴圈; (4) 接著按著上述迴圈分別比較左右兩側的序列,最終有序。 程式碼實現:

  public void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public int partition(int[] arr,int low,int high){
            int base = arr[low];//基準
            while(low < high){
                while(low < high && arr[high] >= base){//右側掃描,找到第一個小於base的元素
                    high--;
                }
                arr[low] = arr[high];
                while(low < high && arr[low] <= base){//左側掃描,找到第一個大於base的元素
                    low++;
                }
                arr[high] = arr[low];
            }
            arr[low] = base;
            return low;
        }
        public void quickSort(int[] arr,int low,int high){
            if(low < high){
                int p = partition(arr,low,high);
                quickSort(arr,low,p-1);
                quickSort(arr,p+1,high);
            }
        }

快速排序演算法的優化: (1)隨機選定基準 當序列幾乎有序時,基準每次選定為第一個值時,快排的效能不好,因此,將基準選擇改為隨機的。(但當整個序列是亂序的,排序效能可能會下降,有些許的運氣成分) 程式碼實現:

 public void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public int partition(int[] arr,int low,int high){
            //排序前將base與隨機的數進行交換,此時,基準值具有隨機性了
            swap(arr,low,(int)(Math.random()*(high-low+1)+low));
            int base = arr[low];
            int j = low;
            for(int i = low+1;i <= high;i++){
                if(base > arr[i]){
                    j++;
                    swap(arr,i,j);
                }
            }
            swap(arr,low,j);
            return j;
        }
        public void quickSort(int[] arr,int low,int high){
            if(low < high){
                int p = partition(arr,low,high);
                quickSort(arr,low,p-1);
                quickSort(arr,p+1,high);
            }
        }

(2)兩路快排 快速排序時,將大於基準的排在基準右邊,將小於等於基準的排在基準左邊。 程式碼實現:

 public void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public int partition(int[] arr,int low,int high){
            swap(arr,low,(int)(Math.random()*(high-low+1)+low));
            int base = arr[low];//每次將基準丟擲,相當於對[low+1,...,high]的排序
            int i = low+1;//i表示對[low+1,...,i)的比base小(等)的部分排序
            int j = high;//j表示對(j,...,high]的比base大(等)的部分排序
            while(true){
                while (i <= high && arr[i] < base){//左側掃描,找到第一個比base大的元素
                    i++;
                }
                while(j >= low && arr[j] > base){//右側掃描,找到第一個比base小的元素
                    j--;
                }
                if(i > j){
                    break;
                }
                swap(arr,i++,j--);
            }
            swap(arr,low,j);
            return j;
        }
        public void quickSort(int[] arr,int low,int high){
            if(low < high){
                int p = partition(arr,low,high);
                quickSort(arr,low,p-1);
                quickSort(arr,p+1,high);
            }
        }

(3)配合插入排序使用 快排是採用分治思想,也就是遞迴將問題不斷縮小規模進而解決,但當資料規模小,整個序列近乎有序時,採用“遞迴+不穩定”的方式,效率不一定好,此時利用插入排序,效果會更好一些。 程式碼如下:

 public void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public void insertSort(int[] arr,int m,int n){
            int i,j,temp = 0;
            for(i = m+1;i <= n;i++){
                    temp = arr[i];
                for(j = i-1;j >= 0 && arr[j] > temp;j--){//將大於基準的後移
                        arr[j+1] = arr[j];
                }
                arr[j+1] = temp;
            }
        }
        public int partition(int[] arr,int low,int high){
            swap(arr,low,(int)(Math.random()*(high-low+1)+low));
            int base = arr[low];
            int j = low;
            for(int i = low+1;i <= high;i++) {
                if (base > arr[i]) {
                    j++;
                    swap(arr, i, j);
                }
            }
            swap(arr,low,j);
            return j;
        }
        public void quickSort(int[] arr,int low,int high,int k){
            if(low >= high) return;
            if(high-low < k){
                insertSort(arr,low,high);
                return;
            }
            int p = partition(arr,low,high);
            quickSort(arr,low,p-1,k);
            quickSort(arr,p+1,high,k);
        }

(4)三路快排 當整個序列中有大量的重複資料時,將整個序列分成小於基準,等於基準,大於基準三部分進行排序,也就是所稱的三路快排。用指標從前到後掃描,如果cur指向的數小於base,那麼交換arr[cur]和arr[i]的值,然後i++,cur++;cur指向的數等於base, 那麼cur++;cur指向的數大於base,那麼交換arr[cur]和arr[j]的值,然後j–。當cur > j的時候說明三路都已經完成。 程式碼實現如下:

public static void swap(int[] arr,int i,int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public int[] partition(int[] arr,int low,int high){
            swap(arr,low,(int)(Math.random()*(high-low+1)+low));
            int temp = arr[low];
            int i = low;
            int j = high;
            int cur = i;
            while(cur <= j){
                if(arr[cur] < temp){
                    swap(arr,cur++,i++);
                }else if(arr[cur] == temp){
                    cur++;
                }else{
                    swap(arr,cur,j--);
                }
            }
            return new int[]{i-1,j+1};//[i...j]都等於base,子問題就只需要解決i左邊和j右邊就行了
        }
        public void quickSort(int[] arr,int low,int high){
            if(low < high){
                int[] brr = partition(arr,low,high);
                quickSort(arr,low,brr[0]);
                quickSort(arr,brr[1],high);
            }
        }
    }

三路快排可以避免很多重複元素再次參與遞迴,對於大量重複的待排序列,效率有所提升。