經常使用排序算法之JavaScript實現

筆試面試常常涉及各類算法,本文簡要介紹經常使用的一些算法,並用JavaScript實現。面試


一、插入排序

 1)算法簡介算法

  插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工做原理是經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,一般採用in-place排序(即只需用到O(1)的額外空間的排序),於是在從後向前掃描過程當中,須要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間。api

2)算法描述和實現 數組

  通常來講,插入排序都採用in-place在數組上實現。具體算法描述以下:數據結構

  1. 從第一個元素開始,該元素能夠認爲已經被排序;
  2. 取出下一個元素,在已經排序的元素序列中從後向前掃描;
  3. 若是該元素(已排序)大於新元素,將該元素移到下一位置;
  4. 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置;
  5. 將新元素插入到該位置後;
  6. 重複步驟2~5。

  JavaScript代碼實現:ide

複製代碼
 1 function insertionSort(array) {
 2     if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
 3         for (var i = 1; i < array.length; i++) {
 4             var key = array[i];
 5             var j = i - 1;
 6             while (j >= 0 && array[j] > key) {
 7                 array[j + 1] = array[j];
 8                 j--;
 9             }
10             array[j + 1] = key;
11         }
12         return array;
13     } else {
14         return 'array is not an Array!';
15     }
16 }
複製代碼

3)算法分析ui

  • 最佳狀況:輸入數組按升序排列。T(n) = O(n)
  • 最壞狀況:輸入數組按降序排列。T(n) = O(n2)
  • 平均狀況:T(n) = O(n2)

2、二分插入排序

1)算法簡介spa

  二分插入(Binary-insert-sort)排序是一種在直接插入排序算法上進行小改動的排序算法。其與直接插入排序算法最大的區別在於查找插入位置時使用的是二分查找的方式,在速度上有必定提高。prototype

2)算法描述和實現  設計

  通常來講,插入排序都採用in-place在數組上實現。具體算法描述以下:

  1. 從第一個元素開始,該元素能夠認爲已經被排序;
  2. 取出下一個元素,在已經排序的元素序列中二分查找到第一個比它大的數的位置;
  3. 將新元素插入到該位置後;
  4. 重複上述兩步。

  JavaScript代碼實現:

複製代碼
 1 function binaryInsertionSort(array) {
 2     if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
 3         for (var i = 1; i < array.length; i++) {
 4             var key = array[i], left = 0, right = i - 1;
 5             while (left <= right) {
 6                 var middle = parseInt((left + right) / 2);
 7                 if (key < array[middle]) {
 8                     right = middle - 1;
 9                 } else {
10                     left = middle + 1;
11                 }
12             }
13             for (var j = i - 1; j >= left; j--) {
14                 array[j + 1] = array[j];
15             }
16             array[left] = key;
17         }
18         return array;
19     } else {
20         return 'array is not an Array!';
21     }
22 }
複製代碼

3)算法分析

  • 最佳狀況:T(n) = O(nlogn)
  • 最差狀況:T(n) = O(n2)
  • 平均狀況:T(n) = O(n2)

3、選擇排序

1)算法簡介

  選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工做原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到全部元素均排序完畢。

2)算法描述和實現

  n個記錄的直接選擇排序可通過n-1趟直接選擇排序獲得有序結果。具體算法描述以下:

  1. 初始狀態:無序區爲R[1..n],有序區爲空;
  2. 第i趟排序(i=1,2,3...n-1)開始時,當前有序區和無序區分別爲R[1..i-1]和R(i..n)。該趟排序從當前無序區中選出關鍵字最小的記錄 R[k],將它與無序區的第1個記錄R交換,使R[1..i]和R[i+1..n)分別變爲記錄個數增長1個的新有序區和記錄個數減小1個的新無序區;
  3. n-1趟結束,數組有序化了。

  JavaScript代碼實現:

