排序算法
1、桶排序
原理m 桶排序我个人认为是利用数组下标有序完成的排序,newList的key是需要排序的数,value是重复的个数。 优点 时间复杂度只有O(m+n),计算效率高 缺点: 空间消耗比较大
/** * params {number[]} list
* return {number[]} */
function sort(list) {
let newList=[];
list.forEach(el => newList[el]===undefined?newList[el]=0:newList[el] += 1);
return newList.reduce((pre, el, index) => {
for(let i = el; i>=0; i--) {
pre.push(index)
}
return pre;
}, [])
}
1.1延申
思路
其实通过堆排序可以知道利用下标是可以排序的,因此考虑到是不是利用对象的key也是也是可以的排序的,对象的好处是对象的长度就是真实的长度,而数组的length不一定是真的。
优点
时间复杂度只有O(m+n),计算效率高,空间消耗不大
缺点:
需要数据类型的转换
Object 的 key 只有在小于2^32 - 1时才会排序。
function sort(list) {
let obj={};
list.forEach((_)=>{obj[_]===undefined?obj[_]=0:obj[_]+=1})
let result=[];
for(let key in obj){
for(let i=0;i<=obj[key];i++){
result.push(+key)
}
}
return result
}
缺点证明:
let obj = {
"4294967296": 1,
"4294967295": 1,
"4294967299": 1,
"4294967298": 1,
"1": 1,
"2": 1,
"3": 1
};
显示如下很明显 4294967296 4294967295 顺序有问题
图片: uploader.shimo.im/f/PLjBEosBS…
2、插入排序
原理:
局部有序。数组的从前往后是慢慢有序的,首次的时候吧数组的第二个拿出来第一个认为是局部有序,以此类推
插入排序最多次数1+2+3+...+N-1=N*(N-1)/2 但是插入排序并不是比较所有的平均下来是N*(N-1)/4 这样比冒泡是高的
ArrayList.prototype.insertionSort = function () {
var length = this.array.length
for (var i = 1; i < length; i++) {
var j = i
var temp = this.array[i]
while (j > 0 && this.array[j-1] > temp) {
this.array[j] = this.array[j-1]
j--
}
this.array[j] = temp
}
}
3、插入排序升级-希尔排序
原理
希尔排序核心思想也是局部有序减少移动的次数,gap增量来控制,只是利用分组排序的,其实插入排序可以理解为分组gap是1的希尔排序。
希尔原稿中建议是N/2 .
Hibbard增量序列
增量是2^k-1 基数增量 1,3,5,、、、
最坏的O(N^3/2)平均复杂度O(N^5/4)
sedgewick增量序列
直接给出序列{1,5,9,41,109,、、}
最坏的O(N^4/3)平均复杂度O(N^7/6)
ArrayList.prototype.shellSort = function () {
var length = this.array.length
var gap = Math.floor(length / 2)
while (gap > 0) {
for (var i = gap; i < length; i++) {
var j = i
var temp = this.array[i]
while (j > gap - 1 && this.array[j - gap] > temp) {
this.array[j] = this.array[j - gap]
j -= gap
}
this.array[j] = temp
}
gap = Math.floor(gap / 2)
}
}
3、冒泡排序
原理
针对未排序的元素从头到尾一次和自己相邻的元素进行比较大小关系,大的逐渐排到最后。
缺点:
时间复杂度O(n^2),计算慢。
ArrayList.prototype.bubbleSort = function () {
// 1.获取数组的长度
var length = this.array.length
// 2.反向循环, 因此次数越来越少
for (var i = length - 1; i >= 0; i--) {
// 3.根据i的次数, 比较循环到i位置
for (var j = 0; j < i; j++) {
// 4.如果j位置比j+1位置的数据大, 那么就交换
if (this.array[j] > this.array[j+1]) {
// 交换
[this.array[j],this.array[j+1]]= [this.array[j+1],this.array[j]]
}
}
}
}
4、选择排序
原理:
从前往后找最小的数据的下标,找到后互换.
比较次数和冒泡排序次数是一样的都是N*(N-1)/2
优点
交换次数比冒泡的少
ArrayList.prototype.selectionSort = function () {
var length = this.array.length
for (var i = 0; i < length - 1; i++) {
var min = i
for (var j = min + 1; j < length; j++) {
if (this.array[min] > this.array[j]) {
min = j
}
}
[this.array[min],this.array[i]]= [this.array[i],this.array[min]]
}
}
5、快速排序
原理:
快速排序是冒泡排序的升级版本,基准值可以一次性的放到正确的位置不需要移动
复杂度O(n*log(n))
function quickSort(arr) {
if(arr.length <=1) {
return arr;
} else {
const pivot = arr[0]; // 基准值
const pivotArr = []; // 一样大的放中间
const lowArr= []; // 小的放左边
const hightArr = []; // 大的放右边
arr.forEach(_ => {
if(_ === pivot) pivotArr.push(_);
else if(_ > pivot) hightArr.push(_);
else lowArr.push(_);
})
return quickSort(lowArr).concat(pivotArr).concat(quickSort(hightArr));
}
}