推荐一个讲解清晰的网站: https://sort.hust.cc/
冒泡排序
算法步骤
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len - 1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
复杂度分析
平均时间复杂度:O(n^2)
空间复杂度:O(1)
最好情况:O(n) 当输入的数据已经是正序时
最坏情况:O(n^2) 当输入的数据已经是逆序时
其他
排序方式 in-place
稳定性: 稳定
快速排序
在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
算法步骤
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
function quickSort(arr, left, right) {
var len = arr.length;
var partitionIndex;
var left = typeof left !== "number" ? 0 : left;
var right = typeof right !== "number" ? len - 1 : right;
if (left < right) {
partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex - 1);
quickSort(arr, partitionIndex + 1, right);
}
}
// 分区操作
function partition(arr, left, right) {
// 选定最左侧的元素为枢纽元
var pivot = left;
var index = pivot + 1;
for (var i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
// 将比枢纽元小的依次放到枢纽元下标的右侧
swap(arr, i, index);
index++;
}
}
// 这样左侧都是比枢纽元小的,右侧都是比枢纽元大的
swap(arr, pivot, index - 1);
return index - 1;
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
复杂度分析
平均时间复杂度:O(nlogn)
空间复杂度:O(logn)
最好情况:O(nlogn)
最坏情况:O(n^2)
其他
排序方式 in-place
稳定性: 不稳定