# 前端必會的七種排序算法

"},{"type":"text","marks":[{"type":"strong"}],"text":"時間複雜度：O(N^2)；"},{"type":"text","text":"
"},{"type":"text","marks":[{"type":"strong"}],"text":"空間複雜度：O(1)"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"function BubbleSort(arr) {\n if(arr == null || arr.length <= 0){\n return [];\n }\n var len = arr.length;\n for(var end = len - 1; end > 0; end--){\n for(var i = 0; i < end; i++) {\n if(arr[i] > arr[i + 1]){\n swap(arr, i, i + 1);\n }\n }\n }\n return arr;\n}\nfunction swap(arr, i, j){\n // var temp = arr[i];\n // arr[i] = arr[j];\n // arr[j] = temp;\n //交換也可以用異或運算符\n arr[i] = arr[i] ^ arr[j];\n arr[j] = arr[i] ^ arr[j];\n arr[i] = arr[i] ^ arr[j];\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、選擇排序"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"選擇排序的實現思路：遍歷數組，把最小數放在頭部；
"},{"type":"text","marks":[{"type":"strong"}],"text":"時間複雜度：O(N^2)；"},{"type":"text","text":"
"},{"type":"text","marks":[{"type":"strong"}],"text":"空間複雜度：O(1)"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"function SelectionSort(arr) {\n if(arr == null || arr.length < 0) {\n return [];\n }\n for(var i = 0; i < arr.length - 1; i++) {\n var minIndex = i;\n for(var j = i + 1; j < arr.length; j++) {\n minIndex = arr[j] < arr[minIndex] ? j : minIndex;\n }\n swap(arr, i, minIndex);\n }\n return arr;\n}\n\nfunction swap(arr, i, j) {\n arr[i] = arr[i] ^ arr[j];\n arr[j] = arr[i] ^ arr[j];\n arr[i] = arr[i] ^ arr[j];\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、插入排序"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插入排序實現思路：將一個新的數，和前面的比較，只要當前數小於前一個則和前一個交換位置，否則終止；
"},{"type":"text","marks":[{"type":"strong"}],"text":"時間複雜度：O(N^2)；"},{"type":"text","text":"
"},{"type":"text","marks":[{"type":"strong"}],"text":"空間複雜度：O(1)"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"function insertSort(arr) {\n if(arr == null || arr.length <= 0){\n return [];\n }\n var len = arr.length;\n for(var i = 1; i < len; i++) {\n for(var j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {\n swap(arr, j, j + 1);\n }\n }\n return arr;\n}\n\nfunction swap(arr, i, j){\n var temp = arr[i];\n arr[i] = arr[j];\n arr[j] = temp;\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"四、歸併排序"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歸併排序的思路：
1.先左側部分排好序
2.再右側部分排好序
3.再準備一個輔助數組，用外排的方式，小的開始填，直到有個動到末尾，將另一個數組剩餘部分拷貝到末尾
4.再將輔助數組拷貝回原數組
*"},{"type":"text","marks":[{"type":"italic"}],"text":"時間複雜度:O(N "},{"type":"text","text":" logN)"},{"type":"text","marks":[{"type":"strong"}],"text":"
"},{"type":"text","text":"空間複雜度:O(N)**"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"// 遞歸實現\n\nfunction mergeSort(arr){\n if(arr == null || arr.length <= 0){\n return [];\n }\n sortProcess(arr, 0, arr.length - 1);\n return arr;\n}\n\nfunction sortProcess(arr, L, R){\n //遞歸的終止條件，就是左右邊界索引一樣\n if(L == R){\n return;\n }\n var middle = L + ((R - L) >> 1);//找出中間值\n sortProcess(arr, L, middle);//對左側部分進行遞歸\n sortProcess(arr, middle + 1, R);//對右側部分進行遞歸\n merge(arr, L, middle, R);//然後利用外排方式進行結合\n}\n\nfunction merge(arr, L, middle, R){\n var help = [];\n var l = L;\n var r = middle + 1;\n var index = 0;\n //利用外排方式進行\n while(l <= middle && r <= R){\n help[index++] = arr[l] < arr[r] ? arr[l++] : arr[r++];\n }\n while(l <= middle){\n help.push(arr[l++]);\n }\n while(r <= R){\n help.push(arr[r++]);\n }\n\n for(var i = 0; i < help.length; i++) {\n arr[L + i] = help[i];\n }\n //arr.splice(L, help.length, ...help);//這個利用了ES6的語法\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"// 循環實現\n\nfunction mergeSort(arr){\n if(arr ==null || arr.length <= 0){\n return [];\n }\n var len = arr.length;\n //i每次乘2，是因爲每次合併以後小組元素就變成兩倍個了\n for(var i = 1; i < len; i *= 2){\n var index = 0;//第一組的起始索引\n while( 2 * i + index <= len){\n index += 2 * i;\n merge(arr, index - 2 * i, index - i, index);\n }\n //說明剩餘兩個小組，但其中一個小組數據的數量已經不足2的冪次方個\n if(index + i < len){\n merge(arr, index, index + i, len);\n }\n }\n return arr;\n}\n\n//利用外排的方式進行結合\nfunction merge(arr, start, mid, end){\n //新建一個輔助數組\n var help = [];\n var l = start, r = mid;\n var i = 0;\n while(l < mid && r < end){\n help[i++] = arr[l] < arr[r] ? arr[l++] : arr[r++];\n }\n while(l < mid){\n help[i++] = arr[l++];\n }\n while(r < end){\n help[i++] = arr[r++];\n }\n for(var j = 0; j < help.length; j++){\n arr[start + j] = help[j];\n }\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"五、快速排序"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"快速排序實現思路：隨機取出一個值進行劃分，大於該值放右邊，小於該值放左邊（該算法在經典快排的基礎上經過荷蘭國旗思想和隨機思想進行了改造）
*"},{"type":"text","marks":[{"type":"italic"}],"text":"時間複雜度：O(N"},{"type":"text","text":"logN)"},{"type":"text","marks":[{"type":"strong"}],"text":"
"},{"type":"text","text":"空間複雜度：O(logN)**"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"function quickSort(arr) {\n if(arr == null || arr.length <= 0){\n return [];\n }\n quick(arr, 0, arr.length - 1);\n}\n\nfunction quick(arr, L, R){\n //遞歸結束條件是L >= R\n if(L < R){\n //隨機找一個值，然後和最後一個值進行交換，將經典排序變爲快速排序\n swap(arr, L + Math.floor(Math.random() * (R - L + 1)), R);\n //利用荷蘭國旗問題獲得劃分的邊界，返回的值是小於區域的最大索引和大於區域的最小索引，在這利用荷蘭國旗問題將等於區域部分就不用動了\n var tempArr = partition(arr, L, R, arr[R]);\n quick(arr, L, tempArr[0]);\n quick(arr, tempArr[1], R);\n }\n}\n//返回值是小於區域最後的索引和大於區域的第一個索引\nfunction partition(arr, L, R, num){\n var less = L - 1;\n var more = R + 1;\n var cur = L;\n while(cur < more){\n if(arr[cur] < num){\n swap(arr, ++less, cur++);\n }else if(arr[cur] > num) {\n swap(arr, --more, cur);\n }else{\n cur++;\n }\n }\n return [less, more];\n}\nfunction swap(arr, i, j){\n var temp = arr[i];\n arr[i] = arr[j];\n arr[j] = temp;\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"六、堆排序"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆排序思路：
1.讓數組變成大根堆
2.把最後一個位置和堆頂做交換
3.則最大值在最後，則剩下部分做heapify，則重新調整爲大根堆，則堆頂位置和該部分最後位置做交換
4.重複進行，直到減完，則這樣最後就調整完畢，整個數組排完序（爲一個升序）
*"},{"type":"text","marks":[{"type":"italic"}],"text":"時間複雜度:O(N "},{"type":"text","text":" logN)"},{"type":"text","marks":[{"type":"strong"}],"text":"
"},{"type":"text","text":"空間複雜度:O(1)**"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"function heapSort(arr) {\n if(arr == null || arr.length <= 0) {\n return [];\n }\n\n //首先是建立大頂堆的過程\n for(var i = 0; i < arr.length; i++) {\n heapInsert(arr, i);\n }\n var size = arr.length;//這個值用來指定多少個數組成堆，當得到一個排序的值後這個值減一\n //將堆頂和最後一個位置交換\n /**\n * 當大頂堆建立完成後，然後不斷將最後一個位置和堆頂交換；\n * 這樣最大值就到了最後，則剩下部分做heapify，重新調整爲大根堆，則堆頂位置和倒數第二個位置交換，重複進行，直到全部排序完畢*/\n //由於前面已經是大頂堆，所以直接交換\n swap(arr, 0, --size);\n while(size > 0) {\n //重新變成大頂堆\n heapify(arr, 0, size);\n //進行交換\n swap(arr, 0, --size);\n }\n}\n\n//加堆過程中\nfunction heapInsert(arr, index) {\n //比較當前位置和其父位置，若大於其父位置，則進行交換，並將索引移動到其父位置進行循環，否則跳過\n //結束條件是比父位置小或者到達根節點處\n while(arr[index] > arr[parseInt((index - 1) / 2)]){\n //進行交換\n swap(arr, index, parseInt((index - 1) / 2));\n index = parseInt((index - 1) / 2);\n }\n}\n//減堆過程\n/**\n * size指的是這個數組前多少個數構成一個堆\n * 如果你想把堆頂彈出，則把堆頂和最後一個數交換，把size減1，然後從0位置經歷一次heapify，調整一下，剩餘部分變成大頂堆*/\nfunction heapify(arr, index, size) {\n var left = 2 * index + 1;\n while(left < size) {\n var largest = (left + 1 < size && arr[left] < arr[left + 1]) ? left + 1 : left;\n largest = arr[index] > arr[largest] ? index : largest;\n\n //如果最大值索引和傳進來索引一樣，則該值到達指定位置，直接結束循環\n if(index == largest) {\n break;\n }\n\n //進行交換，並改變索引和其左子節點\n swap(arr, index, largest);\n index = largest;\n left = 2 * index + 1;\n }\n}\n\nfunction swap(arr, i, j) {\n var temp = arr[i];\n arr[i] = arr[j];\n arr[j] = temp;\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"七、桶排序"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"桶排序會經歷三次遍歷：準備一個數組、遍歷一遍數組、重構一遍數組，是非基於比較的排序，下面以一個問題來闡述其思路。
"},{"type":"text","marks":[{"type":"strong"}],"text":"問題："},{"type":"text","text":"

"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"思路："},{"type":"text","text":"
1.準備桶：數組中有N個數就準備N+1個桶
2.遍歷一遍數組，找到最大值max和最小值min"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"。若min = max，則差值=0；若min≠max，則最小值放在0號桶，最大值放在N號桶，剩下的數屬於哪個範圍就進哪個桶
3.根據鴿籠原理，則肯定有一個桶爲空桶，設計該桶的目的是爲了否定最大值在一個桶中，則最大差值的兩個數一定來自於兩個桶，但空桶兩側並不一定是最大值
4.所以只記錄所有進入該桶的最小值min和最大值max和一個布爾值表示該桶有沒有值
5.然後遍歷這個數組，如果桶是空的，則跳到下一個數，如果桶非空，則找前一個非空桶，則最大差值=當前桶min - 上一個非空桶max，用全局變量更新最大值
"},{"type":"text","marks":[{"type":"strong"}],"text":"時間複雜度：O(N)"},{"type":"text","text":"
"},{"type":"text","marks":[{"type":"strong"}],"text":"空間複雜度：O(N)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"function maxGap(arr) {\n if(arr == null || arr.length <= 0) {\n return 0;\n }\n var len = arr.length;\n var max = -Infinity, min = Infinity;\n //遍歷一遍數組,找到最大值max和最小值min\n for(var i = 0; i < len; i++) {\n max = max > arr[i] ? max : arr[i];\n min = min > arr[i] ? arr[i] : min;\n }\n\n //若min = max,則差值爲0;\n if(min == max) {\n return 0;\n }\n\n var hasNum = new Array(len + 1);\n var mins = new Array(len + 1);\n var maxs = new Array(len + 1);\n\n var bid = 0;//指定桶的編號\n\n for(var i = 0; i < len; i++) {\n bid = bucket(arr[i], min, max, len);//獲得該值是在哪個桶//由於有N+1個桶，所以間隔就是N個，所以此處除以的是len，然後通過這個函數得到應該放到哪個桶裏\n maxs[bid] = hasNum[bid] ? Math.max(arr[i], maxs[bid]) : arr[i];\n mins[bid] = hasNum[bid] ? Math.min(arr[i], mins[bid]) : arr[i];\n hasNum[bid] = true;\n }\n\n var res = 0;\n var lastMax = maxs[0];\n\n for(var i = 0; i < len + 1; i++) {\n if(hasNum[i]) {\n res = Math.max(mins[i] - lastMax, res);\n lastMax = maxs[i];\n }\n }\n return res;\n}\n\n//獲得桶號\n//這個函數用於判斷在哪個桶中，參數分別爲值、最小值、最大值、桶間隔\nfunction bucket(value, min, max, len) {\n return parseInt((value - min) / ((max - min) / len));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎老鐵們加羣或者私聊"}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/04/04bcc3924f6f42db08e94af0d4a64bc0.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}