十大排序总结(js实现、稳定性、内外部排序区别、时间空间复杂度、冒泡、快速、直接选择、堆、直接插入、希尔、桶、基数、归并、计数排序)
目錄
排序相關(guān)概念
穩(wěn)定性
?內(nèi)部排序
外部排序
?十種排序算法特點(diǎn)總結(jié)
交換排序
冒泡排序(數(shù)組sort方法的原理)
圖解
?js實(shí)現(xiàn)
特點(diǎn)
快速排序
圖解
js實(shí)現(xiàn)
特點(diǎn)
選擇排序
直接選擇排序
圖解
?js實(shí)現(xiàn)
特點(diǎn)
堆排序
大(小)頂堆
非葉節(jié)點(diǎn)
js實(shí)現(xiàn)?
特點(diǎn)
插入排序?
直接插入排序
圖解
js實(shí)現(xiàn)?
特點(diǎn)
希爾排序
圖解
js實(shí)現(xiàn)
特點(diǎn)
其它排序
桶排序
圖解
js實(shí)現(xiàn)
特點(diǎn)
基數(shù)排序
圖解
js實(shí)現(xiàn)
特點(diǎn)
歸并排序
圖解
?js實(shí)現(xiàn)
特點(diǎn)
計(jì)數(shù)排序
圖解
?js實(shí)現(xiàn)
特點(diǎn)
排序相關(guān)概念
穩(wěn)定性
不改變相同數(shù)值的順序?yàn)榉€(wěn)定。例如在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,則稱這種排序算法是穩(wěn)定的(記錄的相對(duì)次序保持不變);否則稱為不穩(wěn)定的。
?內(nèi)部排序
指待排序列完全存放在內(nèi)存中所進(jìn)行的排序過程,適合不太大的元素序列。有冒泡、選擇、插入、希爾、快速、堆排序(通常快速排序?yàn)樽詈玫?#xff09;。
外部排序
外部排序指的是大文件的排序,即待排序的記錄存儲(chǔ)在外存儲(chǔ)器上,待排序的文件無法一次裝入內(nèi)存,需要在內(nèi)存和外部存儲(chǔ)器之間進(jìn)行多次數(shù)據(jù)交換,以達(dá)到排序整個(gè)文件的目的。有歸并、計(jì)數(shù)、桶、基數(shù)排序。
?十種排序算法特點(diǎn)總結(jié)
交換排序
冒泡排序(數(shù)組sort方法的原理)
1.比較相鄰的元素。如果第一個(gè)比第二個(gè)大,就交換他們兩個(gè)。
2.對(duì)每一對(duì)相鄰元素作同樣的工作,從開始第一對(duì)到結(jié)尾的最后一對(duì)。這步做完后,最后的元素會(huì)是最大的數(shù)。
3.針對(duì)所有的元素重復(fù)以上的步驟,除了最后一個(gè)。
4.持續(xù)每次對(duì)越來越少的元素重復(fù)上面的步驟,直到?jīng)]有任何一對(duì)數(shù)字需要比較。
圖解
?js實(shí)現(xiàn)
function swap(arr, x, y) {let tmp = arr[x]arr[x] = arr[y]arr[y] = tmp
}
function bubbleSort(arr) {let len = arr.length;for (let i = 0; i < len - 1; i++) {//比較出一輪中的最大值放入最后for (let j = 0; j < len - 1 - i; j++) {if (arr[j] > arr[j + 1]) {swap(arr, j, j + 1)}}}return arr;
}
特點(diǎn)
快速排序
1.以數(shù)組第一位為基準(zhǔn)值,將小于的元素放在左邊,大于的元素放在右邊,分為左右兩塊。
2.在左右2塊中重復(fù)第一步,不斷循環(huán),直到分區(qū)的大小為1則停止。
圖解
注意左右分區(qū),我們可以通過將基準(zhǔn)元素和左邊分區(qū)最右端元素交換實(shí)現(xiàn)
js實(shí)現(xiàn)
function swap(arr, x, y) {let tmp = arr[x]arr[x] = arr[y]arr[y] = tmp
}
//用于一次以基準(zhǔn)值進(jìn)行排序
function partition(arr, left, right) {//設(shè)定基準(zhǔn)值pivotlet pivot = leftlet index = pivot + 1for (let i = index; i <= right; i++) {//小于基準(zhǔn)值的放左邊,大于的不變if (arr[i] < arr[pivot]) {swap(arr, i, index)index++}}//和小于基準(zhǔn)值的最右邊一個(gè)交換位置,實(shí)現(xiàn)左邊小于基準(zhǔn)值,右邊大于基準(zhǔn)值swap(arr, pivot, index - 1)//返回基準(zhǔn)值的位置return index - 1
}
function quickSort(arr, left, right) {let len = arr.lengthleft = typeof left != 'number' ? 0 : leftright = typeof right != 'number' ? len - 1 : rightif (left < right) {//進(jìn)行一次排序let partitionIndex = partition(arr, left, right)//遞歸對(duì)左邊進(jìn)行排序quickSort(arr, left, partitionIndex - 1)//遞歸對(duì)右邊進(jìn)行排序quickSort(arr, partitionIndex + 1, right)}return arr
}
console.log(quickSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))
特點(diǎn)
選擇排序
直接選擇排序
每一次遍歷待排序的數(shù)據(jù)元素從中選出最小(或最大)的一個(gè)元素,存放在序列的起始(或者末尾)位置(和原存放位置上的元素交換順序),直到全部待排序的數(shù)據(jù)元素排完。
圖解
?js實(shí)現(xiàn)
//交換數(shù)組下標(biāo)x位和y位的元素
function swap(arr, x, y) {arr[x] = arr[x] - arr[y]arr[y] = arr[y] + arr[x]arr[x] = arr[y] - arr[x]return arr
}
function selectionSort(arr) {let t = arr.lengthlet x = 0while (x != t) {let min = 0;let tmp = Number.MAX_VALUE//選出最小的元素的下標(biāo)for (let i = x; i < t; i++) {if (tmp > arr[i]) {tmp = arr[i]min = i}}//不是當(dāng)前位置,則交換順序if (x != min) {swap(arr, x, min)}x++}return arr
}
console.log(selectionSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))
特點(diǎn)
堆排序
1.將元素排序?yàn)橐粋€(gè)大(小)頂堆,則頂部元素為最大(小)元素,將其與元素尾(頭)部元素交換順序。
2.重新排序剩下元素,再次形成大(小)頂堆,重復(fù)1操作,直到只剩一個(gè)元素。
大(小)頂堆
每個(gè)節(jié)點(diǎn)的值都大于(小于)或等于其子節(jié)點(diǎn)的值,在堆排序算法中用于升(降)序排列;
代碼中表示為:
大頂堆a(bǔ)rr[i]>=arr[2i+1]和arr[i]>=arr[2i+2]????????父節(jié)點(diǎn)大于左右子節(jié)點(diǎn)
小頂堆a(bǔ)rr[i]<=arr[2i+1]和arr[i]<=arr[2i+2]?????????父節(jié)點(diǎn)小于左右子節(jié)點(diǎn)
非葉節(jié)點(diǎn)
有孩子節(jié)點(diǎn)的節(jié)點(diǎn)。最后一個(gè)非葉節(jié)點(diǎn)在數(shù)組中的序號(hào)為元素個(gè)數(shù)除以2向下取整。
js實(shí)現(xiàn)?
// 建立大頂堆
function buildMaxHeap(arr) {let len = arr.length;//Math.floor(len/2)表示最后一個(gè)非葉節(jié)點(diǎn)(含有子節(jié)點(diǎn)的節(jié)點(diǎn)),//從后往前調(diào)整每一個(gè)非葉子節(jié)點(diǎn)的順序,使堆變?yōu)榇箜敹裦or (let i = Math.floor(len / 2); i >= 0; i--) {heapify(arr, i, len);}
}
// 調(diào)整堆
function heapify(arr, i, len) {let left = 2 * i + 1,right = 2 * i + 2,largest = i;//通過2次判斷選出父節(jié)點(diǎn)和2個(gè)子節(jié)點(diǎn)中最大的一個(gè)if (left < len && arr[left] > arr[largest]) {largest = left;}if (right < len && arr[right] > arr[largest]) {largest = right;}//將最大的節(jié)點(diǎn)和父節(jié)點(diǎn)交換順序if (largest != i) {swap(arr, i, largest);//遞歸對(duì)換順序的節(jié)點(diǎn)進(jìn)行調(diào)整,使其還是大頂堆heapify(arr, largest, len);}
}
function swap(arr, i, j) {var temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
function heapSort(arr) {// 建立大頂堆buildMaxHeap(arr);let len = arr.length//每次建立一個(gè)大頂堆,將最大的元素(堆頂元素)放入尾部達(dá)到排序效果for (let i = len - 1; i > 0; i--) {swap(arr, 0, i);len--;heapify(arr, 0, len);}return arr;
}
console.log(heapSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))
特點(diǎn)
插入排序?
直接插入排序
每次從無序表中取出第一個(gè)元素,把它插入到有序表的合適位置,使有序表仍然有序(默認(rèn)是從后向前比較)。
圖解
藍(lán)色部分為無序表,黃色部分為有序表:
js實(shí)現(xiàn)?
使用的是一個(gè)數(shù)組,將數(shù)組下標(biāo)為i之前的為有序,之后為無序。
?注意用的是改變?cè)瓟?shù)組的方法,可以不用返回。
//將數(shù)組下標(biāo)為的x元素取出插入為y的 function popInsert(arr,x,y){arr.splice(y,0,...arr.splice(x,1)) } function insertSort(arr){let t=arr.lengthfor (let i=1;i<t;i++){let j=i-1do{if(arr[j]<arr[i]){popInsert(arr,i,j+1)break}j--//比較到下標(biāo)為0的時(shí)候直接插入if(j<0){popInsert(arr,i,0)}}while(j>=0)}return arr } console.log(insertSort([3,1,2,3134,113,342,123412,55,111,4,234,5,5]))
特點(diǎn)
希爾排序
希爾排序在直接排序之前,進(jìn)行預(yù)排列,將某些極端數(shù)據(jù)更快的排列到數(shù)列前面,構(gòu)成一個(gè)接近排列好的序列,最后再進(jìn)行一次直接插入排序。
預(yù)排列的原理也是插入排列,只不過這里的將數(shù)組分成了gap組,分別對(duì)每一個(gè)小組進(jìn)行插入排序。
圖解
對(duì)于升序,當(dāng)gap從5 – 2 – 1的過程中,排在后面的數(shù)值小的數(shù)能更快排到前面,當(dāng)gap為1的時(shí)候?qū)嶋H上就是進(jìn)行了一次插入排序
js實(shí)現(xiàn)
每次將下標(biāo)位置差為gap的數(shù)進(jìn)行直接排序,其中第一次gap=Math.floor(arr.length/2),之后每次gap=Math.floor(gap/2)。
function shellsSort(arr){let t=arr.lengthlet gap=tlet tmp//gap取不同值的排序do{//每次的分組gap=Math.floor(gap/2)for(let i=gap;i<t;i++){//只和上一個(gè)間距為gap的元素進(jìn)行比較,判斷是否要插入,還是維持原順序if(arr[i-gap]>arr[i]){tmp = arr[i]let j=i-gap//將插入位置之后的元素整體后移do{arr[j+gap]=arr[j]j-=gap}while(j>=0&&arr[j]>tmp)//插入對(duì)應(yīng)位置arr[j+gap]=tmp}}}while(gap>1)return arr } console.log(shellsSort([3,1,2,3134,113,342,123412,55,111,4,234,5,5]))
特點(diǎn)
其它排序
桶排序
選出待排序元素中的最大最小值,根據(jù)最大最小值劃分多個(gè)區(qū)域(桶),通過映射函數(shù)將元素分到不同區(qū)域(桶)中,對(duì)區(qū)域(桶)中的元素進(jìn)行排序后,依次取出即可。
圖解
將排序的元素放入對(duì)應(yīng)的桶中
?對(duì)桶中的元素進(jìn)行排序
js實(shí)現(xiàn)
//插入排序
function insertionSort(arr) {let len = arr.length;let preIndex, current;for (let i = 1; i < len; i++) {preIndex = i - 1;current = arr[i];while (preIndex >= 0 && arr[preIndex] > current) {arr[preIndex + 1] = arr[preIndex];preIndex--;}arr[preIndex + 1] = current;}return arr;
}
function bucketSort(arr, bucketSize) {let minValue = arr[0];let maxValue = arr[0];//找到數(shù)組中的最大和最小值for (let i = 1; i < arr.length; i++) {if (arr[i] < minValue) {minValue = arr[i];} else if (arr[i] > maxValue) {maxValue = arr[i];}}//桶的初始化let DEFAULT_BUCKET_SIZE = 5;// 設(shè)置一個(gè)桶能存儲(chǔ)的默認(rèn)數(shù)量為5bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;//桶的個(gè)數(shù)let bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;let buckets = new Array(bucketCount);for (let i = 0; i < buckets.length; i++) {buckets[i] = [];}//利用映射函數(shù)將元素分配到各個(gè)桶中for (let i = 0; i < arr.length; i++) {buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);}//刪除arr數(shù)組中的所有元素arr.length = 0;for (let i = 0; i < buckets.length; i++) {// 對(duì)每個(gè)桶進(jìn)行排序,這里使用了插入排序insertionSort(buckets[i]);// 將每個(gè)桶的數(shù)據(jù)從小到大拿出 for (let j = 0; j < buckets[i].length; j++) {arr.push(buckets[i][j]);}}return arr;
}
console.log(bucketSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))
特點(diǎn)
基數(shù)排序
確定最大數(shù)的位數(shù),先按第一位進(jìn)行排序,在按第二位進(jìn)行排序,直到按最后一位進(jìn)行排序?yàn)橹埂?/p>
圖解
js實(shí)現(xiàn)
function radixSort(arr) {let counter = [];//獲取元素的最大位數(shù)let maxDigit = `${Math.max(...arr)}`.lengthlet mod = 10;let dev = 1;//從小到大依次對(duì)元素的每一位進(jìn)行循環(huán)排序for (let i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {for (let j = 0; j < arr.length; j++) {//獲取元素對(duì)應(yīng)位上的數(shù)值let bucket = parseInt((arr[j] % mod) / dev);if (counter[bucket] == null) {counter[bucket] = [];}//根據(jù)對(duì)應(yīng)的數(shù)值,將元素放入對(duì)應(yīng)的桶中counter[bucket].push(arr[j]);}let pos = 0;//從0~9的桶中將元素取出,完成一次排序for (let j = 0; j < counter.length; j++) {let value = null;if (counter[j] != null) {while ((value = counter[j].shift()) != null) {arr[pos++] = value;}}}}return arr;
}
console.log(radixSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5], 6))
特點(diǎn)
歸并排序
申請(qǐng)空間,使其大小為兩個(gè)已經(jīng)排序序列之和,該空間用來存放合并后的序列;
設(shè)定兩個(gè)指針,最初位置分別為兩個(gè)已經(jīng)排序序列的起始位置;
比較兩個(gè)指針?biāo)赶虻脑?#xff0c;選擇相對(duì)小的元素放入到合并空間,并移動(dòng)指針到下一位置;
重復(fù)步驟 3 直到某一指針達(dá)到序列尾;
將另一序列剩下的所有元素直接復(fù)制到合并序列尾。
圖解
?js實(shí)現(xiàn)
function mergeSort(arr) { // 采用自上而下的遞歸方法let len = arr.length;if (len < 2) {return arr;}let middle = Math.floor(len / 2),left = arr.slice(0, middle),right = arr.slice(middle);return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {let result = [];while (left.length && right.length) {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;
}
console.log(mergeSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5], 6))
特點(diǎn)
計(jì)數(shù)排序
1.找出待排序的數(shù)組中最大和最小的元素
2.統(tǒng)計(jì)數(shù)組中每個(gè)值為i的元素出現(xiàn)的次數(shù),存入數(shù)組C的第i項(xiàng)
3.對(duì)所有的計(jì)數(shù)累加(從C中的第一個(gè)元素開始,每一項(xiàng)和前一項(xiàng)相加)
4.反向填充目標(biāo)數(shù)組:將每個(gè)元素i放在新數(shù)組的第C(i)項(xiàng),每放一個(gè)元素就將C(i)減去1
圖解
?js實(shí)現(xiàn)
function countingSort(arr) {let maxValue = Math.max(...arr)let bucket = new Array(maxValue + 1),sortedIndex = 0;arrLen = arr.length,bucketLen = maxValue + 1;for (let i = 0; i < arrLen; i++) {if (!bucket[arr[i]]) {bucket[arr[i]] = 0;}bucket[arr[i]]++;}for (let j = 0; j < bucketLen; j++) {while (bucket[j] > 0) {arr[sortedIndex++] = j;bucket[j]--;}}return arr;
}
console.log(countingSort([3, 1, 2, 3134, 113, 342, 123412, 55, 111, 4, 234, 5, 5]))
特點(diǎn)
---------------------
作者:YF-SOD
來源:CSDN
原文:https://blog.csdn.net/AIWWY/article/details/121346132
版權(quán)聲明:本文為作者原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!
內(nèi)容解析By:CSDN,CNBLOG博客文章一鍵轉(zhuǎn)載插件
總結(jié)
以上是生活随笔為你收集整理的十大排序总结(js实现、稳定性、内外部排序区别、时间空间复杂度、冒泡、快速、直接选择、堆、直接插入、希尔、桶、基数、归并、计数排序)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win11 的日历 替代
- 下一篇: [转]Docker 大势已去,Podma