一、冒泡排序
排序思想:列表每兩個相鄰的數(shù)進行比較,如果前面的數(shù)比后面的數(shù)大,則交換這兩個數(shù),一輪排序完成后,則無序區(qū)減少一個數(shù),有序區(qū)增加一個數(shù)。
function sort(list) {
for (let i= 0; len = list.length-1; i<len-1; i++) {
let exchange = false; // 用來標記在一輪冒泡過程中有無交換過
for (let j= 0; j<len-i; j++) {
if (list[j] > list[j+1]) {
// 交換兩個數(shù)
const m = list[j];
list[j] = list[j+1];
list[j+1] = m;
exchange = true;
}
}
// 如果在一輪冒泡過程中沒有交換過,說明此時的列表已經是排序好的了,直接結束循環(huán)
if (!exchange) {
return;
}
}
}
二、選擇排序
排序思想:剛開始將整個數(shù)組看作一個無序區(qū),每一輪拿無序區(qū)的第一個數(shù)與無序區(qū)其他數(shù)值依次進行比較,遇到更小的數(shù)則交換,每一輪排序完成后有序區(qū)增加一個數(shù),無序區(qū)減少一個數(shù)。
function sort (list) {
for (let i=0,len=list.length; i<len-1; i++) {
for (let j=i+1; j<len; j++) {
if (list[i] > list[j]) {
// 交換兩個數(shù)
const m = list[i];
list[i] = list[j];
list[j] = m;
}
}
}
}
三、插入排序
排序思想:剛開始將整個數(shù)組看作一個無序區(qū),每一輪拿無序區(qū)的第一數(shù)與有序區(qū)的數(shù)從后往前依次進行比較,遇到更大的數(shù)則交換,每一輪排序完成后,有序區(qū)增加一個數(shù),無序區(qū)減少一個數(shù)。
function sort(list) {
for (let i=1,len=list.length; i<len; i++) {
for (let j=i-1; j>=0; j--) {
if (list[j+1] < list[j]) {
// 交換兩個數(shù)
const m = list[j+1];
list[j+1] = list[j];
list[j] = m;
}
}
}
}
四、快速排序
排序思想:取一個元素p(第一個數(shù)),使元素p歸位,此時列表會被p分成兩部分,左邊都比p小,右邊都比p大,然后左邊和右邊再分別進行遞歸使元素歸位,最終完成排序。
function sort(list, left, right) {
if (left < right) {
const mid = homing(list, left, right);
sort(list, left, mid-1);
sort(list, mid+1, right);
}
}
/**
* 歸位函數(shù)
* 首先將第一個元素緩存起來,左指針剛開始指向第一個元素
* 然后右指針依次往左走,直到找到比緩存起來的元素小的元素,將該元素賦值給當前左指針指向的元素
* 然后左指針依次往右走,直到找到比緩存起來的元素大的元素,將該元素賦值給當前右指針指向的元素
* 重復以上操作,當左指針和右指針重合時,便找到了要歸位的地方,將緩存起來的元素賦值給左指針指向的元素,完成歸位
*/
function homing(li, left, right) {
const tmp = li[left];
while (left < right) {
while (left < right && li[right] >= tmp) {
right--;
}
li[left] = li[right];
while(left < right && li[left] <= tmp) {
left++;
}
li[right] = li[left];
}
li[left] = tmp;
return left;
}
五、歸并排序
排序思想:假設有兩個排序好的數(shù)組,如何將他們合并成一個排序好的數(shù)組,首先創(chuàng)建一個空的新數(shù)組,將兩個有序的數(shù)組依次進行比較,每次將較小的數(shù)放到新數(shù)組中,最終得到一個排序好的新數(shù)組,此過程稱為歸并。數(shù)組為空或者只有一個元素時就是有序的,就可以進行歸并,歸并排序就是先兩兩進行歸并,再利用遞歸,將歸并后的兩個數(shù)組再進行歸并,最終得到排序好的數(shù)組。
function sort(list) {
if (list && list.length > 1) {
const mid = Math.floor(list.length / 2);
const left = list.slice(0, mid);
const right = list.slice(mid);
return merge(sort(left), sort(right));
}
return list;
}
/**
* 歸并函數(shù)
* 首先創(chuàng)建一個空的新數(shù)組
* 將兩個有序數(shù)組中的元素依次進行比較,每次將較小的數(shù)放到新數(shù)組中
* 最終得到一個排序好的新數(shù)組
*/
function merge(leftList, rightList) {
const newList = [];
const leftLength = leftList && leftList.length;
const rightLength = rightList && rightList.length;
let i = 0;
let j = 0;
while (i < leftLength && j < rightLength) {
if (leftList[i] < rightList[j]) {
newList.push(leftList[i++]);
} else {
newList.push(rightList[j++]);
}
}
while (i < leftLength) {
newList.push(leftList[i++]);
}
while (j < rightLength) {
newList.push(rightList[j++]);
}
return newList;
}
六、堆排序
排序思想:二叉樹的存存儲方式分為鏈式存儲和順序存儲,這里堆排序使用順序存儲。堆是一種特殊的完全二叉樹,堆分為大根堆和小根堆,滿足任一節(jié)點都比其孩子節(jié)點大的一個完全二叉樹就是大根堆,滿足任一節(jié)點都比其孩子節(jié)點小的一個完全二叉樹就是小根堆,這里堆排序使用大根堆。假設根節(jié)點的左右子樹都是堆,但是根節(jié)點不滿足堆的性質,可以通過一次向下調整來將其變成一個堆,這就是堆的向下調整性質。堆排序的排序過程是,首先構造一個大根堆(此時整個堆是無序區(qū)),然后將堆頂?shù)脑厝〕龇诺接行騾^(qū)(也就是數(shù)組的最后),然后將堆的最后一個元素(也就是無序區(qū)的最后一個元素)放到堆頂,堆就少了一個元素,此時通過一次向下調整重新使堆有序,調整后的堆頂就是整個數(shù)組的第二大元素,然后重復之前的操作依次將元素放到有序區(qū),直到堆變空,便可得到排序好的數(shù)組。
function sort(list) {
if (list && list.length > 1) {
const len = list.length;
// 首先構造大根堆,從最后一個不是葉子節(jié)點的節(jié)點開始遍歷,從后往前依次進行向下調整
for (let i = Math.floor((len-2)/2); i>=0; i--) {
sift(list, i, len-1);
}
// 然后將堆的第一元素與有序區(qū)的第一個元素進行交換,此時有序區(qū)增加一個,無序區(qū)減少一個,再進行一次堆的向下調整,然后重復上述操作,最終使整個數(shù)組有序
for(let i = len-1; i>=0; i--){
const m = list[0];
list[0] = list[i];
list[i] = m;
sift(list, 0, i-1);
}
}
}
/**
* 堆的向下調整
* 先從根節(jié)點開始,如果孩子節(jié)點比父節(jié)點大,則將該孩子節(jié)點賦值給父節(jié)點
* 然后指針指向下一層,重復上面的操作,直到找到孩子節(jié)點沒有比父節(jié)點大的節(jié)點,終止循環(huán)
* 最后將原始的根節(jié)點賦值給當前父節(jié)點
*/
function sift(li, low, high) {
const tmp = li[low]; // 緩存根節(jié)點
let i = low; // 當前的父節(jié)點,最開始指向根節(jié)點
let j = i*2+1; // 當前的孩子節(jié)點,最開始指向根節(jié)點的左孩子節(jié)點
while (j <= high) {
// 如果有右孩子節(jié)點且比左孩子節(jié)點大,則j指向右孩子節(jié)點
if (j+1 <= high && li[j+1] > li[j]) {
j++;
}
if (li[j] > tmp) {
li[i] = li[j]; // 將較大的孩子節(jié)點賦值給父節(jié)點
i = j; // i指向下一層
j = i*2 +1;
} else {
break; // 如果當前子節(jié)點沒有比原始根節(jié)點大,結束循環(huán)
}
}
li[i] = tmp; // 最后將原始的根節(jié)點賦值給當前父節(jié)點
}
總結
更多個人文章