1. 程式人生 > >使用JavaScript實現排序演算法

使用JavaScript實現排序演算法

最近在準備春招面試,遠在美國大半個月了一直都沒有接到面試電話。很是灰心難過。但是學習還是要繼續,生活總是要繼續,菜鳥總有一天會高飛呀!

不管怎麼樣,還是學習吧!

1. 氣泡排序

氣泡排序是我記得最牢的了,可能是因為它的思想最簡單,重複走過要排序的序列,一次比較兩個元素,如果順序錯誤就把它們調整過來(升序)。一直重複工作,知道把最大的元素一步步下沉到陣列的尾部

function BubbleSort(arr) {
        for(var i=0;i<arr.length;i++){
            for(var j=0;j<arr.length-1-i;j++){//因為每次迴圈都會有一個數被下沉,隨著x增大下沉的元素就增加了
                if(arr[j]>arr[j+1]){
                    let temp = arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp
                }
            }
        }
        return arr;
    }

2. 選擇排序

選擇排序對我來說也很好理解,首先在未排序的陣列中找到最小(最大)元素,存放到排序序列的起始位置,然後在從生育未排序元素中繼續尋找最小(最大)的元素,然後放到已排序序列的尾部,以此類推,直到所有元素排序完畢

 function quickSort(arr) {
        for(var i=0;i<arr.length;i++){
            for(var j=i+1;j<arr.length;j++){
                if(arr[j]<arr[i]){
                    let temp = arr[j];
                    arr[j] = arr[i];
                    arr[i] = temp
                }
            }
        }
                return arr;
    }

3. 插入排序

原理是,對於未排序資料,在一排序序列中從後向前掃描,為其找到相應的位置並插入。從後向前掃描的過程中需要對元素進行向後移位操作

function insertSort(arr) {
        for(var i=1;i<arr.length;i++){
            var preIndex =i-1;
            var current = arr[i];
            while(preIndex>=0&&current<arr[preIndex]){
                arr[preIndex+1]=arr[preIndex];
                preIndex--;
            }
            arr[preIndex+1]=current;
        }
                return arr;
    }

4. 希爾排序

據說是第一個突破O(n^2)的排序操作,可以說是簡單插入排序的改進版。它與插入排序的不同在於,它會優先比較距離較遠的元素。所以又叫縮小增量排序。核心在於間隔序列的設定,好的間隔序列的設定能夠很大程度上的降低排序的時間複雜度。這次我採用的增量是陣列長度的一半,然後依次折半。當增量減至1時候,陣列被分為了一組,演算法停止。

我們來看下希爾排序的基本步驟,在此我們選擇增量gap=length/2,縮小增量繼續以gap = gap/2的方式,這種增量選擇我們可以用一個序列來表示,{n/2,(n/2)/2...1},稱為增量序列


function ShellSort(arr) {
        var len = arr.length;
        for (var gap = Math.floor(arr.length/2); gap>0;gap=Math.floor(gap/2)){
            for(var i=0;i<gap;i++){//i表示被分成了幾組
                var j = i;
                while(j<arr.length-gap){
                    var k = j;
                    while(arr[k+gap]<arr[k]&&k>=0){
                       var temp = arr[k+gap];
                        arr[k+gap] = arr[k];
                        arr[k] = temp;
                        k=k-gap;
                    }
                    j=j+gap;
                }
            }
        }
        return arr
    }

5. 歸併排序

和選擇排序一樣,歸併排序的效能不受輸入資料的影響,但表現比選擇排序好的多,因為始終都是O(n log n)的時間複雜度。代價是需要額外的記憶體空間。歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。歸併排序是一種穩定的排序方法。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為2-路歸併。 核心思想:分治。主題流程:先將一個序列分成很多個不能再分割的子序列,將各個子序列分別排序後再將子序列合併。其實就是重複兩個步驟:【1】分【2】合併。首先是第一個小問題,怎麼分?比如說一個序列:12 ,23,1,44,233,10,9,8。我們先分成兩段:12 ,23,1,44 和 233,10,9,8,發現還能再分成4段:12 ,23 和 1,44------233,10 和 9,8。再分成8段:12--23--1--44 和233--10--9--8。這時候開始把子序列進行排序合併,一個元素就是有序的。所以不用排序。合併成2個一組排序得到:12,23----1,44---10,233---8,9。再合併成4個一組排序得到:1,12,23,44---8,9,10,233。最後合併得到最終結果:1,8,9,10,12,23,44,233

