排序算法时间复杂度
| 排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 排序方式 | 稳定性 |
|---|---|---|---|---|---|---|
| 冒泡排序 | O(n*n) | O(n*n) | O(n*n) | O(1) | In-place | 稳定 |
| 选择排序 | O(n*n) | O(n*n) | O(n*n) | O(1) | In-place | 不稳定 |
| 插入排序 | O(n*n) | O(n) | O(n*n) | O(1) | In-place | 稳定 |
| 快速排序 | O(n*log n) | O(n*log n) | O(n*n) | O(n*log n) | In-place | 不稳定 |
| 归并排序 | O(n*log n) | O(n*log n) | O(n*log n) | O(n) | Out-place | 稳定 |
| 希尔排序 | O(n*log n) | O(n) | O(n*n) | O(1) | In-place | 不稳定 |
| 堆排序 | O(n*log n) | O(n) | O(n*log n) | O(1) | In-place | 不稳定 |
注释:
- In-place: 占用常数内存,不占用额外内存
- Out-place: 占用额外内存
冒泡排序
定义
就是将大的数组往上冒(我这里上指的是数组尾端) , 所以冒一次就可以将一个最大的数冒的最上面, 所以不需要每次都全部冒
代码实现
function bubbleSort(arr) {
let len = arr.length;
for (let i = 0; i < len; i++) {
for(let j = 1; j < len - i; j ++) {
if (arr[j-1] > arr[j]) {
let temp = arr[j-1];
arr[j-1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
选择排序
定义
选择排序 , 从开始位置每次找出最小那个数字, 放到起始位置 ,然后起始位置迭代重复操作
代码实现
function selectionSort(arr) {
for (let i = 0; i < arr.length; i++) {
let minIndex = i;
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
const temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp
}
return arr
}
插入排序
定义
插入排序 比如 2,3,1 数组 , 假如此时到1了进行插入排序, 1先拿出来, 3和1比较大,就向后移动一下,然后2和1比较还是大就继续向后移动, 此时就可以找到合适位置插入了
代码实现
function insertSort(arr) {
for (let i = 1; i < arr.length; i++) {
let j = i - 1;
let temp = arr[i];
while(j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j];
j --;
}
arr[j + 1] = temp;
}
return arr;
}
快速排序
定义
1、在数组中找一个元素(节点),比它小的放在节点的左边,比它大的放在节点右边。一趟下来,比节点小的在左边,比节点大的在右边; 2、不断执行这个操作....
实现
function quickSort(arr = []) {
if (arr.length <= 1) {
return arr;
}
// 取第一个元素作为判断的依据
let left = [], right = [];
for(let i = 1; i < arr.length; i ++) {
if (arr[i] < arr[0]) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat(arr[0], quickSort(right));
}
归并排序
定义
- 将两个已排好序的数组合并成一个有序的数组
- 将元素分隔开来,看成是有序的数组,进行比较合并
- 断拆分和合并,直到只有一个元素
实现
function merge(left, right) {
let res = [];
while(left.length > 0 && right.length > 0) {
if (left[0] < right[0]) {
res.push(left.shift());
} else {
res.push(right.shift());
}
}
return res.concat(left, right);
}
function mergeSort(arr) {
if (arr.length <= 1) {
return arr;
}
// arr.length >> 1 相当于 Math.floor(arr.length/2) 也可以这样 arr.length / 2 | 0
let mid = arr.length >> 1;
let left = arr.slice(0, mid);
let right = arr.slice(mid);
return merge(mergeSort(left), mergeSort(right));
}
希尔排序
定义
- 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。
- 所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序。
- 取第二个增量d2小于d1重复上述的分组和排序,直至所取的增量dt=1(dt小于dt-l小于…小于d2小于d1),即所有记录放在同一组中进行直接插入排序为止。
代码实现
function shellSort(arr) {
const len = arr.length;
let gap = len >> 1;
for (gap; gap > 0; gap = gap >> 1) {
for (let i = gap; i < len; i++) {
let j = i - gap;
const temp = arr[i];
while(j >= 0 && arr[j] > temp) {
arr[j + gap] = arr[j];
j -= gap;
}
arr[j + gap] = temp;
}
}
return arr;
}
堆排序
定义
利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。
- 将待排序的序列构造成一个最大堆,此时序列的最大值为根节点
- 依次将根节点与待排序序列的最后一个元素交换
- 再维护从根节点到该元素的前一个节点为最大堆,如此往复,最终得到一个递增序列
代码实现
function heapSort(arr) {
// 初始化大顶堆,从第一个非叶子节点开始
let len = arr.length;
let i = Math.floor(len / 2 - 1);
for (i; i >= 0; i --) {
adjustHeap(arr, i, len);
}
// 排序,每一次for循环找出一个当前最大值,数组长度减一
for (let j = len - 1; j > 0; j --) {
// 根节点与最后一个节点交换
const temp = arr[0];
arr[0] = arr[j];
arr[j] = temp;
adjustHeap(arr, 0, j);
}
return arr;
}
function adjustHeap(arr, index, len) {
let j = (index << 1) + 1; //左子节点索引
// 左子节点索引超出计算范围,直接返回。
if (j >= len) {
return;
}
// 找到两个孩子中较大的一个,再与父节点比较
if (j + 1 < len && arr[j] < arr[j + 1]) {
j ++;
}
// 如果父节点小于子节点则交换;否则跳出
if (arr[index] < arr[j]) {
const temp = arr[index];
arr[index] = arr[j];
arr[j] = temp;
adjustHeap(arr, j, len);
}
}