1. 程式人生 > >傳統的三種排序以及氣泡排序的優化演算法

傳統的三種排序以及氣泡排序的優化演算法

    今天聽到leader說面試的事,說問一個有兩年工作經驗的人,傳統的三種排序可以手寫嗎都手寫不出來。讓我心中也是一顫,其實想想,工作了這麼久,對於原生js這塊兒真的有些淡忘了,在工作中平時都是用的框架來搞事情,直接拿來就可以用,想想當初剛入這行的時候,那時候就覺得js真的很神奇,可是隨著工作時間越來越久,一些東西都是直接拿來用,對於底層的原理也不那麼深究了,之前還好,還看看,現在都已經麻木了。今天leader說的這番話,其實你如果說讓我手寫這三種排序我還是可以寫出來的,但是我覺得對於原生js這塊兒確實忘了一些,學無止境,不是新的東西就是好的東西,不能忘記最底層的實現,嗯~,寫部落格寫部落格,不閒聊了。
  對於我們leader今天說的話,我也引入了另外一個知識點,多少和效能優化沾一點邊,氣泡排序我們都知道,可你知不知道它還可以進行優化呢,今天我們就來聊一聊傳統的三種基本排序演算法(選擇排序、快排、冒泡)以及關於氣泡排序如何進行優化的三種優化方案

  因為這三種基本的排序演算法原理都很簡單,所以在思路這塊我就不做過多的解釋了,我們把重點放在最後的氣泡排序如何優化上面

 一、選擇排序

選擇排序的思路:比如一個數組arr=[9,6,2,5,10,20,1]進行從小到大排序,我們讓每一個數字都和後面的數字進行比較,比自己小的就交換位置,比自己大的就進行下一個比較,將這個數放到相應的位置,下面是程式碼的實現:

function selectSort(arr){
    var temp;     //定義一個變數用來運算元字交換
    for(var i=0;i<arr.length-1;i++){
         for(var j=i+1;j<arr.length;j++){
             if(arr[i]>arr[j]){      //簡單的交換位置邏輯,相信你可以看的懂
                 temp = arr[i];
                 arr[i] = arr[j];
                 arr[j] = temp;
             }
         }        
         
     }
     
return arr; }

 二、快速排序

  快速排序的思路:從一個數組中任意的挑選一個元素(通常來說我們會選擇中間的那個元素),將這個元素作為中軸元素,然後把剩下的元素以這個中軸元素的大小作為標準,比中軸元素小的放到左邊,比中軸元素大的放到右邊,然後把左邊和右邊的都作為一個數組,重複以上操作,知道子元素的元素個數小於等於1的時候,就證明全部的元素都已經從小到大排好序了(因為只有一個元素的陣列一定是有序的,已經不需要再繼續排序了),在這個演算法中我們主要是用到了了一個遞迴演算法(遞迴演算法不瞭解的建議可以先去看下這方面的知識),以下是程式碼的實現:

function quickSort(arr){
    if(arr.length<=1){
        return arr;
    }

    //獲取下標
    var midIndex = arr.length%2 == 0?arr.length/2:(arr.length+1)/2;
    //取中間值
    var mid = arr[midIndex];
    //定義左邊的陣列
    var left = [];
    //定義右邊的陣列
    var right = [];

    for(var i=0;i<arr.length;i++){
        if(i != midIndex && arr[i]<=mid){
            left.push(arr[i]);
        }

        if(i != midIndex && arr[i]>mid){
            right.push(arr[i]);
        }
    }

    return quickSort(left).concat(mid).concat(quickSort(right))
}

 

    三、氣泡排序

  氣泡排序的思路:顧名思義,氣泡排序,就像冒泡一樣,從小往大冒,由於邏輯過於簡單,在這裡我就直接貼出程式碼了:

