日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

數(shù)據(jù)結(jié)構(gòu)與算法——排序算法(5)——快速排序

 印度阿三17 2019-09-07

目錄

1.基本思想

1.1 基本思想

1.2 使用分治策略的三個步驟分析快速排序

1.3 歸并排序與快速排序比較

2.圖解原理

3.代碼實現(xiàn)

3.1 實現(xiàn)方式1:選取中間值作為基準(zhǔn)值

3.2 實現(xiàn)方式2:選取最右邊的值作為基準(zhǔn)值

4.性能分析


1.基本思想

1.1 基本思想

  • 快速排序是基于分治策略的一種排序

  • 基本思想

    • 1.基準(zhǔn):先從數(shù)列中取出一個數(shù)作為基準(zhǔn)數(shù)。

    • 2.分區(qū)過程:將比這個數(shù)大的數(shù)全放到它的右邊,小于或等于它的數(shù)全放到它的左邊。

    • 3.再對左右區(qū)間重復(fù)第一、二步,直到各區(qū)間只有一個數(shù)。  

  • 注意:快速排序在分的時候,就進(jìn)行了“排序”處理

1.2 使用分治策略的三個步驟分析快速排序

  • 1.分解:將數(shù)組A[p..r]劃分為兩個(可能為空)子數(shù)組A[p...q-1]和A[q 1...r],使得A[p...q-1]中的每一個元素都小于等于A[q],而A[q]也小于等于A[q 1...r]中的每個元素

  • 2.解決:通過遞歸調(diào)用快速排序,對子數(shù)組A[p...q-1]和A[q 1...r]進(jìn)行排序

  • 3.合并:因為子數(shù)組都是原址排序的,所以不需要合并操作:數(shù)組A[p...r]已經(jīng)有序

1.3 歸并排序與快速排序比較

  • 歸并排序?qū)?shù)組分成連個子數(shù)組分別排序,并將有序的子數(shù)組歸并以將整個數(shù)組排序;而快速排序是當(dāng)兩個子數(shù)組都有序時,整個數(shù)組也就自然有序了

  • 歸并排序在分的時候不進(jìn)行任何處理,而快速排序在分的時候,要進(jìn)行元素交換,保正左邊元素小于基準(zhǔn)值,右邊元素大于基準(zhǔn)值

  • 歸并排序遞歸調(diào)用發(fā)生在處理整個數(shù)組之前,快速排序遞歸調(diào)用發(fā)生在處理整個數(shù)組之后

  • 歸并排序在合并的時候完成排序,快速排序在分的時候完成排序,合并時,什么都不做

  • 在歸并排序中,一個數(shù)組被等分為兩半,在快速排序中,切分的位置取決于數(shù)組的內(nèi)容

2.圖解原理

3.代碼實現(xiàn)

關(guān)鍵詞解釋

  • pivot:基準(zhǔn)元素

  • partition:將整個數(shù)組進(jìn)行切分

3.1 實現(xiàn)方式1:選取中間值作為基準(zhǔn)值

public class QuickSort extends Sort {
    /**
     * sort方法為外界提供了統(tǒng)一的調(diào)用“接口”
     * @param arr
     */
    @Override
    public void sort(Comparable[] arr) {
        quickSort(arr,0,arr.length-1);
    }

    
    private void quickSort(Comparable[] arr,int left,int right){
        if(left >= right){
            return;
        }
        //經(jīng)過切分后基準(zhǔn)值最終索引
        int pivot = partition(arr,left,right);
        //對pivot左邊進(jìn)行遞歸切分
        quickSort(arr,left,pivot-1);
        //對pivot右邊進(jìn)行遞歸切分
        quickSort(arr,pivot 1,right);
    }

    /**
     * 切分?jǐn)?shù)組,切分為左邊小于pivot,右邊大于pivot
     * @param arr
     * @param left
     * @param right
     * @return  返回中間基準(zhǔn)值pivot的索引值
     */
    private int partition(Comparable[] arr,int left,int right){
        //我們這里取中間值基準(zhǔn)值,
        Comparable pivot = arr[(left right)/2];

        //定義兩個指針指向兩邊
        int move_right = left;
        int move_left = right;
        /**
         * 當(dāng)兩個指針相等或向右移動的指針大于向左移動的指針的時候,代表遍歷完所有的元素
         *
         * while循環(huán)的目的是讓比pivot小的元素在pivot的左邊,比pivot大的元素在右邊
         */
        while(move_right < move_left){

            /**
             * 右移指針尋找大于等于pivot的值,
             *
             *
             * 最后的情況就是pivot左邊全部是小于pivot的,這時,找到的是pivot值
             * 然后右邊找到的是非pivot的話,就發(fā)生交換,等于pivot的話,move_right不一定等于move_left
             *
             * 因為有可能有多個重復(fù)的與pivot本身相同的值,所以交換后,我們也要讓指針繼續(xù)移動,
             * 確保通過指針的相對大小判斷循環(huán)結(jié)束,否則,在交換后,不移動指針,可能會形成死循環(huán)(兩個指針同時指向兩個相鄰的pivot)
             *
             */

            while(arr[move_right].compareTo(pivot) < 0){
                //右移指針指向的值小于pivot的值,不用交換,指針繼續(xù)右移
                move_right  ;
            }

            /**
             * 左移指針尋找小于等于pivot的值
             *
             * 同上,最后的情況就是找到pivot
             */
            while (arr[move_left].compareTo(pivot) > 0){
                //左移指針指向的值大于pivot的值,不用交換,指針繼續(xù)左移
                move_left--;
            }

            //兩個指針遍歷完所有元素,結(jié)束循環(huán)
            if(move_right >= move_left)
                break;

            //交換兩個指針指向的值
            swap(arr,move_right,move_left);

            /**
             * 迭代,讓指針移動
             *
             * 指針指向的值為pivot的時候,讓另一個指針靠近自己,以最終結(jié)束循環(huán)
             */
            //如果右移指針指向pivot,左移指針--
            if(arr[move_right].compareTo(pivot) == 0){
                move_left--;
            }

            //如果左移指針指向pivot,右移指針--
            if(arr[move_left].compareTo(pivot) == 0){
                move_right  ;
            }

        }

        /**
         * 最終我們需要返回用于切分的基準(zhǔn)值的最終索引
         *
         * 循環(huán)過后,兩個指針總有一個是指向pivot的,通過以下判斷,最終返回pivot的索引
         */
        int pivotIndex;

        if(arr[move_right] == pivot){
            pivotIndex = move_right;
        }else{
            pivotIndex = move_left;
        }

        return pivotIndex;
    }
}
import java.util.Arrays;

