1. 程式人生 > >小橙書閱讀指南(六)——快速排序和三向切分快速排序

小橙書閱讀指南(六)——快速排序和三向切分快速排序

指針 rri 位置 容易 情況 相關鏈接 created style 了解

算法描述:快速排序是一種分治的排序算法。它將數組分為兩個子數組,並將兩部分獨立的排列。快速排序和歸並排序是互補的:歸並排序將數組分成兩個子數組分別排序,並將子數組歸並以將整個數組排序;而快速排序將數組排序的方式則是當兩個子數組都有序時整個數組也就自然有序了。

算法圖示:

技術分享圖片

算法解釋:選擇標的元素(5)並且便利數組,將素有小於5的元素都安排在它的左側,而大於5的元素都安排在它的右側。之後再通過遞歸的方法分別處理左邊的子數組和右邊的子數組。

快速排序的算法難點在於盡量不要使用額外的存儲空間(即保證原地切分)以及如何處理與標的元素(5)相等的元素。

Java代碼示例:

package algorithms.sorting;

import algorithms.Sortable; import algorithms.common.Arrays; import algorithms.common.ArraysGenerator; /** * Created by learnhow on 2018/8/17. */ public class Quick extends Arrays<Integer> implements Sortable<Integer> { @Override public void sort(Integer[] array) { sort(array,
0, array.length - 1); } // 遞歸體 private void sort(Integer[] array, int lo, int hi) { if (lo >= hi) { return; } int part = partition(array, lo, hi); sort(array, lo, part - 1); sort(array, part + 1, hi); } // 切分算法 private int partition(Integer[] array, int
lo, int hi) { // 限制數組的遍歷範圍 array(lo, hi]; int i = lo; int j = hi + 1; int temp = array[lo]; while (true) { while (array[++i] < temp) { if (i == hi) { break; } } while (array[--j] > temp) { if (j == lo) { break; } } if (i >= j) { break; } exchange(array, i, j); } exchange(array, lo, j); return j; } public static void main(String[] args) { Integer[] arr = ArraysGenerator.generate(10, 0, 10); Quick quick = new Quick(); quick.sort(arr); System.out.println(java.util.Arrays.toString(arr)); } }

Qt/C++代碼示例:

void Quick::sort(int *arr, int lo, int hi)
{
    if (lo >= hi) {
        return;
    }
    int part = partition(arr, lo, hi);
    sort(arr, lo, part - 1);
    sort(arr, part + 1, hi);
}

int Quick::partition(int *arr, int lo, int hi)
{
    int i = lo;
    int j = hi + 1;
    int targetValue = arr[lo];
    while (true) {
        while (arr[++i] < targetValue) {
            if (i == hi) {
                break;
            }
        }
        while (arr[--j] > targetValue) {
            if (j == lo) {
                break;
            }
        }
        if (i >= j) {
            break;
        }
        // 交換元素位置
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    int temp = arr[lo];
    arr[lo] = arr[j];
    arr[j] = temp;

    return j;
}

算法性能分析:

快速排序速度優勢子啊與它的比較次數很少,但是排序效果還是依賴切分數組的情況,並且如果在數組中存在大量重復元素。算法性能還可以大幅度提升。下面我們給出三向切分的快速排序算法。大家作為了解即可。

算法圖示:

技術分享圖片

算法解釋:在三向切分快速排序算法中我們定義了三個指針變量:gt 小於 V 的數組的上界,i 等於 V 的數組的上界,gt 大於 V 的數組的下界。以及一段中間數組array[i, gt]。一次遍歷的判斷過程如下:

  • array[i]小於V,將array[lt]和array[i]交換,並將lt和i分別加1;
  • array[i]大於V,將array[gt]和array[i]交換,並將gt減1;
  • array[i]等於V, 將i單獨加1;

Java代碼示例:

package algorithms.sorting;

import algorithms.Sortable;
import algorithms.common.Arrays;
import algorithms.common.ArraysGenerator;

public class Quick3way extends Arrays<Integer> implements Sortable<Integer> {
    @Override
    public void sort(Integer[] array) {
        sort(array, 0, array.length - 1);
    }

    private void sort(Integer[] array, int lo, int hi) {
        if (hi <= lo) {
            return;
        }
        int temp = array[lo]; // 比較標的
        int lt = lo; // 小於temp的index記錄
        int eq = lo + 1; // 等於temp的index記錄
        int gt = hi; // 大於temp的index記錄

        while (eq <= gt) {
            if (array[eq] < temp) {
                exchange(array, lt++, eq++);
            } else if (array[eq] > temp) {
                exchange(array, eq, gt--);
            } else {
                eq++;
            }
        }
        sort(array, lo, lt - 1);
        sort(array, gt + 1, hi);
    }

    public static void main(String[] args) {
        Integer[] arr = ArraysGenerator.generate(10, 0, 10);
        Quick3way quick3way = new Quick3way();
        quick3way.sort(arr);

        System.out.println(java.util.Arrays.toString(arr));
    }
}

Qt/C++代碼示例(略)

三向切分快速排序針對特定數組能夠起到非常優秀的效果,但是在處理下標越界的事情往往容易弄錯。因此在考慮算法性能的同時,保證正確應該優先考慮。

相關鏈接:

Algorithms for Java

Algorithms for Qt

小橙書閱讀指南(六)——快速排序和三向切分快速排序