我覺得歸併排序在實現上就可以分為兩個函式,一個負責分段,一個負責合併(因為分割後的每個子序列都是有序的,合併就是兩個有序數組合並的過程)

function merge(arr) {
    if(arr.length<2){
        return arr;
    }
    var mid = Math.floor(arr.length/2);
    //floor 向下取值;ceil向上取值,around 正常的四捨五入
    var left = arr.slice(0,mid);
    var right = arr.slice(mid);
    return sort(merge(left),merge(right));
}
function sort(left,right) {
    var result=[];
    var i=0;var j=0;
    while(i<left.length&&j<right.length){
        if(left[i]<right[j]){
            result.push(left[i]);
            i++
        }
        else{
            result.push(right[j]);
            j++
        }
    }
    if(i==left.length){
        result=result.concat(right.slice(j));
    }
    if(j==right.length){
        result=result.concat(left.slice(i));
    }
    return result;
}

6. 堆排序

這篇文章講的太好了,我就是按照它的講解步驟理解了整個過程,將程式碼寫出來了。

堆是具有以下性質的完全二叉樹:每個節點的值都大於或者等於其左右節點的值,稱為大頂堆;每個節點的值都小於或者等於其左右孩子節點的值,稱為小頂堆。如下圖

有幾個公式比較重要:

大頂堆:arr[i]>=arr[2i+1]&&arr[i]>=arr[ai+2];

小頂堆:arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2];

第一個非葉子節點:arr.length/2-1

再簡單總結下堆排序的基本思路:

  a.將無需序列構建成一個堆,根據升序降序需求選擇大頂堆或小頂堆;

  b.將堆頂元素與末尾元素交換,將最大元素"沉"到陣列末端;

  c.重新調整結構,使其滿足堆定義,然後繼續交換堆頂元素與當前末尾元素,反覆執行調整+交換步驟,直到整個序列有序。

    function HeapSort(arr) {
        for(var k=Math.floor(arr.length/2)-1;k>=0;k--){
            arr = HightHeap(arr,k,arr.length);
        }

        for(var j= arr.length-1;j>0;j--){
            //重新對堆頂進行調整
            var temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            //調整後進行堆調整
            for(var f = Math.floor(j/2)-1;f>=0;f--)
                arr = HightHeap(arr,f,j);
        }

        return arr
    }
    function HightHeap(arr,i,length) {
        var current = arr[i];
        if(i*2+2<length&&arr[i*2+1]<arr[i*2+2]){
            if(arr[i*2+2]>arr[i]){
                arr[i]= arr[i*2+2];
                arr[i*2+2] =current
            }
        }
        else {
           if(arr[i*2+1]>arr[i]){
               arr[i]= arr[i*2+1];
               arr[i*2+1] =current
           }

        }
        return arr
    }

7. 快速排序

快速排序的基本思想:通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序

    function sort(arr,left,right) {//第一次呼叫的時候left=0,right=arr.length-1
        var standard = arr[left];
        var i=left;
        var j=right;
        if(left>=right)return arr;
        while(i<j){
            while(arr[j]>=standard&&j>i)j--;
            while (arr[i]<=standard&&i<j)i++;
            if(i<j){
                var temp= arr[j];
                arr[j] = arr[i];
                arr[i] = temp;
            }
        }
        arr[left] = arr[j];
        arr[j]=standard;

        sort(arr,left,i-1);
        sort(arr,i+1,right);
        return arr;
    }

做個記錄,希望之後能找到一個自己滿意的單位吧!我好笨啊 啊 啊