public class SortTest {
    public static void main(String[] args) {
        Integer[] arr = new Integer[]{8,9,1,7,2,3,5,5,4,4,5,6,0};
        System.out.println("未排序的數(shù)組為:");
        System.out.println(Arrays.toString(arr));
        QuickSort quickSort = new QuickSort();
        quickSort.sort(arr);
        System.out.println("排序后的數(shù)組為:");
        System.out.println(Arrays.toString(arr));
    }
}

3.2 實現(xiàn)方式2:選取最右邊的值作為基準(zhǔn)值

public class QuickSort extends Sort {
    /**
     * sort方法為外界提供了統(tǒng)一的調(diào)用“接口”
     *
     * @param arr
     */
    @Override
    public void sort(Comparable[] arr) {
        quickSort(arr, 0, arr.length - 1);
    }
    
    private void quickSort(Comparable[] arr, int left, int right) {
        if (left >= right) {
            return;
        }
        //經(jīng)過切分后基準(zhǔn)值最終索引
        int pivot = partition(arr, left, right);
        //對pivot左邊進(jìn)行遞歸切分
        quickSort(arr, left, pivot - 1);
        //對pivot右邊進(jìn)行遞歸切分
        quickSort(arr, pivot   1, right);
    }

    /**
     * 切分?jǐn)?shù)組,切分為左邊小于pivot,右邊大于pivot
     *
     * @param arr
     * @param left
     * @param right
     * @return 返回中間基準(zhǔn)值pivot的索引值
     */
    private int partition(Comparable[] arr, int left, int right) {
        //我們這里取最后一個值作為基準(zhǔn)值,
        Comparable pivot = arr[right];

        //定義一個指針,右移記錄,i指向小于等于pivot的最后一個值的下一個位置
        int i = left;  //初始化為起始位置

        //遍歷left到right-1之間的元素
        for (int j = left; j <= right - 1; j  ) {

            if (arr[j].compareTo(pivot) <= 0) {
                //當(dāng)前遍歷元素小于等于基準(zhǔn)值
                //交換小于等于pivot的最后一個值的下一個位置的值(無所謂它的大?。┡c當(dāng)前遍歷元素
                swap(arr, i, j);
                //此時小于等于pivot的最后一個值為交換后的值,所以i 1,在i前面的表示都小于等于pivot
                i  ;
            }
        }
        //這樣遍歷完,小于等于pivot的值都被交換到了i位置的前面,最終i位置本身指向的是大于pivot的

        //這時,交換i位置處的值和pivot,最終pivot左邊是小于等于它的值,pivot右邊是大于它的值
        swap(arr, i, right);

        //最終返回pivot最終的索引值,即i
        return i;
    }
}
import java.util.Arrays;

public class SortTest {
    public static void main(String[] args) {
        Integer[] arr = new Integer[]{8,9,1,7,2,3,5,5,4,4,5,6,0,0,343,6789};
        System.out.println("未排序的數(shù)組為:");
        System.out.println(Arrays.toString(arr));
        QuickSort quickSort = new QuickSort();
        quickSort.sort(arr);
        System.out.println("排序后的數(shù)組為:");
        System.out.println(Arrays.toString(arr));
    }
}

4.性能分析

  • 快速排序是一種最壞時間復(fù)雜度為O(n^2)的排序算法,

  • 但是快速排序通常是實際排序應(yīng)用中最好的選擇,因為它的平均性能非常好——它期望的時間復(fù)雜度為O(nlgn),而且O(nlgn)中隱含的常數(shù)因子非常小

  • 快速排序是原址排序的,所以空間復(fù)雜度為O(1)

  • 快速排序再虛存環(huán)境中也能很好地工作

代碼演示示例:

public class SortPerformanceTest {
    public static void main(String[] args) {
        //創(chuàng)建100000個隨機數(shù)據(jù)
        Double[] arr1 = new Double[100000];
        for (int i = 0; i < arr1.length; i  ) {
            arr1[i] = (Double) (Math.random() * 10000000);  //這里使用10000000是為了讓數(shù)據(jù)更分散
        }

        //創(chuàng)建排序類的對象
        QuickSort quickSort = new QuickSort();

        //使用希爾排序?qū)rr1進(jìn)行排序
        long quickSort_start = System.currentTimeMillis();
        quickSort.sort(arr1);
        long quickSort_end = System.currentTimeMillis();

        System.out.println("快速排序所用的時間為:" (quickSort_end - quickSort_start) "ms");

    }
}

來源:https://www./content-1-442251.html

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多