文章目录
已经有很多排序算法的文章了,这里主要是做一个自己的总结。
内容大部分整合自《算法第四版》,图示大部分来自菜鸟教程网。语言选择的当然是JavaScript啦~
代码可以直接在LeetCode的912.排序数组题目上运行
在此之前先定义一个交换数组中两个元素的函数
function swap(arr, i, j) {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
1. 冒泡排序 (简单)
流程
- 比较相邻的元素。如果前者大于后者就交换
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数
- 针对所有的元素重复以上的步骤,除了最后一个
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
图示
代码
参考代码1
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function (nums) {
for (let i = 0; i < nums.length; i++) {
for (let j = 0; j < nums.length - i -1; j++) {
if (nums[j] > nums[j + 1]) {
swap(nums, j, j + 1);
}
}
}
return nums;
};
参考代码2 优化冒泡排序
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function (nums) {
for (let i = 0; i < nums.length; i++) {
let flag = true;
for (let j = 0; j < nums.length - i -1; j++) {
if (nums[j] > nums[j + 1]) {
swap(nums, j, j + 1);
flag = false;
}
}
if(flag) {
return;
}
}
return nums;
};
复杂度分析
时间复杂度: O ( N 2 ) O(N^2) O(N2),这里 N N N是数组的长度;
空间复杂度: O ( 1 ) O(1) O(1),使用到常数个临时变量。
2. 选择排序(简单)
一种最简单的排序算法:首先找到数组中最小的元素,将它和数组中的第一个元素交换位置,然后在剩下的元素中找到最小的,与第二个元素交换位置,直到整个数组排序。(不断选择剩余数组中最小的元素)
流程
- 首先在未排序序列中找到最小元素,存放到排序序列的起始位置
- 再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾
- 重复第二步,直到所有元素均排序完毕
图示
代码
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function (nums) {
for (let i = 0; i < nums.length; i++) {
let min = i;
// 已排序区间 [0, i) ,未排序区间 [i+1 , len)
// 遍历 i+1 之后的元素找到最小元素的索引
for (let j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[min]) {
min = j;
}
}
swap(nums, i, min);
}
return nums;
};
复杂度分析
- 对于长度为N的数组,选择排序需要大约 N 2 / 2 N^2/2 N2/2次比较和 N N N次交换。
- 有两个特点:①运行时间和输入无关 ②数据移动是最少的,交换次数和数组的大小是线性关系
时间复杂度: O ( N 2 ) O(N^2) O(N2),这里 N N N 是数组的长度;
空间复杂度: O ( 1 ) O(1) O(1),使用到常数个临时变量。
3. 插入排序(重点)
流程
每次将一个数字插入一个有序的数组里,成为一个长度更长的有序数组,有限次操作以后,数组整体有序
图示
代码
参考代码1(交换元素)(算法第四版的思路)
/**
* 插入排序
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function (nums) {
// 从下标为1的元素开始选择合适的位置插入,因为下标为0的只有一个元素,默认是有序的
// 已排序区间 [0, i) ,未排序区间 [i , len)
// 将 nums[i] 插入到区间 [0, i) 使之成为有序数组
for (let i = 1; i < nums.length; i++) {
// 从右往左遍历
for (let j = i; j > 0 && nums[j] < nums[j - 1]; j--) {
// 只要nums[j]比前一个元素nums[j-1]小,就交换这两个元素
swap(nums, j, j - 1);
}
}
return nums;
};
参考代码2(移动元素)
/**
* 插入排序
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function (nums) {
for (let i = 1; i < nums.length; i++) {
// 已排序区间 [0, i) ,未排序区间 [i , len)
// 将 nums[i] 插入到区间 [0, i) 使之成为有序数组
// 先暂存这个元素,然后之前元素逐个后移,留出空位
let temp = nums[i];
let j = i;
while(j > 0 && temp < nums[j - 1]) {
// 只要nums[j]比前一个元素nums[j-1]小,将nums[j-1]移动到nums[j]
nums[j] = nums[j - 1];
j--;
}
// 找到位置j,将i的值放在j上
nums[j] = temp;
}
return nums;
};
复杂度分析
插入排序对部分有序的数组和“短数组”很有效
时间复杂度: O ( N 2 ) O(N^2) O(N<