常見排序演算法

Limyes發表於2018-09-18

不寫下來,每次查資料感覺都沒什麼耐心看啊,真是令人惆悵,所以還是要自己捋一遍的。盜張圖:

常見排序演算法

直接插入排序:

從第一個數開始,每次將一個待排序的數,插入到已經排列好的數列中的適當位置,使數列有序,直到所有元素都在新數列為止。

舉例:
陣列:【】89 46 36 09 52 91

第一次:【89】46 36 09 52 91

第二次:【46 89】36 09 52 91

第三次:【36 46 89】09 52 91

第四次:【09 36 46 89】52 91

第五次:【09 36 46 52 89】91

第六次:【09 36 46 52 89 91】

function insertionSort(arr) {
    var len = arr.length;
    var preIndex, current;
    for (var i = 1; i < len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while(preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex+1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex+1] = current;
    }
    return arr;
}
複製程式碼

希爾排序:

相當於一種分組直接插入排序,原理即每次設定一個間隔,對有同樣間隔的兩個數作為一組進行插入排序即比大小,按照比大小的結果決定是否互換位置,然後繼續下一組排序,知道全部資料都排一遍。繼續設定更小的間隔按照上面的步驟排,直到最後一輪間隔為1時得出的新數列即為最後排序結果。

舉例:

常見排序演算法

依次類推,第二次間隔為2,排序結果為:2  1  0  4  3  8  6  5  9  7

第三次間隔為1,排序結果為:0  1  2  3  4  5  6  7  8  9

function shellSort(arr) {
    var len = arr.length,
        temp,
        gap = 1;
    while(gap < len/3) {          //動態定義間隔序列
        gap =gap*3+1;
    }
    for (gap; gap> 0; gap = Math.floor(gap/3)) {
        for (var i = gap; i < len; i++) {
            temp = arr[i];
            for (var j = i-gap; j > 0 && arr[j]> temp; j-=gap) {
                arr[j+gap] = arr[j];
            }
            arr[j+gap] = temp;
        }
    }
    return arr;
}
複製程式碼

直接選擇排序:
第一次從第1到n個數中選出最小值,放在第一位,

第二次從第2個到第n箇中選出最小值,放在第二位,

。。。

第n-1次從第n-1個到n箇中選出最小值,放在第n-1位。

舉例:

陣列:【】89 46 36 09 52 91

第一次:【09】89 46 36 52 91

第二次:【09 36】89 46  52 91

第三次:【09 36 46】89 52 91

第四次:【09 36 46 52】89 91

第五次:【09 36 46 52 89】91

程式碼實現:

function selectSort(arr){
    let len=arr.length,minIndex;//minIndex記錄最小的索引
    for(var i=0;i<len-1;i++){
        minIndex=i;  //記錄最小的索引
        for(var j=i+1;j<len;j++){
            if(arr[j]<arr[minIndex]){
                minIndex=j;
            }
        }
        if(i!=minIndex){
            var temp =arr[i];
            arr[i]=arr[minIndex];
            arr[minIndex]=temp;
        }
    }
    return arr;
}
selectSort(arr);
複製程式碼

堆排序:

堆排序是一種樹形選擇排序,將陣列看成是一棵二叉樹的順序結構儲存,然後利用完全二叉樹中雙親結點和孩子結點之間的的關係來調整每一層關係。

畫圖好費勁,不想畫圖了,舉個例子:
陣列:20 70 30 80 90 60 10 50 40 

從左到右排成二叉樹的形狀就是:
                               20                                                                   結點1

               70                               30                                                結點2、3

        80               90          60                 10                                    結點4、5、6、7

50              40                                                                                結點8、9

按照二叉樹中子結點的數值要小於等於父結點,所以從最下面一層起,分別把左右孩子結點和對應的父結點做比較,如果小於等於父結點則不做變動,如果大於父結點就跟父結點交換資料。所以第一輪交換後,再檢查二叉樹,把不符合規則的重新排序,得到的結果就是:

                                 90

                  80                            60

        50                  70        30               10

20             40

此時得到陣列中最大值90,在二叉樹最頂端的父節點,此時的陣列為:90 80 60 50 70 30 10 20 40 ,將第一位與最後一位交換位置,即:40 80 60 50 70 30 10 20 【90】作為新陣列重新開始進行排序,其中90不再參與排序,以此類推,完成排序。

var len;    //因為宣告的多個函式都需要資料長度,所以把len設定成為全域性變數

function buildMaxHeap(arr) {   //建立大頂堆
    len = arr.length;
    for (var i = Math.floor(len/2); i &gt;= 0; i--) {
        heapify(arr, i);
    }
}

function heapify(arr, i) {     //堆調整
    var left = 2 * i + 1,
        right = 2 * i + 2,
        largest = i;

    if (left < len && arr[left] > arr[largest]) {
        largest = left;
    }

    if (right < len && arr[right] > arr[largest]) {
        largest = right;
    }

    if (largest != i) {
        swap(arr, i, largest);
        heapify(arr, largest);
    }
}

