这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
一、希尔排序
希尔排序是插入排序的一种高效的改进版,而且效率也比插入排序更高;希尔排序是超越O(N^2)的排序算法;实现理解起来可能比较难,是通过三层循环实现;
-
希尔排序和插入排序是很相似的,只不过在插入排序中的增量是1,而希尔排序的初始化增量
gap是自定义的;一般最好定义为length/2;var length = this.array.length; var gap = Math.floor(length / 2); -
以
gap为间隔进行分组,对每个分数进行插入排序,让每个分组按从小到大的排序排列; -
在每个间隔的插入排序完成之后缩小间隔继续循环,直到间隔为1时停止;内层循环跟插入排序中的是差不多的,只是插入排序中的
gap=1而已;while (gap >= 1) { for (var i = gap; i < length; i++) { var temp = this.array[i]; var j = i; while (this.array[j - gap] > temp && j > gap - 1) { this.array[j] = this.array[j - gap]; j -= gap; } this.array[j] = temp; } gap = Math.floor(gap / 2); }
希尔排序的效率跟增量也是有关系的,经过统计,希尔排序最坏情况西的时间复杂度为O(N^2),通常情况下都比O(N^2)好。总之,希尔排序在大多数情况下效率都是高于简单排序的;
二、快速排序
在大多数情况下,快速排序是最快的;快速排序可以在一次循环中(其实是递归调用),找出某个元素的正确位置,并且该元素之后不需要任何移动;快速排序最重要的实现就是分而治之;
在快速排序中,最重要的就是找枢纽,对于一个无序的排列,我们在其中分别找出【左、中、右】三个位置的,比如对于下面这个数组a:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 34 | 11 | 67 | 45 | 86 | 9 | 23 | 27 | 30 | 4 |
-
a[left] = a[0] = 34; a[right] = a[9] = 4; a[center] = a[4] = 86;我们先对这三个数进行排序,再在里面取其中位数(即a[center]=9;找中位数是为了让找到的这个枢纽不要太大也不要太小)作为枢纽;注:center = (left+right)/2;var center = Math.floor((left + right) / 2) if (this.array[left] > this.array[center]) { this.swap(left, center) } if (this.array[left] > this.array[right]) { this.swap(left, right) } if (this.array[center] > this.array[right]) { this.swap(center, right) } -
找到枢纽后,我们把枢纽和在
a[right-1]位置的元素进行交换(经过了上面的排序,a[right]一定大于a[right-1]),指定两个指针分别指向a[left]和a[right-1];这样操作之后我们就找到了枢纽并把它放到对应的位置,便于我们后序的操作this.swap(center, right - 1) return this.array[right-1];0 1 2 3 4 5 6 7 8 9 4 11 67 45 30 9 23 27 34 86 -
左边的指针向右走,当走到一个比枢纽大的元素时停止,也就是走到67的时候停止;让右边的指针向左走,当走到一个比枢纽小的元素时停止,也就是27;让这两个数进行交换;
if (left >= right) return; // 递归结束条件 // 1.获取枢纽 var pivot = this.median(left, right); var i = left + 1; var j = right; while (i < j) { while (this.array[i] < pivot && i < j) { i++ } while (this.array[j] > pivot && i < j) { j-- } if (i < j) { this.swap(i, j); } }0 1 2 3 4 5 6 7 8 9 4 11 27 45 30 9 23 67 34 86 交换之后继续执行上述操作,直到这两个指针指向的是同一位置为止;再把枢纽和两个指针指向的位置进行交换;第一轮结束之后,两个指针指向的位置就是
a[6]=45的位置,将枢纽和a[6]进行交换之后,排列如下:0 1 2 3 4 5 6 7 8 9 4 11 27 23 30 9 34 67 45 86 -
这个时候可以看到,枢纽(34)的左边的所有数都都小于34,枢纽右边的所有数都大于34;再继续对左边和右边进行上面的操作,也就是递归操作,最后得到的就是一个有序的排列
if (i > right) { this.swap(i, right - 1); } this.quick(left, i - 1); // 递归函数 this.quick(i, right);
如果搞懂了插入排序,希尔排序就很好理解了;而快排是相对较好的排序方式,实现的思路还是比较复杂的,多打打便于理解;