1. 程式人生 > >Javascript演算法之快速排序

Javascript演算法之快速排序

快速排序是一個時間複雜度為O(nlogn)的演算法,可以快速處理百萬數量級資料。

1.主要思想

第一步:找到一個基準值(v)
第二步:進行劃分,使得比基準值小的值在基準值(v)左邊,比基準值大的值在基準值(v)右邊
第三步:對劃分好的左右兩部分,再重複進行一、二步

2.舉個栗子

假設有一個數組[34,21,56,43,33,89,3]

第一步:選擇陣列的第一個值為"基準值"
在這裡插入圖片描述

第二步: 按照順序,將每個元素與"基準值"進行比較,形成兩個子集,一個"小於34",另一個"大於等於34"
在這裡插入圖片描述

第三步:對劃分好的左右兩部分,再重複進行一、二步

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

3.具體實現

在具體實現中會出現一個swap方法,主要用於把陣列內的兩個位置進行交換,

	function swap(arr, a, b) {
	  if (a===b) return
	  let c = arr[a];
	  arr[a] = arr[b];
	  arr[b] = c;
	}
一、定義三個函式

quickSort函式: 主要用於被外部呼叫;引數為陣列;無返回值;
sort函式:主要用於遞迴處理陣列;引數為陣列和要進行劃分的陣列的起始下角標;無返回值;
_partation函式:用於處理劃分陣列;引數為陣列和要進行劃分的陣列的起始下角標;返回值為“基準值”所在位置

function quickSort (arr) {}
function sort (arr, l, r) {}
function _partation (arr, l, r) {}
二、quickSort函式

首先,檢查陣列元素的個數,小於等於1,返回;

	function quickSort (arr) {
		+ let n = arr.length
		+ if (n<=1) return
	}

其次,進行陣列的劃分

	function quickSort (arr) {
		let n = arr.length
		if (n<=1) return
		+  _partation(arr, 0, n-1)
	}
三、sort函式

首先,確定遞迴的停止條件,當陣列的左角標大於等於右角標時,說明已經沒有再進行陣列劃分的必要了,直接返回

	function sort (arr, l, r) {
		+ if (l>=r) return
	}

其次,進行陣列的劃分,得到“基準值”的位置

	function sort (arr, l, r) {
		 if (l>=r) return
		 + let j = _partation(arr, l, r)
	}

最後,再次呼叫sort函式,繼續進行“基準值”左右兩個陣列劃分; [l…j-1]為小於“基準值”的元素集合

	function sort (arr, l, r) {
		 if (l>=r) return
		 let j = _partation(arr, l, r)
		 + sort(arr, l, j-1)
		 + sort(arr, j+1, r)
	}
四、_partation函式

首先,定義一些變數用來記錄關鍵資訊值

v: 代表“基準值”,這裡暫定陣列第一個元素為“基準值”,即l值;
lpos: 代表小於“基準值”部分的最後一個元素的下角標;

	function _partation (arr, l, r) {
		+ let lpos = l;
		+ let v = arr[l];
	}

然後,按照順序,與“基準值”進行比較,小於“基準值”的元素放到lpos的下一個位置,並且lpos++,讓lpos繼續代表小於“基準值”部分的最後一個元素的下角標,直到迴圈結束

	function _partation (arr, l, r) {
		let lpos = l;
		let v = arr[l];
		+ for (let i = l+1;i<=r;i++){
			+ if (arr[i]<v) {
				+ swap(arr, i, lpos+1);
				+ lpos++;
			+ }
		+ }
	}

接著,迴圈完成。把lpos位置上的元素和l上的元素(即基準值)進行交換,實現lpos位置之前的元素均小於“基準值”,並且返回“基準值”的位置

	function _partation (arr, l, r) {
		let lpos = l;
		let v = arr[l];
		for (let i = l+1;i<=r;i++){
			if (arr[i]<v) {
				swap(arr, i, lpos+1);
				lpos++;
			 }
		}
		+ swap(arr,  l, lpos );
		+ return lpos;
	}
五、優化

1.取基準值的優化
在陣列近乎有序時候,基準值從第一個元素開始取,也就是會獲得一個最小值,在它劃分時會出現,下面的情況,
例如:
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

在這裡插入圖片描述

原本最好可以劃分為3層,但是這裡卻變成了7層,演算法會退化成O(n^2)級別。
優化方法:基準值變成一個隨機數,這樣在資料足夠大時,每次取到最小值的概率會變得非常小。

	function _partation (arr, l, r) {
		let lpos = l;
		+ let randomIndex = parseInt(Math.random() * (r - l + 1) + l);
		+ swap(arr, l, randomIndex);
		let v = arr[l];
		for (let i = l+1;i<=r;i++){
			if (arr[i]<v) {
				swap(arr, i, lpos+1);
				lpos++;
			 }
		}
		swap(arr,  l, lpos );
		return lpos;
	}

2.當要劃分的陣列,個數小到一定程度的時候,可以藉助插入排序,完成陣列排序。
插入排序也是個神奇的演算法,以後會總結,這裡就先不說了。

這是本人第一次總結所學知識,行文思路不是很好,本文內容有借鑑阮一峰老師在寫快速排序分析的思路,寫的不好,望大家海涵,內容有誤的地方,望大家指出。
參考資料:
https://github.com/liuyubobobo/Play-with-Algorithms
http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html