前端十大排序演算法
阿新 • • 發佈:2018-12-03
一、插入排序
<script> /* 原理:通過構建有序序列,對於未排序資料,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,通常採用inplace排序(即只需用到O(1)的額外空間排序),因而在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,為最新元素提供插入空間。 步驟: 1.從第一個元素開始,該元素可以認為已經被排序; 2.取出下一個元素,在已經排序的元素序列中從後向前掃描; 3.如果該元素(已排序)大於新元素,將該元素移到下一位置; 4.重複步驟3,直到找到已排序的元素小於或者等於新元素的位置; 5.將新元素插入到該位置後; 6.重複步驟2-5 */ var arr=[3,1,5,4,2,9,8,6]; function insertionSort(array){ if(Object.prototype.toString.call(array).slice(8,-1)==='Array'){ for(var i=1;i<array.length;i++){ var key=arr[i];//新元素 var j=i-1;//前一位的位置 while(j>=0&&array[j]>key){//該元素大於新元素 array[j+1]=array[j];//該元素向後挪一位 j--;//換前一個去進行比較 } array[j+1]=key;//將新元素插入到對應的位置 } return array; }else{ return 'array is not an Array'; } } console.log(insertionSort(arr)) /* 改進插入排序:查詢插入位置時使用二分法查詢的方式 */ function binaryinsertionSort(array){ if(Object.prototype.toString.call(array).slice(8,-1)==='Array'){ for(var i=1;i<array.length;i++){ var key=array[i],left=0,right=i-1;//left,right表示位置 while(left<=right){ var middle=parseInt((left+right)/2);//找中間位置 if(key<array[middle]){ right=middle-1; }else{ left=middle+1;//left的位置就是我們想要找的位置 } } for(var j=i-1;j>=left;j--){ array[j-1]=array[j];//left後面的所有都往後面挪一位 } array[left]=key;//把新元素插入 } return array; }else{ return 'array is not an Array'; } } console.log(binaryinsertionSort(arr)) </script>
效果演示:
二、快速排序
/* 原理:通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄進行排序,已達到整個序列有序。 步驟: 快速排序使用分治法來把一個串分為兩個子串 1.從數列中挑出一個元素,稱為“基準”; 2.重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分割槽退出之後,該基準就處於數列的中間位置。這個稱為分割槽操作; 3.遞迴地把小於基準值元素的子數列和大於基準值元素的子數列排序。 */ var arr=[3,1,5,4,2,9,8,6]; function quickSort(array,left,right){ if(Object.prototype.toString.call(array).slice(8,-1)==='Array'&& typeof left==='Number' && typeof right==='Number'){ if(left<right){ var x=array[right],i=left-1,temp; for(var j=left;j<right;j++){ if(array[j]<=x){ i++; temp=array[i]; array[i]=array[j]; array[j]=temp; } } quickSort(array,left,i-1); quickSort(array,i+1,right); } return array; }else{ return 'array is not an Array or left or right is not a number' } } function quickSort2(arr){ if(arr.length<=1){ return arr; } var pivotIndex=Math.floor(arr.length/2); var pivot=arr.splice(pivotIndex,1)[0]; var left=[]; var right=[]; for(var i=0;i<arr.length;i++){ if(arr[i]<pivot){ left.push(arr[i]); }else{ right.push(arr[i]); } } return quickSort2(left).concat([pivot],quickSort2(right)); } console.log(quickSort2(arr)) </script>
效果演示:
三、選擇排序
<script> /* 原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。 步驟: 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趟結束,陣列有序化了。 */ var arr=[3,1,5,4,2,9,8,6]; function selectionSort(arr){ var len=arr.length; var minIndex,temp; for(var i=0;i<len-1;i++){ minIndex=i; for(var j=i+1;j<len;j++){ if(arr[j]<arr[minIndex]){//尋找最小的數 minIndex=j;//將最小數的索引儲存,方便下面使用 } } temp=arr[i];//交換位置 arr[i]=arr[minIndex]; arr[minIndex]=temp; } return arr; } console.log(selectionSort(arr)); </script>
效果演示:
四、氣泡排序
<script>
/*
原理:
1.比較相鄰的元素,如果第一個比第二個大,就交換它們兩個;
2.對每一對相鄰的元素做同樣的工作,從開始第一對到結尾最後一對,這樣在最後的元素應該會是最大的數;
3.針對所有的元素重複以後以上的步驟,除了最後一個
4.重複步驟1-3,直到排序完成
*/
var arr=[3,1,5,4,2,9,8,6];
function bubbleSort(arr){
var len=arr.length;
for(var i=0;i<len;i++){
for(var j=0;j<len-1-i;j++){
if(arr[j]>arr[j+1]){//相鄰兩個元素進行比較
var temp=arr[j+1];//前後交換位置,大的到後面
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
/*
改進一:
設定一標誌性變數pos,用於記錄每趟排序中最後一次進行交換的位。由於pos位置之後的記錄均已交換到位,故在進行下一趟排序時只要掃描到pos位置即可。
*/
function bubbleSort2(arr){
var i=arr.length-1;//初始時,最後位置保持不變
while(i>0){
var pos=0;//每趟開始時無記錄交換
for(var j=0;j<i;j++){
if(arr[j]>arr[j+1]){
pos=j;//記錄交換的位置
var temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
i=pos;//為下一趟排序做準備
}
return arr;
}
console.log(bubbleSort2(arr));
/*
改進二:
傳統氣泡排序每一趟排序操作只能找到一個最大值或最小值,我們考慮利用在每趟排序中進行正向和反向兩遍冒泡的方法,一次可以得到兩個最終值(最大值和最小值),從而使排序趟數幾乎減少一半。
*/
function bubbleSort3(arr){
var low=0;
var high=arr.length-1;//設定變數的初始值
var tmp,j;
while(low<high){
for(j=low;j<high;++j)//正向冒泡找到最大值
if(arr[j]>arr[j+1]){
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
--high;//修改high值,前移一位
for(j=high;j>low;--j)//反向冒泡找到最小者
if(arr[j]<arr[j-1]){
tmp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=tmp;
}
++low;
}
return arr;
}
console.log(bubbleSort3(arr))
</script>
效果演示:
五、計數排序
<script>
/*
介紹:計數排序(Counting sort)是一種穩定的排序演算法。計數排序使用一個額外的陣列C,其中第i個元素是待排序陣列A中值等於i的元素的個數。然後根據陣列C來將A中的元素排到正確的位置。它只能對整數進行排序。
步驟:
1.找出待排序的陣列中最大和最小的元素;
2.統計陣列中每個值為i的元素出現的次數,存入陣列C的第i項;
3.對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加);
4.反向填充目標陣列:將每個元素i放在新陣列的第C(i)項,每放一個元素就將C(i)減去1。
*/
function countingSort(array){
var len=array.length,
B=[],
C=[],
min=max=array[0];
for(var i=0;i<len;i++){
min=min<=array[i]?min:array[i];
max=max>=array[i]?max:array[i];
C[array[i]]=C[array[i]]?C[array[i]]+1:1;
}
for(var j=min;j<max;j++){
C[j+1]=(C[j+1]||0)+(C[j]||0);
}
for(var k=len-1;k>=0;k--){
B[C[array[k]]-1]=array[k];
C[array[k]]--;
}
return B;
}
var arr=[3,1,5,4,2,9,8,6];
console.log(countingSort(arr))
</script>
效果演示:
六、堆排序
/*
介紹:堆排序(Heapsort)是指利用對這種資料結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子節點的鍵值或索引總是小於(或者大於)它的父節點。
步驟:
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,則整個排序過程完成。
*/
/*
堆排序
array待排序陣列
*/
function heapSort(array){
if(Object.prototype.toString.call(array).slice(8,-1)==='Array'){
//建堆
var heapSize=array.length,temp;
for(var i=Math.floor(heapSize/2)-1;i>=0;i--){
heapify(array,i,heapSize);
}
//堆排序
for(var j=heapSize-1;j>=1;j--){
temp=array[0];
array[0]=array[j];
array[j]=temp;
heapify(array,0,--heapSize);
}
return array;
}else{
return 'array is not an Array!'
}
}
/*
維護堆的性質
arr陣列
x 陣列下標
len 堆大小
*/
function heapify(arr,x,len){
if(Object.prototype.toString.call(arr).slice(8,-1)==='Array'&&typeof x ==='number'){
var l=2*x+1,r=2*x+2,largest=x,temp;
if(l<len&&arr[l]>arr[largest]){
largest=l;
}
if(r<len&&arr[r]>arr[largest]){
largest=r;
}
if(largest!=x){
temp=arr[x];
arr[x]=arr[largest];
arr[largest]=temp;
heapify(arr,largest,len);
}
}else{
return 'arr is not an Array or x is not a number !'
}
}
var arr=[3,1,5,4,2,9,8,6];
console.log(heapSort(arr))
</script>
效果演示:
七、桶排序
<script>
/*
原理:假設輸入資料服從均勻分佈,將資料分到有限數量的桶裡,每個桶再分別排序(有可能再使用別的排序演算法或是以遞迴方式繼續使用桶排序進行排)
步驟:
1.設定一個定量的陣列當作空桶;
2.遍歷輸入資料,並且把資料一個一個放到對應的桶裡去;
3.對每個不是空的桶進行排序;
4.從不是空的桶裡把排好序的資料拼接起來。
*/
/*
桶排序
array 陣列
num 桶的數量
*/
function bucketSort(array,num){
if(array.length<=1){
return array;
}
var len=array.length,buckets=[],result=[],min=max=array[0],regex='/^[1-9]+[0-9]*$',space,n=0;
num=num||((num>1&®ex.test(num))?num:10);
for(var i=1;i<len;i++){
min=min<=array[i]?min:array[i];
max=max>=array[i]?max:array[i];
}
space=(max-min+1)/num;
for(var j=0;j<len;j++){
var index=Math.floor((array[j]-min)/space);
if(buckets[index]){
var k=buckets[index].length-1;
while(k>=0&&buckets[index][k]>array[j]){
buckets[index][k+1]=buckets[index][k];
k--;
}
buckets[index][k+1]=array[j];
}else{
buckets[index]=[];
buckets[index].push(array[j]);
}
}
while(n<num){
result=result.concat(buckets[n]);
n++;
}
return result;
}
var arr=[3,1,5,4,2,9,8,6];
console.log(bucketSort(arr,4))
</script>
效果演示:
八、基數排序
<script>
/*
介紹:基數排序是按照低位先排序,然後收集;再按照高位排序,然後再收集;依次類推,直到最高位。有時候有些屬性是有優先順序順序的,先按低優先順序排序,再按高優先順序排序。最後的次序就是高優先順序高的在前,高優先順序相同的低優先順序高的在前。基數排序基於分別排序,分別收集,所以是穩定的。
步驟:
1.取得陣列中的最大數,並取得位數;
2.arr為原始陣列,從最低位開始取每個位組成radix;
3.對radix進行計數排序(利用計數排序適用於小範圍數的特點)
*/
/*
基數排序適用於:
1.資料範圍較小,建議在小於1000;
2.每個數值都要大於等於0
arr待排序陣列
maxDight最大位數
*/
function radixSort(arr,maxDight){
var mod=10;
var dev=1;
var counter=[];
for(var i=0;i<maxDight;i++,dev*=10,mod*=10){
for(var j=0;j<arr.length;j++){
var bucket=parseInt((arr[j]%mod)/dev);
if(counter[bucket]==null){
counter[bucket]=[];
}
counter[bucket].push(arr[j]);
}
var pos=0;
for(var j=0;j<counter.length;j++){
var value=null;
if(counter[j]!=null){
while((value=counter[j].shift())!=null){
arr[pos++]=value;
}
}
}
}
return arr;
}
var arr=[3,1,5,4,2,9,8,6];
console.log(radixSort(arr,1))
</script>
效果演示:
九、歸併排序
<script>
/*
介紹:歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法的一個非常典型的應用。歸併排序是一種穩定的排序方法。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若有兩個有序表合併成一個有序表,稱為2-路歸併。
步驟:
1.把長度為n的輸入序列分成兩個長度為n/2的子序列;
2.對這兩個子序列分別採用歸併排序;
3.將兩個排序好的子序列合併成一個最終的排序序列。
*/
var arr=[3,1,5,4,2,9,8,6];
function mergeSort(arr){//採用自上而下的遞迴方法
var len=arr.length;
if(len<2){
return arr;
}
var middle=Math.floor(len/2),
left=arr.slice(0,middle),
right=arr.slice(middle);
return merge(mergeSort(left),mergeSort(right));
}
function merge(left,right){
var 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(arr))
</script>
效果演示:
十、希爾排序
/*
原理:是簡單插入排序的改進版;它與插入排序的不同之處在於,它會優先比較距離較遠的元素。希爾排序又叫縮小增量排序。
步驟:
先將整個待排序的記錄序列分割成為若干個子序列分別進行直接插入排序。
1.選擇一個增量序列t1,t2,...,tk,其中ti>tj,tk=1;
2.按增量序列個數k,對序列進行k趟排序;
3.每趟排序,根據對應的增量ti,將待排序列分割成若干個長度為m的子序列,分別對哥哥子表進行直接插入排序。僅增量因子為1時,整個序列作為一個表來處理,表長度即為整個序列的長度。
*/
var arr=[3,1,5,4,2,9,8,6];
function shellSort(arr){
var len=arr.length,
temp,
gap=1;
while(gap<len/5){//動態定義間隔序列
gap=gap*5+1;
}
for(gap;gap>0;gap=Math.floor(gap/5)){
for(var i=gap;i<len;i++){
temp=arr[i];
for(var j=i-gap;j>=0&&arr[j]>temp;j-=gap){
arr[j+gap]=arr[j];
}
arr[j+gap]=temp;
}
}
return arr;
}
console.log(shellSort(arr))
</script>
效果演示: