快速排序
递归思路——以x为基准,比他小的排到他前面,比他大的排到他后面。
这样x位置就永远定下来了!因为他前面的全是比他小的,后面全是比他大的。那前面那些数字自己在排序,后面的也自己在排序。x就永远不动了!
那左边和右边的数组也像这样选个基准排序。

//对一个数组进行快排
let quickSort = arr => {
if (arr.length <= 1) { return arr; } //如果数组就一个元素,就不用快排了
let pivotIndex = Math.floor(arr.length / 2); //把数组中最中间(或中间偏左)的那个元素作为基准
let pivot = arr.splice(pivotIndex,1)[0]; //从数组中删去基准元素,得到返回值就是基准元素
let left =[]; //定义一个空数组left
let right = []; //定义一个空数组right
for (let i = 0;i < arr.length; i++) { //遍历除去了基准元素的那个数组,比基准元素小的就放到left,比基准元素大的就放到right
if (arr[i] < pivot) {left.push(arr[i])} else { right.push(arr[i]) }
}
//最后返回一个数组,这个数组:对left快排得到的数组+基准元素+对right快排得到的数组
return quickSort(left).concat(
[pivot], quickSort(right))
}
归并排序(merge)
我只会做一件事情:把两个排好序的数组合并merge成一个新的排好序的数组。(先别管我是怎么做的)
那数组怎么才算排好序?一个元素组成的数组不就算这个数组排好序了?
把一个数组分为左右两边数组
所以把一个数组一直分左右两个数组,再分左右两个数组,直到变成一个元素组成的数组。好啦,就可以开始合并了。把一个元素的数组和另一个一个元素的数组合并,在和其他合并合并合并完事

//给数组arr排序
let mergeSort = arr => {
//如果数组中就一个元素就算排好序了,直接返回该数组
if (arr.length === 1) {return arr;}
//数组中有两个及以上元素,那就把这些元素分左右两个数组
let left = arr.slice(0, Math.floor(arr.length / 2));
let right = arr.slice(Math.floor(arr.length / 2));
//再把左右两个数组再分为
return merge(mergeSort(left), mergeSort(right));
};
如何把两个排好序的数组合并merge成一个新的排好序的数组?
let merge = (a, b) => {
if (a.length === 0) return b;
if (b.length === 0) return a;
return a[0] > b[0]
? [b[0]].concat(merge(a, b.slice(1)))
: [a[0]].concat(merge(a.slice(1), b));
};

计数排序
- 只需遍历一次数组生成一个哈希表,让数组中的元素成为key,元素出现的次数成为value。(这叫做用空间换时间)
- 还要记下数组中的最大值
- 最后从0到最大值,看看哈希表里面有没有这个数字对应的key。有就推到新数组里。

let countSort = arr => {
let hashTable = {}, max = 0, result = [];
//遍历数组,生成一个哈希表
for (let i = 0; i < arr.length; i++) {
if (!(arr[i] in hashTable)) {
hashTable[arr[i]] = 1;
} else {
hashTable[arr[i]] += 1;
}
if (arr[i] > max) {
max = arr[i];
}
}
//遍历0到最大数,看哈希表里面有没有这个key
for (let j = 0; j <= max; j++) {
if (j in hashTable) {
for (let i = 0; i < hashTable[j]; i++) {
result.push(j);
}
}
}
return result;
};
时间复杂度对比
- 选择排序O(n^2)
- 快速排序O(n log2 n)
- 归并排序O(n log2 n)
- 计数排序O(n + max) 时间快但同时内存也多了
冒泡排序
- 请看生动的例子
- 每次都从第一个数开始看,如果第一个数比后面的大就换位置,没后面的大就后面的开始和后面的这样比。
- 每次都从第一个开始。相邻的比较,大的在前就前后交换位置。
bubbleSortSoul1 = (arr = []) => {
let count = 0;
// i为轮数(因i从0开始 即i<arr.length-1)
for (let i = 0; i < arr.length - 1; i++) {
count++;
// 第i轮仅需比较length-1-i次
for (let j = 0; j < arr.length - 1 - i; j++) {
// 这里能不能写成arr[j-1]>arr[j]? 如果有这种特殊癖好 那么j就从1开始吧,然后j<arr.length-i
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
console.log(`bubbleSortSoul1排序完成用了${count}轮`);
return arr;
}
插入排序
- 请看生动的例子点INS
- 跟打牌一样。每次抽到一张牌就把它放到手中已经排好的牌里的正确的位置
- 从已经排好的牌后面往前看,直到新抽的牌比他小(前面的比他小,后面的比他大)
基数排序
适合多位数