- 选择排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
选择排序的思想其实和冒泡排序有点类似,都是在一次排序后把最小的元素放到最前面,或者将最大值放在最后面。
但是过程不同,冒泡排序是通过相邻的比较和交换。而选择排序是通过对整体的选择,每一趟从前往后查找出无序区最小值,将最小值交换至无序区最前面的位置。
平均时间复杂度:O(N^2)
最佳时间复杂度:O(N^2)
最差时间复杂度:O(N^2)
空间复杂度:O(1)
排序方式:In-place
稳定性:不稳定
/**
* 基础选择排序 - 方法1
* @param {*} arr 数组 [3, 1, 5, 4, 7, 6, 0, 2]
* @returns sort-arr
*/
console.time('selectSort1');
var arr1 = [3, 1, 5, 4, 7, 6, 0, 2];
var selectSort1 = arr => {
let len = arr.length;
if (len <= 1) return;
let minIndex = 0; // 默认最小值为第一位0
for (let i = 0; i < len - 1; i++) {
minIndex = i
for(let j = i+1;j<len;j++){
arr[j] < arr[minIndex] ? minIndex = j : null
}
[arr[i],arr[minIndex]] = [arr[minIndex],arr[i]]
}
console.log('排序后数组', arr);
return arr;
};
selectSort1(arr1);
console.timeEnd('selectSort1');
- 快速排序
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
快速排序的基本思想就是分治法
平均时间复杂度:O(NlogN)
最佳时间复杂度:O(NlogN)
最差时间复杂度:O(N^2)
空间复杂度:根据实现方式的不同而不同
/**
* 基础快速排序 - 方法1
* 缺点:① 多开辟了left,right空间
* ② 使用push及concat方法
* @param {*} arr 数组 [3, 1, 5, 4, 7, 6, 0, 2]
* @returns sort-arr
*/
console.time('quickSort1');
// var arr1 = [3, 1, 5, 4, 7, 6, 0, 2];
var arr1 = [3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2];
var quickSort1 = arr => {
// 排异
let len = arr.length;
if (len <= 1) return arr;
// 算法核心
let target = arr[0];
let left = [],
right = [];
for (let i = 1; i < len; i++) {
if (arr[i] < target) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 终值
// return quickSort1(left).concat([target], quickSort1(right)); // es5 数组concat
return [...quickSort1(left),target,...quickSort1(right)] // es6 展开运算符
};
console.log(quickSort1(arr1));
console.timeEnd('quickSort1');
/**
* 优化快速排序 - 方法2
* 优点:① 增加前后两个指针l,r 每次默认选择当前分区数组的第一个为目标值target
* ② 通过一轮while大循环 将小于target的值排到左分区 将大于target的值排到右分区
* ③ 此时再分别递归左右分区数组
* ④ 针对大数组数据时 效果很好 相较于上面的算法 我们以较好的状态来进行分区 而不是每次对比来左右分区
* @param {*} arr 数组 [3, 1, 5, 4, 7, 6, 0, 2]
* @returns sort-arr
*/
console.time('quickSortWrapper');
// var arr2 = [3, 1, 5, 4, 7, 6, 0, 2];
var arr2 = [3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2];
var quickSortWrapper = arr => {
// 排异
let len = arr.length;
if (len <= 1) return arr;
// 算法核心
let arrSort = quickSort2(arr,0,len-1)
// 终值
return arrSort
};
var quickSort2 = (arr,start,end) => {
if(end - start < 1) return
let target = arr[start] // 首次进来默认选择第0个作为目标值
let l = start
let r = end // 这里另外声明两个变量来替代start end 因为后面在进行左右分区递归遍历时需要原数组的start,end
while(l < r){
while(l < r && arr[r] >= target){
r--
}
arr[l] = arr[r]
while(l < r && arr[l] <= target){
l++
}
arr[r] = arr[l]
}
arr[l] = target
// 此时l索引位置 左边已经是小于target的值 右边已经是大于target的值 且分区索引位置就是l位置
quickSort2(arr,start,l-1) // 左分区 递归遍历
quickSort2(arr,l+1,end) // 右分区 递归遍历
return arr
}
console.log(quickSortWrapper(arr2));
console.timeEnd('quickSortWrapper');
/**
* 优化快速排序 - 方法3
* 优点:① 三路快排
* ② 适合大数组数据且含重复数据比较多的情况 使用
* ③ 感觉跟第一种 就是多了针对center收集了重复数据 避免重复遍历比较
* @param {*} arr 数组 [3, 1, 5, 4, 7, 6, 0, 2]
* @returns sort-arr
*/
console.time('quickSort3');
// var arr3 = [3, 1, 5, 4, 7, 6, 0, 2];
var arr3 = [3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2,3, 1, 5, 4, 7, 6, 0, 2];
var quickSort3 = arr => {
// 排异
let len = arr.length;
if (len <= 1) return arr;
// 算法核心
let left = [],right=[],center=[];
let target = arr[0],i=0;
while(i<len){
if(arr[i] < target){
left.push(arr[i])
}else if(arr[i] === target){
center.push(arr[i])
}else{
right.push(arr[i])
}
i++
}
// 终值
// return quickSort3(left).concat(...center, quickSort3(right)); // es5 数组concat
return [...quickSort3(left),...center,...quickSort3(right)] // es6 展开运算符
};
console.log(quickSort3(arr3));
console.timeEnd('quickSort3');
参考链接:
选择排序
快速排序
快速排序
快速排序时间空间复杂度推算