複製代碼
 1 function selectionSort(array) {
 2     if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
 3         var len = array.length, temp;
 4         for (var i = 0; i < len - 1; i++) {
 5             var min = array[i];
 6             for (var j = i + 1; j < len; j++) {
 7                 if (array[j] < min) {
 8                     temp = min;
 9                     min = array[j];
10                     array[j] = temp;
11                 }
12             }
13             array[i] = min;
14         }
15         return array;
16     } else {
17         return 'array is not an Array!';
18     }
19 }
複製代碼

3)算法分析

  • 最佳狀況:T(n) = O(n2)
  • 最差狀況:T(n) = O(n2)
  • 平均狀況:T(n) = O(n2)

4、冒泡排序

1)算法簡介

  冒泡排序是一種簡單的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,若是它們的順序錯誤就把它們交換過來。走訪數列的工做是重複地進行直到沒有再須要交換,也就是說該數列已經排序完成。這個算法的名字由來是由於越小的元素會經由交換慢慢「浮」到數列的頂端。

2)算法描述和實現

  具體算法描述以下:

  1. 比較相鄰的元素。若是第一個比第二個大,就交換它們兩個;
  2. 對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對,這樣在最後的元素應該會是最大的數;
  3. 針對全部的元素重複以上的步驟,除了最後一個;
  4. 重複步驟1~3,直到排序完成。

  JavaScript代碼實現:

複製代碼
 1 function bubbleSort(array) {
 2     if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
 3         var len = array.length, temp;
 4         for (var i = 0; i < len - 1; i++) {
 5             for (var j = len - 1; j >= i; j--) {
 6                 if (array[j] < array[j - 1]) {
 7                     temp = array[j];
 8                     array[j] = array[j - 1];
 9                     array[j - 1] = temp;
10                 }
11             }
12         }
13         return array;
14     } else {
15         return 'array is not an Array!';
16     }
17 }
複製代碼

3)算法分析

  • 最佳狀況:T(n) = O(n)
  • 最差狀況:T(n) = O(n2)
  • 平均狀況:T(n) = O(n2)

5、快速排序

1)算法簡介

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

2)算法描述和實現

  快速排序使用分治法來把一個串(list)分爲兩個子串(sub-lists)。具體算法描述以下:

  1. 從數列中挑出一個元素,稱爲 "基準"(pivot);
  2. 從新排序數列,全部元素比基準值小的擺放在基準前面,全部元素比基準值大的擺在基準的後面(相同的數能夠到任一邊)。在這個分區退出以後,該基準就處於數列的中間位置。這個稱爲分區(partition)操做;
  3. 遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。

  JavaScript代碼實現:

複製代碼
 1 //方法一
 2 function quickSort(array, left, right) {
 3     if (Object.prototype.toString.call(array).slice(8, -1) === 'Array' && typeof left === 'number' && typeof right === 'number') {
 4         if (left < right) {
 5             var x = array[right], i = left - 1, temp;
 6             for (var j = left; j <= right; j++) {
 7                 if (array[j] <= x) {
 8                     i++;
 9                     temp = array[i];
10                     array[i] = array[j];
11                     array[j] = temp;
12                 }
13             }
14             quickSort(array, left, i - 1);
15             quickSort(array, i + 1, right);
16         };
17     } else {
18         return 'array is not an Array or left or right is not a number!';
19     }
20 }  
21 var aaa = [3, 5, 2, 9, 1];
22 quickSort(aaa, 0, aaa.length - 1);
23 console.log(aaa);
24 
25 
26 //方法二
27 var quickSort = function(arr) {
28   if (arr.length <= 1) { return arr; }
29   var pivotIndex = Math.floor(arr.length / 2);
30   var pivot = arr.splice(pivotIndex, 1)[0];
31   var left = [];
32   var right = [];
33   for (var i = 0; i < arr.length; i++){
34     if (arr[i] < pivot) {
35       left.push(arr[i]);
36     } else {
37       right.push(arr[i]);
38     }
39   }
40   return quickSort(left).concat([pivot], quickSort(right));
41 };
複製代碼

3)算法分析

  • 最佳狀況:T(n) = O(nlogn)
  • 最差狀況:T(n) = O(n2)
  • 平均狀況:T(n) = O(nlogn)

