# 排序算法 **排序的稳定性** 是指对于相等的元素,排序之后,任然保存2个元素的位置没有变,就是稳定的排序,反之就是不稳定排序。 **交换排序算法** * 冒泡排序 :没轮确定一个最大的数排后面 * 插入排序 * 选择排序 :每轮选择最小的数排前面 * 希尔排序 * 快排 : 选最后一个作为pivot(基数), 将数据分为 左边小于 pivot, 右边大于 pivot * 归并排序 : * 堆排序 **线性排序算法** * 桶排序 # 交换排序算法 排序算法的复杂度由 `比较的次数` 和 `交换的次数` 一起决定。 ### 直接选择排序 1. 从未排序的序列中选择最小的元素,与放在第一个位置的元素交换 2. 依次类推,直到全部排序 在a【i,n】中最小的元素和 a[i]交换位置。空间复杂度O(1),时间复杂度 O(n^2) ### 冒泡排序 1. 相邻的2各元素比较,大的向后移,经过一轮比较,做大的元素排在最后 2. 第二轮,第二大的元素排倒数第二个位置 3. 直到全部排好 这样,即使是排好序的拿冒泡排序排序,比较的时间复杂度O(n^2) ### 插入排序 1. 第一个元素算作已经排好 2. 取下一个元素,从已经排好的序列元素中,从后向前扫描 3. 如果排好序的元素大于 新元素,排好序的元素移到下一个位置 4. 重复3,直到直到最后的插入位置 5. 重复2 类似插入扑克牌的效果 最坏的情况: 待排序的是一个逆序排放的数组,这样导致每一轮都要移动元素;此时复杂度是是0(n^2) 最好的情况: 待排序的是一已经顺序排放的数字,此时只需要做一轮比较就够了 0(n)。因此可以看到,**对大部分数据已经有序这样的数组排序,使用`插入排序`非常有优势** 空间复杂度O(1) ### 希尔排序 递减增量排序算法,对`插入排序`的改进,实质是分组插入排序,又叫`缩小增量排序` 1. 先将待排数列分割成若干子序列(增量为m) 2. 对每个子序列使用`插入排序` 3. 减小增量,再排序 4. 对全体元素做一次`插入排序` `希尔排序`提升排序的奥秘就在于`数据元素越有序,使用插入排序效率越高` ### 快速排序 递归一次,pivot 左边都比它小,右边都比它大。这是递归,分治的思想。 > 快速排序就是个二叉树的前序遍历,归并排序就是个二叉树的后序遍历 对 A[p...r] : 0. 选择最后一个元素作为 pivot (基准) 1. 分解:A[p..q-1] A[q+1..r],使得 A[p...q-1]< A[q] =tmp){ j--; } if (i0; --i) { //交换 heap_swop(&a[0],&a[i]); //调整 heap_adjust(a,i); } } 推排序还可以用来求 top-K 大(小)的问题。 接下来,我们看一组排序的动画,你看看能不能猜到他们使用了什么排序算法完成。 ![](./qsort.gif) ![](./mergesort.gif) ![](./heapsort.gif) ![](./selectsort.gif) ![](./bubblesort.gif) ![](./shellsort.gif) 答案是: 快排 |归并排序|堆排序 选择排序|冒泡排序|希尔排序 国外也有人通过舞蹈的方式编排了几种基本的排序算法,非常有趣。[点这里去看看](http://v.youku.com/v_show/id_XMzMyODk4NTQ4.html?from=s1.8-1-1.2) # 线性排序算法 上面的算法都是基于比较的排序,时间复杂度最好也是 NlogN.而非基于比较的排序,可以突破NlogN的时间下限。当然,非比较的排序,也是需要有一些限定条件的。 ### 桶排序 bucket sort 比如给全校学生做个分数排序,最大分100分。我们使用一个100个空间的辅助数据,以key为分数,value为命中的次数。通过O(n)复杂度就可以完成排序任务。这种排序方式就是桶排序。 也就是分配一个hash[100]的空间,初始化为0,遍历一遍,出现的数字就hash[k]++,这样再次遍历一次,就可以得到n个数的顺序了。 # 小结 常见的排序算法都是比较排序,比较排序的时间复杂度通常为 O(n^2) 或 O(nlogn) 但是如果带排序的数字有一些特俗性时,我们可以根据这来设计更加优化的排序算法。