function bubbleSort(arr){
    var temp;
    for(var i=0;i<arr.length;i++){
        for(var j=0;j<arr.length-i;j++){
            if(arr[j]>arr[j+1]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }

    return arr;
}

  通過上面的程式碼可以看出來,通過兩次簡單的for迴圈,並且不加任何判斷語句的形式的演算法註定是低效的,以下是對氣泡排序演算法的三種優化


 (1)、當我們對一個數組arr=[9,1,2,3,4,5,6]進行排序的時候,我們正常氣泡排序的話,還是會每個數字都排一次,但事實上我們第一次排序進行完之後,陣列就已經變成[1,2,3,4,5,6,9],已經達到我們想要的效果了,所以已經不需要再進行其他元素的排序了,所以我們要對這種傳統的氣泡排序演算法做一個優化,思路大概是這樣的:我們定義一個flag,當某一次排序中沒有發生元素的交換的話,設定flag為false,當flag為false的時候直接結束後面的迴圈,這樣的話陣列就不會再進行後面的無意義的排序了,程式碼實現:

    var arr = [9,1,2,3,4,5,6]
    function bubbleSort(arr){
    var temp;
    var flag;       //定義flag,用來判斷陣列是否已經有序
    for(var i=0;i<arr.length;i++){
        flag = true     //我們設定flag初始值為true
        for(var j=0;j<arr.length-i;j++){
            if(arr[j]>arr[j+1]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                flag = false;   //我們自己規定flag為false的時候說明陣列需要繼續排序
            }
        }
        if(flag){   //我們可以規定如果某次迴圈後flag依然為true的話,表明這次沒有進行重新的元素交換,也就是說沒有進行重新排序,那麼此時陣列中元素已經有序了,所以我們可以直接break跳出迴圈,return出陣列
            break;
        }
    }

    return arr;
}

(2)、第二種優化是從另一個角度來考慮的,並且也是基於第一次優化的思想,我們每次排序後,陣列的後面有一部分已經有序了,所以我們也不需要再和後面已經排好序的元素再進行比較了,我們可以這樣做,用一個變數來記錄下最後一次發生交換的位置,後面沒有發生交換的其實已經有序了,所以可以用這個值來作為一一次我們比較結束時的位置,將會減少很多次沒必要的排序過程,程式碼實現如下:

    var arr = [9,1,10,5,6,3,0]
    function bubbleSort(arr){
    var temp;
    var flag;       //定義flag,用來判斷陣列是否已經有序
    var lastindex = 0;       //定義lastindex,用來判記錄每次排好序時的最後一次交換的位置
    var k = arr.length-1;          //用來和lastindex配合,作為每次迴圈的邊界值,實現不會進行沒必要排序的效果
    for(var i=0;i<arr.length;i++){
        flag = true     //我們設定flag初始值為true
        for(var j=0;j<k;j++){
            if(arr[j]>arr[j+1]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                flag = false;   //我們自己規定flag為false的時候說明陣列需要繼續排序
                lastindex = j;  //來記錄最後一次交換元素的下標
            }
        }
        k = lastindex
        if(flag){   //我們可以規定如果某次迴圈後flag依然為true的話,表明這次沒有進行重新的元素交換,也就是說沒有進行重新排序,那麼此時陣列中元素已經有序了,所以我們可以直接break跳出迴圈,return出陣列
            break;
        }
    }

    return arr;
}

    在這裡我只寫了兩種優化的方法,可我為什麼說有三種呢,是我覺得肯定還會有第三種第四種等等很多,等待著我們後續的思考,做了前端也快三年了,效能優化這塊兒在各個方面都有很多,還有原生js也不能丟,我就是說一說自己的一些想法:),希望同行們(為夢想奮鬥中的我們),在使用框架或者一些類庫來工作的同時,最底層最原理的東西也不能丟,我自己都不知道我現在在說什麼,就是感覺我們不能丟了最原始的東西,好了,今天就到這裡了,各位晚安,勿忘初心,方得始終~~~