6、堆排序

1)算法簡介

  堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似徹底二叉樹的結構,並同時知足堆積的性質:即子結點的鍵值或索引老是小於(或者大於)它的父節點。

2)算法描述和實現

  具體算法描述以下:

  1. 將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆爲初始的無序區;
  2. 將堆頂元素R[1]與最後一個元素R[n]交換,此時獲得新的無序區(R1,R2,......Rn-1)和新的有序區(Rn),且知足R[1,2...n-1]<=R[n];
  3. 因爲交換後新的堆頂R[1]可能違反堆的性質,所以須要對當前無序區(R1,R2,......Rn-1)調整爲新堆,而後再次將R[1]與無序區最後一個元素交換,獲得新的無序區(R1,R2....Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數爲n-1,則整個排序過程完成。

  JavaScript代碼實現:

複製代碼
 1 /*方法說明:堆排序
 2 @param  array 待排序數組*/            
 3 function heapSort(array) {
 4     if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
 5         //建堆
 6         var heapSize = array.length, temp;
 7         for (var i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
 8             heapify(array, i, heapSize);
 9         }
10         
11         //堆排序
12         for (var j = heapSize - 1; j >= 1; j--) {
13             temp = array[0];
14             array[0] = array[j];
15             array[j] = temp;
16             heapify(array, 0, --heapSize);
17         }
18     } else {
19         return 'array is not an Array!';
20     }
21 }
22 /*方法說明:維護堆的性質
23 @param  arr 數組
24 @param  x   數組下標
25 @param  len 堆大小*/
26 function heapify(arr, x, len) {
27     if (Object.prototype.toString.call(arr).slice(8, -1) === 'Array' && typeof x === 'number') {
28         var l = 2 * x + 1, r = 2 * x + 2, largest = x, temp;
29         if (l < len && arr[l] > arr[largest]) {
30             largest = l;
31         }
32         if (r < len && arr[r] > arr[largest]) {
33             largest = r;
34         }
35         if (largest != x) {
36             temp = arr[x];
37             arr[x] = arr[largest];
38             arr[largest] = temp;
39             heapify(arr, largest, len);
40         }
41     } else {
42         return 'arr is not an Array or x is not a number!';
43     }
44 }
複製代碼

3)算法分析

  • 最佳狀況:T(n) = O(nlogn)
  • 最差狀況:T(n) = O(nlogn)
  • 平均狀況:T(n) = O(nlogn)

7、歸併排序

1)算法簡介

  歸併排序是創建在歸併操做上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。歸併排序是一種穩定的排序方法。將已有序的子序列合併,獲得徹底有序的序列;即先使每一個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲2-路歸併。

2)算法描述和實現

  具體算法描述以下:

  1. 把長度爲n的輸入序列分紅兩個長度爲n/2的子序列;
  2. 對這兩個子序列分別採用歸併排序;
  3. 將兩個排序好的子序列合併成一個最終的排序序列。

  JavaScript代碼實現:

複製代碼
 1 function mergeSort(array, p, r) {
 2     if (p < r) {
 3         var q = Math.floor((p + r) / 2);
 4         mergeSort(array, p, q);
 5         mergeSort(array, q + 1, r);
 6         merge(array, p, q, r);
 7     }
 8 }
 9 function merge(array, p, q, r) {
10     var n1 = q - p + 1, n2 = r - q, left = [], right = [], m = n = 0;
11     for (var i = 0; i < n1; i++) {
12         left[i] = array[p + i];
13     }
14     for (var j = 0; j < n2; j++) {
15         right[j] = array[q + 1 + j];
16     }
17     left[n1] = right[n2] = Number.MAX_VALUE;
18     for (var k = p; k <= r; k++) {
19         if (left[m] <= right[n]) {
20             array[k] = left[m];
21             m++;
22         } else {
23             array[k] = right[n];
24             n++;
25         }
26     }
27 }
複製代碼

3)算法分析

  • 最佳狀況:T(n) = O(n)
  • 最差狀況:T(n) = O(nlogn)
  • 平均狀況:T(n) = O(nlogn)

