【原】小搞一下 javascript算法

前言:在前端大全中看到這句話,以此共勉。基礎決定你可能達到的高度, 而業務決定了你的最低瓶頸javascript

 

其實javascript算法在平時的編碼中用處不大,不過不妨礙咱們學習它,學習一下這些算法的思想,鍛鍊一下本身的思惟模式。php

本文不會每種方法都介紹一下,只介紹一下七種,純屬爲了學習而學習,若是以爲代碼不是很好理解,能夠將數組裏面的內容代入函數裏面。前端

不過剛開始理解的時候確實挺頭疼的。廢話少說,搞起來!!java

 

要查看排序的動畫,能夠 點這裏 算法

 

冒泡排序

原理:shell

  從第一個元素開始,日後比較,遇到比本身小的元素就交換位置數組

     (來源於百度圖片)函數

 

特色:性能

  交換的次數最多,因此它的性能是最差的學習

 

代碼實現:

function bubbleSort(arr){
    var len=arr.length;
    for(var i=0;i<len;i++){
        for(var j=0;j<len-1-i;j++){  
            if(arr[j]>arr[j+1]){   //相鄰元素兩兩對比
                var temp=arr[j+1];  //交互位置,因此大的都放到了最後面
                arr[j+1]=arr[j];
                arr[j]=temp;

            }
        }
    }
    return arr;
}
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(bubbleSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]

 

插入排序

原理:

  插入排序的基本操做就是將一個數據插入到已經排好序的有序數據中,從而獲得一個新的、個數加一的有序數據

 

      如圖所示

    

在插入排序中,數組會被劃分爲兩種,「有序數組塊」和「無序數組塊」,

    第一遍的時候從」無序數組塊「中提取一個數20做爲有序數組塊。

    第二遍的時候從」無序數組塊「中提取一個數60有序的放到」有序數組塊中「,也就是20,60。

    第三遍的時候同理,不一樣的是發現10比有序數組的值都小,所以20,60位置後移,騰出一個位置讓10插入。

     而後按照這種規律就能夠所有插入完畢。

下面是一張gif圖

特色:

  插入算法把要排序的數組分紅兩部分:

  第一部分包含了這個數組的全部元素,但將第一個元素除外(讓數組多一個空間纔有插入的位置).

      第二部分就只包含這一個元素(即待插入元素)。在第一部分排序完成後,再將這個最後元素插入到已排好序的第一部分中

     比冒泡排序快一點

 

代碼實現:

//插入排序
//假定當前元素以前的元素已經排好序,先把本身的位置空出來,
//而後前面比本身大的元素依次向後移,直到空出一個"坑",
//而後把目標元素插入"坑"中
function insertSort(arr){
    // 從第二個元素開始,由於要留出一個坑
    for(var i=1;i<arr.length;i++){
        var x=arr[i]; 
        for(var j=i-1;arr[j]>x;j--){  //後挪空出位置 .
            arr[j+1]=arr[j];
        }
        if(arr[j+1]!=x){
            arr[j+1]=x;
        }
    }
    return arr;
}
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(insertSort(arr,2)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]

 

希爾排序

 原理:

     希爾排序也叫 遞減增量排序算法,是插入排序的一種神龜進化版。

  什麼叫遞減增量呢,就是定義一個間隔序列,例如是5,3,1。第一次處理,會處理全部間隔爲5的,

      下一次會處理間隔爲3的,最後一次處理間隔爲1的元素。也就是相鄰元素執行標準插入排序。

 

    步驟以下:

    一、先取一個小於n的整數d1做爲第一個增量,把文件的所有記錄分紅d1個組。

    二、全部距離爲d1的倍數的記錄放在同一個組中,在各組內進行直接插入排序。

    三、取第二個增量d2<d1重複上述的分組和排序,

    四、直至所取的增量dt=1(dt<dt-l<…<d2<d1),即全部記錄放在同一組中進行直接插入排序爲止。

這裏增量的取法以下:

      第一次增量的取法爲: d=count/2;

      第二次增量的取法爲:  d=(count/2)/2;

      最後一直到: d=1;

看上圖觀測的現象爲:

        d=3時:將40跟50比,因50大,不交換。

                   將20跟30比,因30大,不交換。

                   將80跟60比,因60小,交換。

        d=2時:將40跟60比,不交換,拿60跟30比交換,此時交換後的30又比前面的40小,又要將40和30交換,如上圖。

                   將20跟50比,不交換,繼續將50跟80比,不交換。

        d=1時:這時就是前面講的插入排序了,不過此時的序列已經差很少有序了,因此給插入排序帶來了很大的性能提升。

 

 特色:

  因爲屢次插入排序,咱們知道一次插入排序是穩定的,不會改變相同元素的相對順序,但在不一樣的插入排序過程當中,

     相同的元素可能在各自的插入排序中移動,最後其穩定性就會被打亂,因此shell排序是不穩定的。

  打個比方,我原來的數組是[5,4,3,2,1]的,這樣一打亂就所有從新排了。

 

 代碼實現:

function shellSort(arr){
    var gap=Math.floor(arr.length/2);
    while(gap>0){
        for(var i=gap;i<arr.length;i++){
            temp=arr[i];
            for(var j=i;j>=gap&&arr[j-gap]>temp;j-=gap){
                arr[j]=arr[j-gap];
            }
            arr[j]=temp;
        }
        gap=Math.floor(gap/2);
    }
    return arr;
}
var arr = [2,3,6,4,2,1,90,100,20,5];
console.log(shellSort(arr));  //[1, 2, 2, 3, 4, 5, 6, 20, 90, 100]

 

歸併排序

