常见排序算法-JS
冒泡排序
特点
- 两两比较
- 比较:N^2 / 2约等于 时间复杂度O(N^2)
- 交换:假设比较两次交换一次,N^2 / 4 约等于 时间复杂度O(N^2)
代码实现
//冒泡排序
bubbleSort() {
for (let i = this.array.length - 1; i >= 0; i--) {
for (let j = 0; j < i; j++) {
if (this.array[j] > this.array[j + 1]) {
this.swap(j, j + 1)
}
}
}
}
选择排序
特点
每轮排序选出一个最小或最大的元素再一个个插入数组
- 比较:N^2 / 2 时间复杂度O(N^2)
- 交换:N-1 时间复杂度O(N)
- 所以选择排序效率上是优于冒泡排序的
代码实现
//选择排序
selectionSort() {
let min = 0
for (let i = 0; i < this.array.length; i++) {
min = i
for (let j = i; j < this.array.length; j++) {
if (this.array[min] > this.array[j]) {
console.log(min + ' ' + j)
min = j
}
}
this.swap(min, i)
}
}
插入排序
特点
从未排序元素中挑出一个元素挨个跟局部有序的元素进行比较,找到对应位置插入即可
- 比较次数 :平均 N(N-1)/4,最坏 N(N-1)/2* ,时间复杂度:O(N^2)
- 交换次数: 平均N*(N-1)/4 ,最坏N*(N-1)/2,时间复杂度:O(N^2)
- 因为插入排序的比较次数开销较小,所以其在简单排序中的效率最高
代码实现
//插入排序
insertSort() {
//未排序的元素挨个跟局部有序的元素比较
for (let i = 1; i < this.array.length; i++) {
//i是局部有序元素的长度,指向要插入的元素
let j = i //j是当前有序元素的下标,最后需要指向temp应该插入的下标
let temp = this.array[i]
while (this.array[j - 1] > temp && j >= 0) {
this.array[j] = this.array[j - 1]
j--
}
this.array[j] = temp
}
}
希尔排序
特点
希尔排序基于插入排序,根据元素长度算出增量,以增量分组,对分组进行插入排序,再减少增量继续以上操作直到增量小于0(计算增量使用原稿的方式N/2)
- 比较次数:最坏 O(N^2),平均O(N*logN)
- 交换次数:最坏 O(N^2),平均O(N*logN)
- 希尔排序属于高级排序比所有简单排序的效率都要高,某种情况下还要好于快速排序
代码实现
shellSort() {
//get gap
for (
let gap = Math.floor(this.array.length / 2);
gap >= 1;
gap = Math.floor(gap / 2)
) {
//以gap为分组,然后对分组进行插入排序
for (let i = gap; i < this.array.length; i += gap) {
//i is the index of head of group
let j = i // j是正确位置
let temp = this.array[i]
while (j > gap - 1 && this.array[j - gap] > temp) {
this.array[j] = this.array[j - gap]
j -= gap
}
this.array[j] = temp
}
}
}
快速排序
特点
快速排序是冒泡排序的升级版,使用递归实现算法
- 1.选出一个中位数(pivot:间隔点)放到元素倒数第一的位置
- 2.设置左L右R两个指针指向将要排序的元素的头和尾
- 3.遍历L,R分别与pivot进行比较,L不断自增当L指向的元素大于pivot时暂停,R相反
- 4.当L >= R 时停止遍历,这时L的位置就指向了pivot的正确位置,且后续都不会改变
- 5.分而治之,分隔点两边继续上述步骤进行递归
- 时间复杂度:O(nlogN)
代码实现
//快速排序
quickSort() {
this.quick(0, this.array.length - 1)
}
quick(left, right) {
//当left,right相等,结束递归
if (left >= right) {
return
} else if (right - left == 1) {
//长度只有两个的元素时,直接判断交换,结束递归
if (this.array[left] > this.array[right]) {
this.swap(left, right)
}
return
}
//拿到分隔点
let pivot = this.getPivot(left, right)
console.log(this.array)
let l = left
let r = right - 1
//遍历元素找到pivot对应正确位置,使pivot左边的元素都小于pivot,右边相反
while (true) {
while (this.array[++l] < pivot) {}
while (this.array[--r] > pivot) {}
if (l < r) {
console.log(this.array[l] + ',' + this.array[r])
this.swap(l, r)
} else {
break
}
}
//将pivot对应正确位置
this.swap(l, right - 1)
//分而治之
this.quick(left, l - 1)
this.quick(l , right)
}
//拿到中位数,分隔点
getPivot(left, right) {
let center = Math.floor((left + right) / 2)
//判断三个元素大小进行交换
if (this.array[left] > this.array[center]) {
this.swap(left, center)
}
if (this.array[center] > this.array[right]) {
this.swap(center, right)
}
if (this.array[left] > this.array[right]) {
this.swap(left, right)
}
//将中位数放到倒数第二的位置
this.swap(center, right - 1)
//返回中位数
return this.array[right - 1]
}