排序

来源:互联网 发布:天猫淘宝商城手机专卖 编辑:程序博客网 时间:2024/05/01 08:41

      笔试面试时候考的概率比较高的算是排序算法了。后来我发现,虽然好多算法没用,但是背下来还是很有必要的,因为可以在面试的时候啪啪啪就给敲出来,给别人一种很熟练的感觉。呵呵。本人到现在基本没有算法经验,等于从零学起。不为别的,只为把自己提高一个境界。虽然以后可能不干程序员,但是希望能以程序员自居。代码基本上来自数据结构与算法四川大学版

      我在理解上可能会出现错误,希望大家指正,谢谢!复杂度均为时间复杂度

      个人认为比较重要的几种排序是,快速排序,堆排序和归并排序,做到能够直接敲代码,一遍通过的程度

1. 简单选择排序

    每次选择一个最小值插入到适当的位置上。

    测试数据 int arr[]={88,23,98,56,12,74,25,36};

    输出数据
    12 23 98 56 88 74 25 36
    12 23 98 56 88 74 25 36
    12 23 25 56 88 74 98 36
    12 23 25 36 88 74 98 56
    12 23 25 36 56 74 98 88
    12 23 25 36 56 74 98 88
    12 23 25 36 56 74 88 98

2. 冒泡排序

    每次循环把最大的放在数组右边

 测试数据  int arr[]={88,23,98,56,12,74,25,36};

 输出数据

 23 88 56 12 74 25 36 98
 23 56 12 74 25 36 88 98
 23 12 56 25 36 74 88 98
 12 23 25 36 56 74 88 98
 12 23 25 36 56 74 88 98
 12 23 25 36 56 74 88 98
 12 23 25 36 56 74 88 98

 可以看到前四次比较已经排好序了。

3. 插入排序

 从数组的前两个数开始,每次排序前面的子序列,也就是把有序子序列随后的一个数插入到有序子序列正确的位置上。

 

 测试数据 int arr[]={88,23,98,56,12,74,25,36};

 输出数据
 23 88 98 56 12 74 25 36
 23 88 98 56 12 74 25 36
 23 56 88 98 12 74 25 36
 12 23 56 88 98 74 25 36
 12 23 56 74 88 98 25 36
 12 23 25 56 74 88 98 36
 12 23 25 36 56 74 88 98

 也就是每次排序把一个数放在正确的位置上

4. 希尔排序

5. 快速排序 O(n log n)

    百度百科解释:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

    维基百科解释:快速排序使用分治法策略来把一个序列分为两个子序列

步骤为:
    1. 从数列中挑出一个元素,称为 "基准"(pivot),
    2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。 在这个分割之后,该基准是它的最后位置。这个称为分割(partition)操作。
    3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

        递回的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递回下去,但是这个算法总会结束,因为在每次的迭代中,它至少会把一个元素摆到它最后的位置去

 测试数据 int arr[]={88,23,98,56,12,74,25,36};

88  23  98  56  12  74  25  36    红色代表low,蓝色代表high

36  23  98  56  12  74  25  88    第一次从右边找第一个小于low的,交换位置,36,88

36  23  88  56  12  74  25  98    第二次从左边找第一个大于high的,交换位置,98,88

36  23  25  56  12  74  88  98    88 , 25 交换

36  23  25  56  12  74  88  98    从low找一个大于high,88的,没有,此时low==high,一轮结束

12  23  25  56  36  74  88  98    这次比较88前面的,1...5,36 和12交换

12  23  25  36  56  74  88  98    交换56,36,再找则low == high ,一轮结束,如此反复直到每个子序列都为一,排好

..................

快速排序的基本思想是任选序列中的一个记录,通常选择第一个记录,以这个值和其余的做比较,将所有关键字比它小的排前面,大的排后面,仔细看上面第四行的88,经过一趟排序后,以此记录为界,将序列分成两部分,重复上述过程。摘自数据结构与算法四川大学版。

6. 归并排序O(n log n)

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

在这里就是先将数组分成小数组,两个数字一份,排序,然后逐渐归并,四个、八个.......

测试数据 int arr[]={88,23,98,56,12,74,25,36};

这次打印的结果是每次的 low mid high

0 3 7
0 1 3
0 0 1
2 2 3
4 5 7
4 4 5
6 6 7

可以看到每次都是把数组在分割成更小的数组,从红色的数字中可以看出,最小的序列是两个数字。如下所示

88 23  98 56  12 74  25 36     上面红色区间

23 88  56 98  12 74  25 36     上面蓝色区间

23 56  88 98  12 25  36 74     上面黑色区间,即整个数组

12 23  25 36  56 74  88 98     排序完成

归并排序能够用来做外部排序。

7. 堆排序 O(n log n)

  大根堆和小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。

  以大根堆举例子,因为堆总能保证根节点是最大的一个数,所以可以用这种方法进行排序,在实现上是每次把根节点与数组最后一个数交换。即是数组的第一位和最后一位交换位置,每次交换之后继续构造大顶堆。从而实现对数组的排序。

测试数组 int arr[]={88,23,98,56,12,74,25,36};

用数组表示二叉树,就是第一个数做为根节点,然后从左到右依次就是左孩子右孩子,一层一层的往下排。上面这个数组转换成二叉树之后如下表示,节点i的左孩子是 2*i ,右孩子是 2*i+1,别按0算啊。父节点是 i/2

                                             

其实简单的理解就是,每个节点的孩子都不能比自己大,一趟比较之后就把最大的数交换到根节点。拿走,之后再寻找那个最大的数。具体过程可以参考代码。

8. 基数排序O(n)

算法本身不难理解,下面给出排序过程就能一目了然。

测试数据 int arr[]={88,23,98,56,12,74,25,36,81,85,21,92,16,38}; 14个

0 ->
1 -> 81 21
2 -> 12 92
3 -> 23
4 -> 74
5 -> 25 85
6 -> 56 36 16
7 ->
8 -> 88 98 38
9 ->

 

0 ->
1 -> 12 16
2 -> 21 23 25
3 -> 36 38
4 ->
5 -> 56
6 ->
7 -> 74
8 -> 81 85 88
9 -> 92 98

参数 n 待排序个数,r 基数,数字10,由多少最基础的单元组成(描述不准确). d 数字位数,上面的数字为两位,d为2

distribute 和 collect 函数要操作D遍数组。

第一次,按照各位上的数字进行分配。第二次,按照十位上的数字进行分配。