原理:

   歸併排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個子序列,每一個子序列是有序的。而後再把有序子序列合併爲總體有序序列。

   有如下幾個步驟:

    一、把長度爲n的輸入序列分紅兩個長度爲n/2的子序列;

    二、對這兩個子序列繼續分爲m/2的子序列,一直分下去

    三、將兩個排序好的子序列合併成一個最終的排序序列。

 

      

 

     再來一張靜態圖,比較好理解

 

這裏須要補充是,歸併中對數組的分割是從上往下的,歸併中數組的比較是從下往上的。

 

特色:

  速度僅次於快速排序,爲穩定排序算法,通常用於對整體無序,可是各子項相對有序的數列.

  屬於分治思想,遞歸歸併

 

代碼實現:

/* 排序併合並*/
function merge(left, right) {
   var re = [];
   while(left.length > 0 && right.length > 0) {
       if(left[0] < right[0]) {
      // 若是左邊的數據小於右邊的數據,將左邊的數據取出,放到新數組那裏 re.push(left.shift()); }
else { re.push(right.shift()); } } /* 當左右數組長度不等.將比較完後剩下的數組項連接起來便可 */ return re.concat(left).concat(right); } function mergeSort(arr) { if(arr.length == 1){ return arr; } /* 首先將無序數組劃分爲兩個數組 */ var mid = Math.floor(arr.length / 2); var left = arr.slice(0, mid); var right = arr.slice(mid); /* 遞歸分別對左右兩部分數組進行排序合併 */ return merge(mergeSort(left), mergeSort(right)); } var arr=[2,3,6,4,2,1,90,100,20,5]; console.log(mergeSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]

 

快速排序

原理:

一、在數據集之中,選擇一個元素做爲"基準"(pivot)。好比選擇下面數字45 

二、全部小於"基準"的元素,都移到"基準"的左邊;全部大於"基準"的元素,都移到"基準"的右邊。

三、對"基準"左邊和右邊的兩個子集,不斷重複第一步和第二步,直到全部子集只剩下一個元素爲止。

 

    

 

 

特色:速度最快。和歸併排序不一樣的是,歸併排序是先分爲兩組再繼續排,而快速排序是邊分邊排

 

代碼實現:

// 大體分三步:
    // 一、找基準(通常是以中間項爲基準)
    // 二、遍歷數組,小於基準的放在left,大於基準的放在right
    // 三、遞歸
    function quickSort(arr){
        //若是數組<=1,則直接返回
        if(arr.length<=1){
            return arr;
        }
        var pivotIndex=Math.floor(arr.length/2);
        //找基準,並把基準從原數組刪除
        var pivot=arr.splice(pivotIndex,1)[0];
        //定義左右數組
        var left=[];
        var right=[];

        //比基準小的放在left,比基準大的放在right
        for(var i=0;i<arr.length;i++){
            if(arr[i]<=pivot){
                left.push(arr[i]);
            }else{
                right.push(arr[i]);
            }
        }
        //遞歸
        return quickSort(left).concat([pivot],quickSort(right));
    }
    var arr=[2,3,6,4,2,1,90,100,20,5];
    console.log(quickSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]

  

選擇排序

原理:在要排序的一組數中,選出最小的一個數與第一個位置的數交換,而後剩下的數當中找出最小的與第二個位置的數交換,如此循環直到倒數第二個數和最後一個數爲止。

 

 

靜態圖:

 

 

特色:能夠說是冒泡排序的衍生品,效率比較通常般

 

代碼實現:

// 在無序區中選出最小的元素,而後將它和無序區的第一個元素交換位置。
function selectSort(arr){
   length = arr.length;
   for (var i = 0; i < length; i++){   // 循環數組
        var _min = arr[i];       // 把每一次的 數組裏面的數字記錄下來
        var k = i;                  // 記錄下來索引
        for (var j = i + 1; j < length; j++){   // 當前的數字與後一個數字相比較
           if (_min > arr[j]){   //當前的數 大於 後面一個數的話
               _min = arr[j];    //  就講後面 的數值 保存下來
               k = j;             /// 保存索引
           }
       }
       arr[k] = arr[i];   // 進行交換位置
       arr[i] = _min;
   }
   return arr;
}
   
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(selectSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]

 

奇偶排序

原理:

    選取全部奇數列的元素與其右側相鄰的元素進行比較,將較小的元素排序在前面;
    選取全部偶數列的元素與其右側相鄰的元素進行比較,將較小的元素排序在前面;
    重複前面兩步,直到全部序列有序爲止。

 以下圖所示:

gif圖:

  

 

 

特色:奇數和偶數序列交替比較

 

代碼實現:

 

function oddEvenSort(arr){
    //swaped用來控制循環是否要繼續,若是左邊的都比右邊的小,則退出循環,返回排好的數組
    var swaped=true;  
    var k=0;
    while(swaped){
        if(k>0){
            swaped=false;  
        }
        for(var i=k;i<arr.length-1;i+=2){
            if(arr[i]>arr[i+1]){
                // 若是左邊的數字比右邊的大,二者交換位置
                arr[i]=[ arr[i+1], arr[i+1]=arr[i] ][0];  
                swaped=true;
            }

        }
        k=[1,0][k]; //奇數和偶數之間的轉行
    }
    return arr;
}   

var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(oddEvenSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]

 

總結

  本文只是總結了算法中的一部分,算法的精髓就在於他們的思想,在js中用處應該不是很大。若是第一遍看不太懂那些代碼,能夠試着本身敲一遍,我在總結的時候,遇到理解不了的代碼,本身敲完理解程度就會加深一些。

理解完,確實這些算法的實現思想博大精深,不得不感慨一下前輩們思想的深度。

 

  有誤之處,歡迎指出。

相關文章
相關標籤/搜索