function swap(arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

function heapSort(arr) {
    buildMaxHeap(arr);

    for (var i = arr.length-1; i > 0; i--) {
        swap(arr, 0, i);
        len--;
        heapify(arr, 0);
    }
    return arr;
}
複製程式碼

氣泡排序:

以第一個數為基準,跟剩下的數字一一對比,如果比基準數大就互換位置,設定新的基準數,如果比基準數小就繼續往下比較下一個數,效果是每執行一輪能夠選出一個最大的數放在最右邊,接著開始第二輪,選出第二大的數字放在倒數第二位,以此類推。

陣列:89 46 36 09 52 91

第一輪以89為基準:46 36 09 52 89 【91】

第二輪以46為基準:36 09  46(此時跟52比較時,基準數字換為52)52(此時跟89比較時,基準數字換為89)【89 91】

第三輪以36為基準: 09 36 46 【52 89 91】

第四輪以09為基準:09 36 【46 52 89 91】

程式碼實現:

function bubbleSort(arr){
    for(let i=0,l=arr.length;i<l;i++){
        for(let j=i+1;j<l;j++){
            if(arr[i]>arr[j]){
                let temp=arr[i];
                arr[i]=arr[j];
                arr[j]=temp;
            }
        }
    }
    return arr;
}
bubbleSort(arr);
複製程式碼

快速排序:

總結出來就是選擇一個數為基準,比它大的往後放,比它小的往前放。一輪完畢再重新選擇基準,以此類推。

具體過程:一般選擇第一個數作為第一輪基準數,倒序從最後一個開始,跟基準數做對比,找到一個比基準數小的就把這個數放在原來基準數的位置,接著從正序第二個開始找到比基準數大的數字就把這個數字填在剛剛比基準數小的數空出來的位置。接著繼續倒序從被替換位置開始找比基準數小的數字,找到就把這個數填在正序時大於基準數的數字空出來的位置,直到,所有的數都被找一遍會空出來一個位置,就把基準數放在這個位置,此時基準數左邊的數都小於它,基準數右邊的數都大於它。從第二輪開始將分割槽作比較,即第一輪基準數的左右兩邊將分別作為兩個不同的區,找不同的基準數開始重複上一輪動作排序,直到所有的數都排好序。

陣列:

20 70 30 80 90 60 10 50 40  

第一輪以20為基準,未填值位用括號代替佔位:
從右往左找比20小的數字,找到10,所以陣列順序變為:10 70 30 80 90 60 ()50 40

從左往右找比20大的數字,找到70,所以陣列順序變為:10 () 30 80 90 60 70 50 40

從右往左找比20小的數字,沒找到,所以陣列順序變為:10 20 30 80 90 60 70 50 40

第二輪以80為基準,因為30明顯就不用再排,此處省略這一步,直接從80找:

按照上述步驟以80位基準後排序結果為:10 20 30 40 50 60 70 80 90

function quickSort(arr, left, right) {
    var len = arr.length,
        partitionIndex,
        left = typeof left != 'number' ? 0 : left,
        right = typeof right != 'number' ? len - 1 : right;

    if (left < right) {
        partitionIndex = partition(arr, left, right);
        quickSort(arr, left, partitionIndex-1);
        quickSort(arr, partitionIndex+1, right);
    }
    return arr;
}

function partition(arr, left ,right) {     //分割槽操作
    var pivot = left,                      //設定基準值(pivot)
        index = pivot + 1;
    for (var i = index; i <= right; i++) {
        if (arr[i] < arr[pivot]) {
            swap(arr, i, index);
            index++;
        }        
    }
    swap(arr, pivot, index - 1);
    return index-1;
}

function swap(arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
複製程式碼

歸併排序:

歸併排序(Merge sort)是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。

過程:把一個陣列一分為二,然後把分好之後的陣列再次一分為二,直到每個陣列長度為1,然後兩兩合併成新陣列進行有序合併,直到全部歸併成一個陣列。

盜圖:

常見排序演算法

function mergeSort(arr) {  //採用自上而下的遞迴方法
    var len = arr.length;
    if(len < 2) {
        return arr;
    }
    var middle = Math.floor(len / 2),
        left = arr.slice(0, middle),
        right = arr.slice(middle);
    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
    var result = [];
    while (left.length>0 && right.length>0) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}
複製程式碼

基數排序(桶排序):

把所有的數字統一位數,位數不夠的用0補全,設定10個桶對應數字0-9,然後從個位數開始排,個位數是什麼就放到對應數字的桶裡,然後按照從第0個到第9個桶裡面的數字重新排序形成新陣列,開始從十位數排,按照十位數上對應的數字放置到對應的桶裡,再形成新陣列,接著百位,千位直到最高位。

數列:73  22  93  43  55  14  28  65  39  81

第一次,根據個位數分配:
0         1            2             3            4          5           6          7          8         9

           81         22        73 93 43   14      55  65                              28       39

第二次,對新數列根據十位數分配:
0          1            2             3            4           5           6          7         8          9

           14        22  28       39          43         55          65        73       81       93

即可得到排完序的新陣列。

function bucketSort(arr, bucketSize) {
    if (arr.length === 0) {
      return arr;
    }

    var i;
    var minValue = arr[0];
    var maxValue = arr[0];
    for (i = 1; i < arr.length; i++) {
      if (arr[i] < minValue) {
          minValue = arr[i];                //輸入資料的最小值
      } else if (arr[i] > maxValue) {
          maxValue = arr[i];                //輸入資料的最大值
      }
    }

    //桶的初始化
    var DEFAULT_BUCKET_SIZE = 5;            //設定桶的預設數量為5
    bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
    var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;   
    var buckets = new Array(bucketCount);
    for (i = 0; i < buckets.length; i++) {
        buckets[i] = [];
    }

    //利用對映函式將資料分配到各個桶中
    for (i = 0; i < arr.length; i++) {
        buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
    }

    arr.length = 0;
    for (i = 0; i < buckets.length; i++) {
        insertionSort(buckets[i]);                      //對每個桶進行排序,這裡使用了插入排序
        for (var j = 0; j < buckets[i].length; j++) {
            arr.push(buckets[i][j]);                      
        }
    }

    return arr;
}
複製程式碼



相關文章