資料結構與演算法整理總結---排序 2

lxzoliver發表於2020-03-22

氣泡排序、插⼊排序、選擇排序這三種排序演算法,它們的時間複雜度都是O(n2),⽐較⾼,適合⼩規模資料的排序。今天,我講兩種時間複雜度為O(nlogn)的排序演算法,歸併排序和快速排序。這兩種排序演算法適合⼤規模的資料排序,⽐氣泡排序、插⼊排序、選擇排序這三種排序演算法要更常⽤。

歸併排序

歸併排序的原理

歸併排序的核⼼思想還是蠻簡單的。如果要排序⼀個陣列,我們先把陣列從中間分成前後兩部分,然後對前後兩部分分別排序,再將排好序的兩部分合並在⼀起,這樣整個陣列就都有序了。

資料結構與演算法整理總結---排序2

資料結構與演算法整理總結---排序2

歸併排序使⽤的就是分治思想。分治,顧名思義,就是分⽽治之,將⼀個⼤問題分解成⼩的⼦問題來解決。⼩的⼦問題解決了,⼤問題也就解決了。

分治演算法⼀般都是⽤遞迴來實現的。分治是⼀種解決問題的處理思想,遞迴是⼀種程式設計技巧,這兩者並不衝突。

歸併排序演算法實現

class mergeSort{

    //需要排序的陣列
    public $arr = [6,3,-1,9,39,43,23,11];

    function __construction()
    {
        $this->mergeSort(0,count($arr)-1);
    }
    publuc function mergeSort($min,$max){
        if($min<$max){
            $middle = ($min+$max)/2;
            $this->mergeSort($this->arr,0,$middle-1);
            $this->mergeSort($this->arr,$middle+1,$max);
            $this->merge($min,$middle,$max);
        }
    }
    //$left為左陣列起始下標,$right為右陣列結束下標
    public function merge($left,$middle,$right){
        $tempArr = $this->arr;
        $rightStart = $middle+1;
        $temp = $left;
        while($left<=$middle&&$rightStart<=$right){
            if($arr[$left]<$arr[$rightStart]){
                $tempArr[$temp++]=$this->arr[$left++]; 
            }else{
                $tempArr[$temp++]=$this->arr[$rightStart++];   
            }
        }
        while($rightStart<=$right){
            $tempArr[$temp++]=$this->arr[$rightStart++];
        }
        $this->arr = $tempArr;
    }
}

$sort = new mergeSort();
$arr = $sort->arr;

歸併排序的效能分析

歸併排序穩不穩定關鍵要看merge()函式,也就是兩個有序⼦陣列合併成⼀個有序陣列的那部分程式碼。
在合併的過程中,如果A[p…q]和A[q+1…r]之間有值相同的元素,那我們可以像虛擬碼中那樣,先把A[p…q]中的元素放⼊tmp 陣列。這樣就保證了值相同的元素,在合併前後的先後順序不變。所以,歸併排序是⼀個穩定的排序演算法。
其時間複雜度是⾮常穩定的,不管是最好情況、最壞情況,還是平均情況,時間複雜度都是O(nlogn)。
歸併排序的合併函式,在合併兩個有序陣列為⼀個有序陣列時,需要藉助額外的儲存空間。空間複雜度為O(n)。

快速排序

快速排序的原理

快排利⽤的也是分治思想。乍看起來,它有點像歸併排序,但是思路其實完全不⼀樣。

快排的思想是這樣的:如果要排序陣列中下標從p到r之間的⼀組資料,我們選擇p到r之間的任意⼀個資料作為pivot(分割槽點)。

我們遍歷p到r之間的資料,將⼩於pivot的放到左邊,將⼤於pivot的放到右邊,將pivot放到中間。經過這⼀步驟之後,陣列p到r之間的資料就被分成了三個部分,前⾯p到q-1之間都是⼩於pivot的,中間是pivot,後⾯的q+1到r之間是⼤於pivot的。

資料結構與演算法整理總結---排序 2

根據分治、遞迴的處理思想,我們可以⽤遞迴排序下標從p到q-1之間的資料和下標從q+1到r之間的資料,直到區間縮⼩為1,就說明所有的資料都有序了。

歸併排序中有⼀個merge()合併函式,我們這⾥有⼀個partition()分割槽函式。partition()分割槽函式實際上我們前⾯已經講過了,就是隨機選擇⼀個元素作為pivot(⼀般情況下,可以選擇p到r區間的最後⼀個元素),然後對A[p…r]分割槽,函式返回pivot的下標。

如果我們不考慮空間消耗的話,partition()分割槽函式可以寫得⾮常簡單。我們申請兩個臨時陣列X和Y,遍歷A[p…r],將⼩於pivot的元素都拷⻉到臨時陣列X,將⼤於pivot的元素都拷⻉到臨時陣列Y,最後再將陣列X和陣列Y中資料順序拷⻉到A[p…r]。

資料結構與演算法整理總結---排序 2

快速排序演算法實現

class quickSort{

    public $arr = [4,3,8,65,43,-1,21,55];

    __construction(){
        $this->quickSort($this->arr,0,count($arr)-1);
    }
    function quickSort($arr,$begin,$end){
        if($begin<$end){
            //以第一個數為基準
            $first = $arr[$begin];
            $i = $begin;
            $j = $end;
            while($i<$j){
                while($i<$j&&$arr[$j]<$first){
                    $j--;
                    $arr[$i]=$arr[$j];
                }
                while($i<$j&&$arr[$i]>=$first){
                    $i++;
                    $arr[$j]=$arr[$i];
                }
                $arr[$i] = $first;
            }
            $this->quickSort($arr,$begin,$i-1);
            $this->quickSort($arr,$i+1,$end);
        }else{
            return $arr;
        }
    }
}
$quickSort = new quickSort();
$arr = $quickSort->arr;

快速排序的效能分析

快排是⼀種原地、不穩定的排序演算法。快排也是⽤遞迴來實現的。對於遞迴程式碼的時間複雜度。如果每次分割槽操作,都能正好把陣列分成⼤⼩接近相等的兩個⼩區間,那快排的時間複雜度遞推求解公式跟歸併是相同的。所以,快排的時間複雜度也是 O(nlogn)。

歸併排序與快速排序比較

資料結構與演算法整理總結---排序 2

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章