8、桶排序

1)算法簡介

  桶排序 (Bucket sort)的工做的原理:假設輸入數據服從均勻分佈,將數據分到有限數量的桶裏,每一個桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排序)。

2)算法描述和實現

  具體算法描述以下:

  1. 設置一個定量的數組看成空桶;
  2. 遍歷輸入數據,而且把數據一個一個放到對應的桶裏去;
  3. 對每一個不是空的桶進行排序;
  4. 從不是空的桶裏把排好序的數據拼接起來。

  JavaScript代碼實現:

複製代碼
 1 /*方法說明:桶排序
 2 @param  array 數組
 3 @param  num   桶的數量*/
 4 function bucketSort(array, num) {
 5     if (array.length <= 1) {
 6         return array;
 7     }
 8     var len = array.length, buckets = [], result = [], min = max = array[0], regex = '/^[1-9]+[0-9]*$/', space, n = 0;
 9     num = num || ((num > 1 && regex.test(num)) ? num : 10);
10     for (var i = 1; i < len; i++) {
11         min = min <= array[i] ? min : array[i];
12         max = max >= array[i] ? max : array[i];
13     }
14     space = (max - min + 1) / num;
15     for (var j = 0; j < len; j++) {
16         var index = Math.floor((array[j] - min) / space);
17         if (buckets[index]) {   //  非空桶,插入排序
18             var k = buckets[index].length - 1;
19             while (k >= 0 && buckets[index][k] > array[j]) {
20                 buckets[index][k + 1] = buckets[index][k];
21                 k--;
22             }
23             buckets[index][k + 1] = array[j];
24         } else {    //空桶,初始化
25             buckets[index] = [];
26             buckets[index].push(array[j]);
27         }
28     }
29     while (n < num) {
30         result = result.concat(buckets[n]);
31         n++;
32     }
33     return result;
34 }
複製代碼

3)算法分析

  桶排序最好狀況下使用線性時間O(n),桶排序的時間複雜度,取決與對各個桶之間數據進行排序的時間複雜度,由於其它部分的時間複雜度都爲O(n)。很顯然,桶劃分的越小,各個桶之間的數據越少,排序所用的時間也會越少。但相應的空間消耗就會增大。


9、計數排序

1)算法簡介

  計數排序(Counting sort)是一種穩定的排序算法。計數排序使用一個額外的數組C,其中第i個元素是待排序數組A中值等於i的元素的個數。而後根據數組C來將A中的元素排到正確的位置。它只能對整數進行排序。

2)算法描述和實現

  具體算法描述以下:

  1. 找出待排序的數組中最大和最小的元素;
  2. 統計數組中每一個值爲i的元素出現的次數,存入數組C的第i項;
  3. 對全部的計數累加(從C中的第一個元素開始,每一項和前一項相加);
  4. 反向填充目標數組:將每一個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1。

  JavaScript代碼實現:

複製代碼
 1 function countingSort(array) {
 2     var len = array.length, B = [], C = [], min = max = array[0];
 3     for (var i = 0; i < len; i++) {
 4         min = min <= array[i] ? min : array[i];
 5         max = max >= array[i] ? max : array[i];
 6         C[array[i]] = C[array[i]] ? C[array[i]] + 1 : 1;
 7     }
 8     for (var j = min; j < max; j++) {
 9         C[j + 1] = (C[j + 1] || 0) + (C[j] || 0);
10     }
11     for (var k = len - 1; k >=0; k--) {
12         B[C[array[k]] - 1] = array[k];
13         C[array[k]]--;
14     }
15     return B;
16 }
複製代碼

3)算法分析

  當輸入的元素是n 個0到k之間的整數時,它的運行時間是 O(n + k)。計數排序不是比較排序,排序的速度快於任何比較排序算法。因爲用來計數的數組C的長度取決於待排序數組中數據的範圍(等於待排序數組的最大值與最小值的差加上1),這使得計數排序對於數據範圍很大的數組,須要大量時間和內存。

相關文章
相關標籤/搜索