摘要:之所以把歸并排序快速排序希爾排序堆排序放在一起比較,是因?yàn)樗鼈兊钠骄鶗r(shí)間復(fù)雜度都為。歸并排序是一種穩(wěn)定的排序方法。因此,快速排序并不穩(wěn)定。希爾排序思想先將整個(gè)待排序的記錄序列分割成為若干子序列。
1. 前言
算法為王。
想學(xué)好前端,先練好內(nèi)功,只有內(nèi)功深厚者,前端之路才會走得更遠(yuǎn)。
筆者寫的 JavaScript 數(shù)據(jù)結(jié)構(gòu)與算法之美 系列用的語言是 JavaScript ,旨在入門數(shù)據(jù)結(jié)構(gòu)與算法和方便以后復(fù)習(xí)。
之所以把歸并排序、快速排序、希爾排序、堆排序放在一起比較,是因?yàn)樗鼈兊钠骄鶗r(shí)間復(fù)雜度都為 O(nlogn)。
請大家?guī)е鴨栴}:快排和歸并用的都是分治思想,遞推公式和遞歸代碼也非常相似,那它們的區(qū)別在哪里呢 ? 來閱讀下文。
2. 歸并排序(Merge Sort)思想
排序一個(gè)數(shù)組,我們先把數(shù)組從中間分成前后兩部分,然后對前后兩部分分別排序,再將排好序的兩部分合并在一起,這樣整個(gè)數(shù)組就都有序了。
歸并排序采用的是分治思想。
分治,顧名思義,就是分而治之,將一個(gè)大問題分解成小的子問題來解決。小的子問題解決了,大問題也就解決了。
注:x >> 1 是位運(yùn)算中的右移運(yùn)算,表示右移一位,等同于 x 除以 2 再取整,即 x >> 1 === Math.floor(x / 2) 。
實(shí)現(xiàn)
const mergeSort = arr => { //采用自上而下的遞歸方法 const len = arr.length; if (len < 2) { return arr; } // length >> 1 和 Math.floor(len / 2) 等價(jià) let middle = Math.floor(len / 2), left = arr.slice(0, middle), right = arr.slice(middle); // 拆分為兩個(gè)子數(shù)組 return merge(mergeSort(left), mergeSort(right)); }; const merge = (left, right) => { const result = []; while (left.length && right.length) { // 注意: 判斷的條件是小于或等于,如果只是小于,那么排序?qū)⒉环€(wěn)定. if (left[0] <= right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } while (left.length) result.push(left.shift()); while (right.length) result.push(right.shift()); return result; };
測試
// 測試 const arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]; console.time("歸并排序耗時(shí)"); console.log("arr :", mergeSort(arr)); console.timeEnd("歸并排序耗時(shí)"); // arr : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50] // 歸并排序耗時(shí): 0.739990234375ms
分析
第一,歸并排序是原地排序算法嗎 ?
這是因?yàn)闅w并排序的合并函數(shù),在合并兩個(gè)有序數(shù)組為一個(gè)有序數(shù)組時(shí),需要借助額外的存儲空間。
實(shí)際上,盡管每次合并操作都需要申請額外的內(nèi)存空間,但在合并完成之后,臨時(shí)開辟的內(nèi)存空間就被釋放掉了。在任意時(shí)刻,CPU 只會有一個(gè)函數(shù)在執(zhí)行,也就只會有一個(gè)臨時(shí)的內(nèi)存空間在使用。臨時(shí)內(nèi)存空間最大也不會超過 n 個(gè)數(shù)據(jù)的大小,所以空間復(fù)雜度是 O(n)。
所以,歸并排序不是原地排序算法。
第二,歸并排序是穩(wěn)定的排序算法嗎 ?
merge 方法里面的 left[0] <= right[0] ,保證了值相同的元素,在合并前后的先后順序不變。歸并排序是一種穩(wěn)定的排序方法。
第三,歸并排序的時(shí)間復(fù)雜度是多少 ?
從效率上看,歸并排序可算是排序算法中的佼佼者。假設(shè)數(shù)組長度為 n,那么拆分?jǐn)?shù)組共需 logn 步, 又每步都是一個(gè)普通的合并子數(shù)組的過程,時(shí)間復(fù)雜度為 O(n),故其綜合時(shí)間復(fù)雜度為 O(nlogn)。
最佳情況:T(n) = O(nlogn)。
最差情況:T(n) = O(nlogn)。
平均情況:T(n) = O(nlogn)。
動畫
3. 快速排序 (Quick Sort)快速排序的特點(diǎn)就是快,而且效率高!它是處理大數(shù)據(jù)最快的排序算法之一。
思想
先找到一個(gè)基準(zhǔn)點(diǎn)(一般指數(shù)組的中部),然后數(shù)組被該基準(zhǔn)點(diǎn)分為兩部分,依次與該基準(zhǔn)點(diǎn)數(shù)據(jù)比較,如果比它小,放左邊;反之,放右邊。
左右分別用一個(gè)空數(shù)組去存儲比較后的數(shù)據(jù)。
最后遞歸執(zhí)行上述操作,直到數(shù)組長度 <= 1;
特點(diǎn):快速,常用。
缺點(diǎn):需要另外聲明兩個(gè)數(shù)組,浪費(fèi)了內(nèi)存空間資源。
實(shí)現(xiàn)
方法一:
const quickSort1 = arr => { if (arr.length <= 1) { return arr; } //取基準(zhǔn)點(diǎn) const midIndex = Math.floor(arr.length / 2); //取基準(zhǔn)點(diǎn)的值,splice(index,1) 則返回的是含有被刪除的元素的數(shù)組。 const valArr = arr.splice(midIndex, 1); const midIndexVal = valArr[0]; const left = []; //存放比基準(zhǔn)點(diǎn)小的數(shù)組 const right = []; //存放比基準(zhǔn)點(diǎn)大的數(shù)組 //遍歷數(shù)組,進(jìn)行判斷分配 for (let i = 0; i < arr.length; i++) { if (arr[i] < midIndexVal) { left.push(arr[i]); //比基準(zhǔn)點(diǎn)小的放在左邊數(shù)組 } else { right.push(arr[i]); //比基準(zhǔn)點(diǎn)大的放在右邊數(shù)組 } } //遞歸執(zhí)行以上操作,對左右兩個(gè)數(shù)組進(jìn)行操作,直到數(shù)組長度為 <= 1 return quickSort1(left).concat(midIndexVal, quickSort1(right)); }; const array2 = [5, 4, 3, 2, 1]; console.log("quickSort1 ", quickSort1(array2)); // quickSort1: [1, 2, 3, 4, 5]
方法二:
// 快速排序 const quickSort = (arr, left, right) => { let len = arr.length, partitionIndex; left = typeof left != "number" ? 0 : left; right = typeof right != "number" ? len - 1 : right; if (left < right) { partitionIndex = partition(arr, left, right); quickSort(arr, left, partitionIndex - 1); quickSort(arr, partitionIndex + 1, right); } return arr; }; const partition = (arr, left, right) => { //分區(qū)操作 let pivot = left, //設(shè)定基準(zhǔn)值(pivot) index = pivot + 1; for (let i = index; i <= right; i++) { if (arr[i] < arr[pivot]) { swap(arr, i, index); index++; } } swap(arr, pivot, index - 1); return index - 1; }; const swap = (arr, i, j) => { let temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; };
測試
// 測試 const array = [5, 4, 3, 2, 1]; console.log("原始array:", array); const newArr = quickSort(array); console.log("newArr:", newArr); // 原始 array: ?[5, 4, 3, 2, 1] // newArr: ? [1, 4, 3, 2, 5]
分析
第一,快速排序是原地排序算法嗎 ?
因?yàn)?partition() 函數(shù)進(jìn)行分區(qū)時(shí),不需要很多額外的內(nèi)存空間,所以快排是原地排序算法。
第二,快速排序是穩(wěn)定的排序算法嗎 ?
和選擇排序相似,快速排序每次交換的元素都有可能不是相鄰的,因此它有可能打破原來值為相同的元素之間的順序。因此,快速排序并不穩(wěn)定。
第三,快速排序的時(shí)間復(fù)雜度是多少 ?
極端的例子:如果數(shù)組中的數(shù)據(jù)原來已經(jīng)是有序的了,比如 1,3,5,6,8。如果我們每次選擇最后一個(gè)元素作為 pivot,那每次分區(qū)得到的兩個(gè)區(qū)間都是不均等的。我們需要進(jìn)行大約 n 次分區(qū)操作,才能完成快排的整個(gè)過程。每次分區(qū)我們平均要掃描大約 n / 2 個(gè)元素,這種情況下,快排的時(shí)間復(fù)雜度就從 O(nlogn) 退化成了 O(n2)。
最佳情況:T(n) = O(nlogn)。
最差情況:T(n) = O(n2)。
平均情況:T(n) = O(nlogn)。
動畫
解答開篇問題
快排和歸并用的都是分治思想,遞推公式和遞歸代碼也非常相似,那它們的區(qū)別在哪里呢 ?
可以發(fā)現(xiàn):
歸并排序的處理過程是由下而上的,先處理子問題,然后再合并。
而快排正好相反,它的處理過程是由上而下的,先分區(qū),然后再處理子問題。
歸并排序雖然是穩(wěn)定的、時(shí)間復(fù)雜度為 O(nlogn) 的排序算法,但是它是非原地排序算法。
歸并之所以是非原地排序算法,主要原因是合并函數(shù)無法在原地執(zhí)行。
快速排序通過設(shè)計(jì)巧妙的原地分區(qū)函數(shù),可以實(shí)現(xiàn)原地排序,解決了歸并排序占用太多內(nèi)存的問題。
4. 希爾排序(Shell Sort)思想
先將整個(gè)待排序的記錄序列分割成為若干子序列。
分別進(jìn)行直接插入排序。
待整個(gè)序列中的記錄基本有序時(shí),再對全體記錄進(jìn)行依次直接插入排序。
過程
舉個(gè)易于理解的例子:[35, 33, 42, 10, 14, 19, 27, 44],我們采取間隔 4。創(chuàng)建一個(gè)位于 4 個(gè)位置間隔的所有值的虛擬子列表。下面這些值是 { 35, 14 },{ 33, 19 },{ 42, 27 } 和 { 10, 44 }。
我們比較每個(gè)子列表中的值,并在原始數(shù)組中交換它們(如果需要)。完成此步驟后,新數(shù)組應(yīng)如下所示。
然后,我們采用 2 的間隔,這個(gè)間隙產(chǎn)生兩個(gè)子列表:{ 14, 27, 35, 42 }, { 19, 10, 33, 44 }。
我們比較并交換原始數(shù)組中的值(如果需要)。完成此步驟后,數(shù)組變成:[14, 10, 27, 19, 35, 33, 42, 44],圖如下所示,10 與 19 的位置互換一下。
最后,我們使用值間隔 1 對數(shù)組的其余部分進(jìn)行排序,Shell sort 使用插入排序?qū)?shù)組進(jìn)行排序。
實(shí)現(xiàn)
const shellSort = arr => { let len = arr.length, temp, gap = 1; console.time("希爾排序耗時(shí)"); while (gap < len / 3) { //動態(tài)定義間隔序列 gap = gap * 3 + 1; } for (gap; gap > 0; gap = Math.floor(gap / 3)) { for (let i = gap; i < len; i++) { temp = arr[i]; let j = i - gap; for (; j >= 0 && arr[j] > temp; j -= gap) { arr[j + gap] = arr[j]; } arr[j + gap] = temp; console.log("arr :", arr); } } console.timeEnd("希爾排序耗時(shí)"); return arr; };
測試
// 測試 const array = [35, 33, 42, 10, 14, 19, 27, 44]; console.log("原始array:", array); const newArr = shellSort(array); console.log("newArr:", newArr); // 原始 array: ??[35, 33, 42, 10, 14, 19, 27, 44] // arr : ??[14, 33, 42, 10, 35, 19, 27, 44] // arr : ??[14, 19, 42, 10, 35, 33, 27, 44] // arr : ??[14, 19, 27, 10, 35, 33, 42, 44] // arr : ??[14, 19, 27, 10, 35, 33, 42, 44] // arr : ??[14, 19, 27, 10, 35, 33, 42, 44] // arr : ??[14, 19, 27, 10, 35, 33, 42, 44] // arr : ??[10, 14, 19, 27, 35, 33, 42, 44] // arr : ??[10, 14, 19, 27, 35, 33, 42, 44] // arr : ??[10, 14, 19, 27, 33, 35, 42, 44] // arr : ??[10, 14, 19, 27, 33, 35, 42, 44] // arr : ??[10, 14, 19, 27, 33, 35, 42, 44] // 希爾排序耗時(shí): 3.592041015625ms // newArr: ? [10, 14, 19, 27, 33, 35, 42, 44]
分析
第一,希爾排序是原地排序算法嗎 ?
希爾排序過程中,只涉及相鄰數(shù)據(jù)的交換操作,只需要常量級的臨時(shí)空間,空間復(fù)雜度為 O(1) 。所以,希爾排序是原地排序算法。
第二,希爾排序是穩(wěn)定的排序算法嗎 ?
我們知道,單次直接插入排序是穩(wěn)定的,它不會改變相同元素之間的相對順序,但在多次不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,可能導(dǎo)致相同元素相對順序發(fā)生變化。
因此,希爾排序不穩(wěn)定。
第三,希爾排序的時(shí)間復(fù)雜度是多少 ?
最佳情況:T(n) = O(n logn)。
最差情況:T(n) = O(n (log(n))2)。
平均情況:T(n) = 取決于間隙序列。
動畫
5. 堆排序(Heap Sort)堆的定義
堆其實(shí)是一種特殊的樹。只要滿足這兩點(diǎn),它就是一個(gè)堆。
堆是一個(gè)完全二叉樹。
完全二叉樹:除了最后一層,其他層的節(jié)點(diǎn)個(gè)數(shù)都是滿的,最后一層的節(jié)點(diǎn)都靠左排列。
堆中每一個(gè)節(jié)點(diǎn)的值都必須大于等于(或小于等于)其子樹中每個(gè)節(jié)點(diǎn)的值。
也可以說:堆中每個(gè)節(jié)點(diǎn)的值都大于等于(或者小于等于)其左右子節(jié)點(diǎn)的值。這兩種表述是等價(jià)的。
對于每個(gè)節(jié)點(diǎn)的值都大于等于子樹中每個(gè)節(jié)點(diǎn)值的堆,我們叫作大頂堆。
對于每個(gè)節(jié)點(diǎn)的值都小于等于子樹中每個(gè)節(jié)點(diǎn)值的堆,我們叫作小頂堆。
其中圖 1 和 圖 2 是大頂堆,圖 3 是小頂堆,圖 4 不是堆。除此之外,從圖中還可以看出來,對于同一組數(shù)據(jù),我們可以構(gòu)建多種不同形態(tài)的堆。
思想
將初始待排序關(guān)鍵字序列 (R1, R2 .... Rn) 構(gòu)建成大頂堆,此堆為初始的無序區(qū);
將堆頂元素 R[1] 與最后一個(gè)元素 R[n] 交換,此時(shí)得到新的無序區(qū) (R1, R2, ..... Rn-1) 和新的有序區(qū) (Rn) ,且滿足 R[1, 2 ... n-1] <= R[n]。
由于交換后新的堆頂 R[1] 可能違反堆的性質(zhì),因此需要對當(dāng)前無序區(qū) (R1, R2 ...... Rn-1) 調(diào)整為新堆,然后再次將 R[1] 與無序區(qū)最后一個(gè)元素交換,得到新的無序區(qū) (R1, R2 .... Rn-2) 和新的有序區(qū) (Rn-1, Rn)。不斷重復(fù)此過程,直到有序區(qū)的元素個(gè)數(shù)為 n - 1,則整個(gè)排序過程完成。
實(shí)現(xiàn)
// 堆排序 const heapSort = array => { console.time("堆排序耗時(shí)"); // 初始化大頂堆,從第一個(gè)非葉子結(jié)點(diǎn)開始 for (let i = Math.floor(array.length / 2 - 1); i >= 0; i--) { heapify(array, i, array.length); } // 排序,每一次 for 循環(huán)找出一個(gè)當(dāng)前最大值,數(shù)組長度減一 for (let i = Math.floor(array.length - 1); i > 0; i--) { // 根節(jié)點(diǎn)與最后一個(gè)節(jié)點(diǎn)交換 swap(array, 0, i); // 從根節(jié)點(diǎn)開始調(diào)整,并且最后一個(gè)結(jié)點(diǎn)已經(jīng)為當(dāng)前最大值,不需要再參與比較,所以第三個(gè)參數(shù)為 i,即比較到最后一個(gè)結(jié)點(diǎn)前一個(gè)即可 heapify(array, 0, i); } console.timeEnd("堆排序耗時(shí)"); return array; }; // 交換兩個(gè)節(jié)點(diǎn) const swap = (array, i, j) => { let temp = array[i]; array[i] = array[j]; array[j] = temp; }; // 將 i 結(jié)點(diǎn)以下的堆整理為大頂堆,注意這一步實(shí)現(xiàn)的基礎(chǔ)實(shí)際上是: // 假設(shè)結(jié)點(diǎn) i 以下的子堆已經(jīng)是一個(gè)大頂堆,heapify 函數(shù)實(shí)現(xiàn)的 // 功能是實(shí)際上是:找到 結(jié)點(diǎn) i 在包括結(jié)點(diǎn) i 的堆中的正確位置。 // 后面將寫一個(gè) for 循環(huán),從第一個(gè)非葉子結(jié)點(diǎn)開始,對每一個(gè)非葉子結(jié)點(diǎn) // 都執(zhí)行 heapify 操作,所以就滿足了結(jié)點(diǎn) i 以下的子堆已經(jīng)是一大頂堆 const heapify = (array, i, length) => { let temp = array[i]; // 當(dāng)前父節(jié)點(diǎn) // j < length 的目的是對結(jié)點(diǎn) i 以下的結(jié)點(diǎn)全部做順序調(diào)整 for (let j = 2 * i + 1; j < length; j = 2 * j + 1) { temp = array[i]; // 將 array[i] 取出,整個(gè)過程相當(dāng)于找到 array[i] 應(yīng)處于的位置 if (j + 1 < length && array[j] < array[j + 1]) { j++; // 找到兩個(gè)孩子中較大的一個(gè),再與父節(jié)點(diǎn)比較 } if (temp < array[j]) { swap(array, i, j); // 如果父節(jié)點(diǎn)小于子節(jié)點(diǎn):交換;否則跳出 i = j; // 交換后,temp 的下標(biāo)變?yōu)?j } else { break; } } };
測試
const array = [4, 6, 8, 5, 9, 1, 2, 5, 3, 2]; console.log("原始array:", array); const newArr = heapSort(array); console.log("newArr:", newArr); // 原始 array: ?[4, 6, 8, 5, 9, 1, 2, 5, 3, 2] // 堆排序耗時(shí): 0.15087890625ms // newArr: ? [1, 2, 2, 3, 4, 5, 5, 6, 8, 9]
分析
第一,堆排序是原地排序算法嗎 ?
整個(gè)堆排序的過程,都只需要極個(gè)別臨時(shí)存儲空間,所以堆排序是原地排序算法。
第二,堆排序是穩(wěn)定的排序算法嗎 ?
因?yàn)樵谂判虻倪^程,存在將堆的最后一個(gè)節(jié)點(diǎn)跟堆頂節(jié)點(diǎn)互換的操作,所以就有可能改變值相同數(shù)據(jù)的原始相對順序。
所以,堆排序是不穩(wěn)定的排序算法。
第三,堆排序的時(shí)間復(fù)雜度是多少 ?
堆排序包括建堆和排序兩個(gè)操作,建堆過程的時(shí)間復(fù)雜度是 O(n),排序過程的時(shí)間復(fù)雜度是 O(nlogn),所以,堆排序整體的時(shí)間復(fù)雜度是 O(nlogn)。
最佳情況:T(n) = O(nlogn)。
最差情況:T(n) = O(nlogn)。
平均情況:T(n) = O(nlogn)。
動畫
6. 排序算法的復(fù)雜性對比復(fù)雜性對比
算法可視化工具
算法可視化工具 algorithm-visualizer
算法可視化工具 algorithm-visualizer 是一個(gè)交互式的在線平臺,可以從代碼中可視化算法,還可以看到代碼執(zhí)行的過程。
效果如下圖。
旨在通過交互式可視化的執(zhí)行來揭示算法背后的機(jī)制。
算法可視化來源 https://visualgo.net/en
效果如下圖。
https://www.ee.ryerson.ca
illustrated-algorithms
變量和操作的可視化表示增強(qiáng)了控制流和實(shí)際源代碼。您可以快速前進(jìn)和后退執(zhí)行,以密切觀察算法的工作方式。
7. 文章輸出計(jì)劃JavaScript 數(shù)據(jù)結(jié)構(gòu)與算法之美 的系列文章,堅(jiān)持 3 - 7 天左右更新一篇,暫定計(jì)劃如下表。
| 標(biāo)題 | 鏈接 |
| :------ | :------ |
| 時(shí)間和空間復(fù)雜度 | https://github.com/biaochenxu... |
| 線性表(數(shù)組、鏈表、棧、隊(duì)列) | https://github.com/biaochenxu... |
| 實(shí)現(xiàn)一個(gè)前端路由,如何實(shí)現(xiàn)瀏覽器的前進(jìn)與后退 ?| https://github.com/biaochenxu... |
| 棧內(nèi)存與堆內(nèi)存 、淺拷貝與深拷貝 | https://github.com/biaochenxu... |
| 遞歸 | https://github.com/biaochenxu... |
| 非線性表(樹、堆) | https://github.com/biaochenxu... |
| 冒泡排序、選擇排序、插入排序 | https://github.com/biaochenxu... |
| 歸并排序、快速排序、希爾排序、堆排序 | https://github.com/biaochenxu... |
| 計(jì)數(shù)排序、桶排序、基數(shù)排序 | 精彩待續(xù) |
| 十大經(jīng)典排序匯總 | 精彩待續(xù) |
| 強(qiáng)烈推薦 GitHub 上值得前端學(xué)習(xí)的數(shù)據(jù)結(jié)構(gòu)與算法項(xiàng)目 | https://github.com/biaochenxu... |
如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑垊?wù)必給予指正,十分感謝。8. 最后
文中所有的代碼及測試事例都已經(jīng)放到我的 GitHub 上了。
覺得有用 ?喜歡就收藏,順便點(diǎn)個(gè)贊吧。
參考文章:
JS 實(shí)現(xiàn)堆排序
數(shù)據(jù)結(jié)構(gòu)與算法之美
十大經(jīng)典排序算法總結(jié)(JavaScript 描述)
JS 中可能用得到的全部的排序算法
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/106010.html
摘要:筆者寫的數(shù)據(jù)結(jié)構(gòu)與算法之美系列用的語言是,旨在入門數(shù)據(jù)結(jié)構(gòu)與算法和方便以后復(fù)習(xí)。這應(yīng)該是目前較為簡單的十大經(jīng)典排序算法的文章講解了吧。比如原本在的前面,而,排序之后,在的后面十大經(jīng)典排序算法冒泡排序思想冒泡排序只會操作相鄰的兩個(gè)數(shù)據(jù)。 showImg(https://segmentfault.com/img/bVbvHet); 1. 前言 算法為王。想學(xué)好前端,先練好內(nèi)功,內(nèi)功不行,就...
摘要:之所以把冒泡排序選擇排序插入排序放在一起比較,是因?yàn)樗鼈兊钠骄鶗r(shí)間復(fù)雜度都為。其中,冒泡排序就是原地排序算法。所以冒泡排序是穩(wěn)定的排序算法。選擇排序思路選擇排序算法的實(shí)現(xiàn)思路有點(diǎn)類似插入排序,也分已排序區(qū)間和未排序區(qū)間。 showImg(https://segmentfault.com/img/bVbuvnj?w=900&h=250); 1. 前言 算法為王。 想學(xué)好前端,先練好內(nèi)功,...
摘要:強(qiáng)烈推薦上值得前端學(xué)習(xí)的數(shù)據(jù)結(jié)構(gòu)與算法項(xiàng)目,包含圖的演示過程與視頻講解。該倉庫包含了多種基于的算法與數(shù)據(jù)結(jié)構(gòu),提供進(jìn)一步閱讀的解釋和鏈接。數(shù)據(jù)結(jié)構(gòu)和算法必知必會的個(gè)代碼實(shí)現(xiàn)。 showImg(https://segmentfault.com/img/bVbvpYZ); 前言 算法為王。想學(xué)好前端,先練好內(nèi)功,內(nèi)功不行,就算招式練的再花哨,終究成不了高手;只有內(nèi)功深厚者,前端之路才會走得...
摘要:之所以把計(jì)數(shù)排序桶排序基數(shù)排序放在一起比較,是因?yàn)樗鼈兊钠骄鶗r(shí)間復(fù)雜度都為。動畫計(jì)數(shù)排序思想找出待排序的數(shù)組中最大和最小的元素。桶排序計(jì)數(shù)排序能派上用場嗎手機(jī)號碼有位,范圍太大,顯然不適合用這兩種排序算法。 showImg(https://segmentfault.com/img/bVbuF9e?w=900&h=500); 1. 前言 算法為王。 想學(xué)好前端,先練好內(nèi)功,只有內(nèi)功深厚者...
閱讀 1307·2019-08-30 15:44
閱讀 1978·2019-08-30 13:49
閱讀 1650·2019-08-26 13:54
閱讀 3484·2019-08-26 10:20
閱讀 3238·2019-08-23 17:18
閱讀 3293·2019-08-23 17:05
閱讀 2129·2019-08-23 15:38
閱讀 1011·2019-08-23 14:35