最近公司不忙,看了一下排序算法,总结起来计算机实现排序就是:基于比较两个数据项,然后交换位置实现的。
以下分别介绍了冒泡排序,选择排序,插入排序,希尔排序和快速排序。
冒泡排序
- 从头到尾依次比较相邻的两个元素大小关系
- 如果前一个元素比后一个元素大,则交换位置
- 一轮比较结束时,最大的元素一定被放在数组的末尾
- 重复前面的操作,这次只用比较到数组倒数第二个元素
- 以此类推
// 冒泡排序
function bubbleSort(arr) {
var length = arr.length;
// 每一轮比较的长度都递减
for (var i = length - 1; i > 0; i--) {
// 从头到尾依次比较
for (var j = 0; j < i; j++) {
// 如前一个比后一个大则交换位置
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
选择排序
- 从数组下标为0位置的元素开始直到数组的末尾
- 找出这些元素中最小的值在哪个位置上
- 将最小的值交换到0位置上
- 重复前面的操作,从下标为1位置的元素开始
- 依此类推
// 选择排序
function selectionSort(arr) {
var length = arr.length;
for (var i = 0; i < length - 1; i++) {
// 找出每一轮的最小值,保存在下标为min的位置
var min = i;
for (var j = min + 1; j < length; j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
// 将min交换到数列的起始位置
var temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
return arr;
}
插入排序
假设数组成员的左边(前面)是已经排好队的序列,这时只需逐个将数组的成员插入到序列的合适位置即可。
- 从数组的第二个元素开始,此时因为左边只有一个元素,可以认为左边是一个已经排好队的序列
- 将第二个元素与第一个比较,如果第一个元素大,则把第一个元素向右移一位,第二个元素放到第一个元素的位置;否则保持位置不变
- 取出第三个元素,与左边已经排好的序列(第一、二个元素)从右到左依次比较,如果序列的元素大于第三个元素,则将该元素向右移一位,直到序列的元素不大于第三个元素或者已经比较到了最左边,将第三个元素插入到最后一次被移动元素的位置
- 取出第四个元素,重复上面一步
- 以此类推
// 插入排序
function insertionSort(array) {
var length = array.length;
// 从第二个元素开始
for (var i = 1; i < length; i++) {
// 保存每一轮比较用来比较的值和下标
var j = i;
var temp = array[i];
// 排好队的序列从右到左依次比较,如果序列的元素较大则右移一位
while (j > 0 && array[j - 1] > temp) {
array[j] = array[j - 1];
j --;
}
// 将用来比较的值插入到合适的位置
array[j] = temp;
}
return array;
}
希尔排序
希尔排序是基于插入排序,只不过不是一开始就依次逐个地去插入,而是先根据一定的间隔将数列分成若干组,在组内通过插入排序将顺序排好,再将间隔缩小,又排好序,直至间隔最小为1。
// 希尔排序
function shellSort(array) {
var length = array.length;
// 初始化间隔为数组长度的一半
var gap = Math.floor(length / 2);
// 间隔最小是1
while (gap > 0) {
// 从第二轮间隔处开始取值,与前面的数进行比较
for (var i = gap; i < length; i++) {
// 保存每一轮用来比较的值和下标
var j = i;
var temp = array[i];
// 进行插入排序的比较,如果左边的值较大则右移一位(在组内)
while(j + 1 > gap && array[j - gap] > temp) {
array[j] = array[j - gap];
j -= gap;
}
// 将用来比较的值插入到合适的位置
array[j] = temp
}
// 间隔每次减半,且最小值为1
gap = Math.floor(gap / 2);
}
return array
}
快速排序
快速排序的核心是选取数组中的一个值,称之为枢纽(pivot),将小于枢纽的值放在枢纽的左边,大于的放在右边,此时枢纽的位置可以不动了。再分别将枢纽的左边和右边使用此规则重复排序,以此类推,直至数组遍历结束。
class ArrayList {
array;
constructor(list) {
this.array = list;
}
// 交换两个元素的位置
swap(m, n) {
let temp = this.array[m]
this.array[m] = this.array[n]
this.array[n] = temp
}
// 获取枢纽(将数组的头,中间,尾按照大小顺序排好,枢纽选取中间的值)
getPivot(left, right) {
// 1.求出中间的位置
let center = Math.floor((left + right) / 2)
// 2.判断并且进行交换
if (this.array[left] > this.array[center]) {
this.swap(left, center)
}
if (this.array[center] > this.array[right]) {
this.swap(center, right)
}
// 此时right位置的值已经确定,只需再次比较left和center
if (this.array[left] > this.array[center]) {
this.swap(left, center)
}
// 3.巧妙的操作: 将pivot移动到right - 1的位置,
// 可用于后面交换的时候, pivot的值不需要移动来移动去
// 选定位置后, 直接交换到正确的位置即可
this.swap(center, right - 1)
// 4.返回pivot
return this.array[right - 1]
}
quickSort () {
this.quickSortRec(0, this.array.length - 1)
}
quickSortRec (left, right) {
// 0.递归结束条件
if (left >= right) return
// 1.获取枢纽
let pivot = this.getPivot(left, right);
// 保存左、右开始时的位置
let i = left
let j = right - 1
// 2.2.循环查找位置
while (i < j) {
// 从两头同时开始查找,如果左边的值大于枢纽,并且右边的值小于枢纽,则交换位置
while (this.array[++i] < pivot) { }
while (this.array[--j] > pivot) { }
if (i < j) {
this.swap(i, j)
} else {
// 如果i > j,则遍历结束,退出循环
break
}
}
// 3.将枢纽放在正确的位置(此时枢纽左边都是较小的值,枢纽右边都是较大的值)
this.swap(i, right - 1);
// 4.递归调用,分别再排枢纽左边和右边
this.quickSortRec(left, i - 1)
this.quickSortRec(i + 1, right)
}
}
let list = new ArrayList([11,14,6,3,8,5,2,8,10]);
list.quickSort();
console.log(list.array) // [2, 3, 5, 6, 8, 8, 10, 11, 14]
效率分析比较
注:详细的效率分析非常复杂,在此仅作简单分析(比较次数和交换次数),或者给出结论。
比较次数:进行数据比较的次数。
交换次数:数据交换的次数。
- 如冒泡排序中,假设数组长度为7
- 则第一次循环6次比较, 第二次5次比较, 第三次4次比较....直到最后一趟进行了一次比较
- 7个数据项比较次数: 6 + 5 + 4 + 3 + 2 + 1
- 则N个数据项的比较次数:N * (N - 1) / 2
冒泡排序 VS 选择排序
冒泡排序和选择排序的比较次数都是N * (N - 1) / 2。冒泡排序假设两次比较需要交换一次,则交换次数为N * (N - 1) / 4;而选择排序的仅有N-1次,所以可知选择排序的效率大于冒泡排序。
选择排序 VS 插入排序
在插入排序中,因为在比较时只要找到了一个不大于的值,就不再进行后面的比较,所以比较次数最多为N * (N - 1) / 2。交换次数最多也为N * (N - 1) / 2。
对于基本有序的情况,插入排序的效率大于选择排序。
希尔排序
多数情况下,希尔排序的效率高于前面3种。
快速排序
多数情况下,快速排序的效率高于前面4种。
在javascript中的应用
js中sort()方法可对数组元素进行排序,其底层实现在chrome V8引擎array.js 源码可知,数组长度小于等于 22 的用插入排序,其它的用快速排序,见下图:
参考链接:www.jianshu.com/p/f1f2dc978…
参考链接:www.jianshu.com/p/3c2184320…