Javascript算法系列之快速排序(Quicksort)


原文出自:

http://www.nczonline.net/blog/2012/11/27/computer-science-in-javascript-quicksort/

https://gist.github.com/paullewis/1981455#file-gistfile1-js

 

快速排序(Quicksort)是對冒泡排序的一種改進,是一種分而治之算法歸並排序的風格

核心的思想就是通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列

理論上的步驟:

  1. 找到一個“支點”項目在數組中,可以是中心點,基准
  2. 在陣列中的第一項開始指針(左指針)。
  3. 在數組中的最后一個項目開始一個指針(右指針)。
  4. 而在左指針數組中的值小於樞軸值,將左指針向右(加1)。 繼續,直到在左指針的值大於或等於所述樞軸值。
  5. 而在合適的指針數組中的值大於樞軸值,將右指針向左(減去1)。 繼續下去,直到在正確的指針的值小於或等於所述樞軸值。
  6. 如果左指針是小於或等於右指針,然后交換的值在數組中的這些位置。
  7. 移動左指針向右由1和右指針向左之一。
  8. 如果左指針和右指針不符合,請轉到步驟1。

 

原文太羅嗦了,簡單的來說

  1. 在數據集之中,選擇一個元素作為"基准"(pivot)。
  2. 所有小於"基准"的元素,都移到"基准"的左邊;所有大於"基准"的元素,都移到"基准"的右邊。
  3. 對"基准"左邊和右邊的兩個子集,不斷重復第一步和第二步,直到所有子集只剩下一個元素為止。

 

來個實際的demo

var items = [4, 2, 6, 5, 3, 9];

具體的流程算法如下:

針對這一組數組,如何選擇這個支點呢,有些算法選擇第一項為支點, 這是不是最好的選擇,性能最差。 一般更好地選擇數組中間的支點,所以考慮5是樞軸值(數組的長度除以2)

接下來從左邊拿0位置拿第一個數與支點5對比,如果4<5,那么指針的位置就偏移到1,然后2<5,一次類推

如果6>5的時候,此時左邊的指針就停止移動

然后從右邊的指針開始移動,也是如此,只是右邊是取的大於的值,比如9>5 ,往前移位,3<5,此時也停止移動,然后交換指針對應的數值

 


第一步:選擇支點
image

 

第二步: 指針在前后開始偏移

image

 

第三步:如果4<5,繼續移動左邊的指針往下

image

 

第四步:如果2<5,往下,6>5停止

image

 

第五步:9>5,往前,3<5停止

image

 

第六步:交換指針指向的值

image

 

第七步:繼續如上的操作,直到支點

image

 

第八部:如果指針到了支點,就停止

image

 


配合實現交換的swap的代碼

function swap(items, firstIndex, secondIndex){
    var temp = items[firstIndex];
    items[firstIndex] = items[secondIndex];
    items[secondIndex] = temp;
}

 

實現的代碼

function partition(items, left, right) {
    var pivot   = items[Math.floor((right + left) / 2)],
        i       = left,
        j       = right;
    while (i <= j) {
        while (items[i] < pivot) {
            i++;
        }
        while (items[j] > pivot) {
            j--;
        }
        if (i <= j) {
            swap(items, i, j);
            i++;
            j--;
        }
    }
    return i;
}

這個函數接受三個參數: items ,這是值進行排序的陣列, left ,這是該指數以啟動左指針時,和right ,這是該指數以啟動右指針。 樞軸值是通過將所確定的leftright的值,然后除以2。 因為這個值可能是一個浮點數,有必要進行一些舍入。

整個算法是循環只是一個循環。 外環確定何時所有的數組范圍的項目已經被處理。 左,右指針的兩個內部循環控制運動。當兩個內部循環的完成,則該指針進行比較,以確定是否交換是必要的。 在交換之后,兩個指針被移動,使外循環繼續,在合適的地方。 該函數返回的左指針的值,因為這是用於確定從哪里開始隔間的下一次。 請記住,該分區是發生在地方,而不會產生任何額外的數組。

快速排序算法基本上通過划分整個陣列的工作原理,然后遞歸地分割陣列的左側和右側的部分,直到整個陣列被排序。

在前面的例子中,數組變[4, 2, 3, 5, 6, 9]一個分區,並返回索引后為4(左指針的最后一個席位),開始遞歸左右2個分割陣列

如下面的圖所示

第一步:找到指針遍歷的位置,確定支點

image

 

第二步:從左右指針位置開始,對比4<3,停止

image

 

第三步:因為5>3,移動右邊的指針,因為3==3,停止

image

 

第四步:交換指針指向的值

image

 

第五步:依次如上處理

image

 

第六步:因為2<3,移動左邊指針,因為4>3,停止

因為左邊的指針超過了右邊的指針,停止

image

該過程之后,該陣列變成[3, 2, 4, 5, 6, 9]和返回的索引是1,繼續這樣,直到所有的陣列左側的排序。 然后相同的處理接着在右側的陣列。 基本對數的快速排序,然后變得非常簡單:

function quickSort(items, left, right) {
    var index;
    if (items.length > 1) {
        index = partition(items, left, right);
        if (left < index - 1) {
            quickSort(items, left, index - 1);
        }
        if (index < right) {
            quickSort(items, index, right);
        }

    }
    return items;
}

// first call
var result = quickSort(items, 0, items.length - 1);

 

快速排序通常被認為是高效,快速等特點是使用V8引擎的實現Array.prototype.sort()上有超過23個項目的數組。 對於少於23個項目,V8采用插入排序法[2]。

歸並排序是快速排序的競爭對手,因為它也是高效,快捷,但有被穩定的好處。 這就是為什么Mozilla和Safari中使用它自己的執行Array.prototype.sort()


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com