十大排序算法可以分成两类:
- 比较类别排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
- 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。
算法的时间复杂度:
冒泡排序
通过相邻元素的比较和交换,使得每一趟循环都能找到未有序数组的最大值或最小值。
内循环: 使用相邻双指针 j , j + 1 从左至右遍历,依次比较相邻元素大小,若左元素大于右元素则将它们交换;遍历完成时,最大元素会被交换至数组最右边 。
外循环: 不断重复「内循环」,每轮将当前最大元素交换至 剩余未排序数组最右边 ,直至所有元素都被交换至正确位置时结束。
代码
function bubbleSort(arr) {
var len1 = arr.length;
// 一共比较多少趟
for (var i = 0; i < len1 - 1; i++) {
// 每趟进行比较,j=要进行排序的元素个数,每一趟排下来最后1个元素都不进行下一趟的排序
for (var j = 0; j < len1 - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
var arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];
console.log(bubbleSort(arr)); // [3, 4, 5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
选择排序
思路:依次找到剩余元素的最小值或者最大值,放置在末尾或者开头。
代码
function selectSort(arr) {
let len1 = arr.length;
for (var i = 0; i < len1 - 1; i++) {
var minIndex = i; // 记录最小元素的索引
for (var j = i + 1; j < len1; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j; // 如果有遇到比首元素小的,就记录该索引,这样就能找到当前趟最小元素的索引
}
}
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; // 交换首元素和最小元素的位置
}
return arr;
}
let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];
console.log(selectSort(arr)); // [3, 4, 5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
希尔排序
通过某个增量 gap,将整个序列分给若干组,从后往前进行组内成员的比较和交换,随后逐步缩小增量至 1。希尔排序类似于插入排序,只是一开始向前移动的步数从 1 变成了 gap
1、选择一个增量 k=arr.length/2 进行排序,增量k为几就将该arr分成几组,每组有 arr.length/k=2 个元素。然后组内元素从前往后进行比较,按照升序排列好,再将小组并成一个大arr。
2、再次分组,分成k/2组,每个组有 arr.length/k/2 个元素,再次将组内元素按升序进行排序,排序好在并成大arr。
3、重复上述操作,直至arr排好序。
4、插入排序时,并不是一个分组内的数字一次性用插入排序完成,而是每个分组交叉进行。
代码
function shellSort(arr) {
var len1 = arr.length;
// 第一次分组是k=arr.length/2,之后的每次分组都是k/2
for (var k = Math.floor(len / 2); k > 0; k = Math.floor(k / 2)) {
// 遍历每个分组的元素,i在分组里表示的是每个组内最后一个元素
// 在原数组arr里表示的是分组后的第一个元素,然后后面的元素依次与前面的元素进行比较
for (var i = k; i < len1; i++) { // i控制当前进行排序的是哪一个小组
var j = i; // j控制的是当前小组的最后一个元素
// 各小组内进行比较时,按升序排列
while (j - k >= 0 && arr[j] < arr[j - k]) {
[arr[j], arr[j - k]] = [arr[j - k], arr[j]];
j = j - k; // 让j变成当前小组的上一个元素
}
}
}
return arr;
}
var arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];
console.log(shellSort(arr)); // [3, 4, 5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
归并排序
把长度为n的arr分成两个长度为n/2的子序列,一直划分到最小个数之后,两两比较,合并成长度为2的小序列,然后再小序列间比较,一直重复合并成最终的arr
代码
function mergeSort(arr) {
var len1 = arr.length;
if (len1 < 2) {
return arr;
}
var mid = Math.floor(len1 / 2);
var left = arr.slice(0, mid);
var right = arr.slice(mid);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {
var result = [];
while (left.length > 0 && right.length > 0) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length) {
result.push(left.shift());
}
while (right.length) {
result.push(right.shift());
}
return result;
}
let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];
console.log(mergeSort(arr)); // [3, 4, 5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
快速排序
快速排序算法是一种基于分治思想的排序算法,其核心思路在于通过选取一个基准值,将待排序数组划分为左右两个子序列,其中左侧序列所有元素均小于基准值,右侧序列所有元素均大于基准值。之后对左右子序列递归进行快排操作,最终将整个序列排好序。
代码
var arr = [9, 16, 8, 10, 19];
var quickSort2 = function(arr) {
if (arr.length <= 1) {
return arr;
}
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1);
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort2(left).concat([pivot], quickSort2(right));
};
quickSort2(arr)