选择类排序-堆排序 简单选择排序
2015-05-18 15:25
232 查看
选择类排序,每一趟在n - i + 1 ( i = 1,2, … , n - 1)个记录中选取关键字最小的记录作为有序序列中的第i个记录。
区别于插入类排序和交换类排序,选择类排序在排序过程完成前,就能通过一趟排序,获得最终有序序列中处于特定位置的元素。这使得选择类排序在TopK问题中,得到广泛的应用。
TopK问题:从无序的n个元素中,选出最大或最小的K(在笔试\面试的过程中,topK问题往往要利用选择类排序的思想来解决,但结合分布式环境和高级数据结构,使得选择类排序的题目非常有难度。
选择类排序主要有两种:
简单选择排序
堆排序
1. 简单选择排序
基本思想:
第一趟时,从第一个记录开始,通过n–1次关键字比较,从n个记录中选出关键字最小的记录,并和第一个记录交换。第二趟从第二个记录开始,选择最小的和第二个记录交换。以此类推,总共需要n-1趟。
时间复杂度:O(n2)
空间复杂度:O(1)
是否稳定:否
注意内外两层循环变量的初始值和循环结束条件的不同,这是容易出错的地方
2. 堆排序
基本思想:
把待排序记录的关键字存放在数组r1…n中,将r看成是一棵完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,其余记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆。
由无序序列建堆:待排序元素从下标1开始存储,调整堆从第n/2个元素(最后一个非叶子节点)开始向上调整,建堆完成后,形成大顶堆。
交换堆顶和最后一个元素后,重新调整堆:待排序列范围-1,然后重新调整[1,length],只需调整堆顶即可(其余部分仍然是大顶堆)。
时间复杂度:O(nlogn),最坏情况下也是O(nlogn)
空间复杂度:O(1)
是否稳定:否{5,5,3}
分析:堆排序是一种比较复杂的排序方法,很容易在笔试、面试中直接考察排序的代码。因此,同学们要认真掌握,达到可以默写的程度才可以哦。让我们剖析一下,堆排序的代码该怎么写。
实际上堆排序的过程可以分为两步:
1. 由n个元素的无序序列建大顶堆。实际上是从i/2(最后一个非叶子节点的下标是i/2)处向上调整堆,直到根节点。调整完后就得到一个大顶堆。
2. 将根节点(下标1)和最后一个节点(下标n)交换后,此时最大的元素一定位于数组末尾(下标n),除根节点外,其余的子树依然保持大顶堆的性质(节点1~n-1),因此只需要调整根节点就可以了。
可见,1,2两步都是在调整堆,只是调整的范围不一样:建堆时需要调整所有的非叶子节点,而选出一个最大元素后,只需调整剩下元素中的根节点就可以。我们设调整堆的函数为
void HeapAdjust(int Record[], int begin, int end)
根据以上分析,我们容易写出下面的代码:
注意,两次调用HeapAdjust()方法的参数不同,如果你理解了为什么不同,那么说明你已经理解了,请继续往下看。
现在,堆排序的主体框架已经有了,只剩下HeapAdjust方法没有实现了,下面要解决如何调整堆的问题,我们通过一个实例来看:
![](http://img.blog.csdn.net/20150518153439265)
上图展示了建队过程中的一趟调整的详细过程,上述例子中共有8个节点,所以从4(8/2=4)号节点开始调整,每次调整完一个节点i后,都使得以i为根的子树成为大顶堆。
根据以上过程,写出HeapAdjust的实现:
完整代码:
原文链接:
http://www.tobebatman.com/2015/05/07/%E9%80%89%E6%8B%A9%E7%B1%BB%E6%8E%92%E5%BA%8F/
区别于插入类排序和交换类排序,选择类排序在排序过程完成前,就能通过一趟排序,获得最终有序序列中处于特定位置的元素。这使得选择类排序在TopK问题中,得到广泛的应用。
TopK问题:从无序的n个元素中,选出最大或最小的K(在笔试\面试的过程中,topK问题往往要利用选择类排序的思想来解决,但结合分布式环境和高级数据结构,使得选择类排序的题目非常有难度。
选择类排序主要有两种:
简单选择排序
堆排序
1. 简单选择排序
基本思想:
第一趟时,从第一个记录开始,通过n–1次关键字比较,从n个记录中选出关键字最小的记录,并和第一个记录交换。第二趟从第二个记录开始,选择最小的和第二个记录交换。以此类推,总共需要n-1趟。
时间复杂度:O(n2)
空间复杂度:O(1)
是否稳定:否
//简单选择排序 void InSortMethod::SelectSort(int Record, int length) { int temp; for(int i=0; i<length; i++) { for(int j= i+1; j<length; j++) { if(Record[j]<Record[i]) { temp = Record[i]; Record[i] = Record[j]; Record[j] = temp; } } } }
注意内外两层循环变量的初始值和循环结束条件的不同,这是容易出错的地方
2. 堆排序
基本思想:
把待排序记录的关键字存放在数组r1…n中,将r看成是一棵完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,其余记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆。
由无序序列建堆:待排序元素从下标1开始存储,调整堆从第n/2个元素(最后一个非叶子节点)开始向上调整,建堆完成后,形成大顶堆。
交换堆顶和最后一个元素后,重新调整堆:待排序列范围-1,然后重新调整[1,length],只需调整堆顶即可(其余部分仍然是大顶堆)。
时间复杂度:O(nlogn),最坏情况下也是O(nlogn)
空间复杂度:O(1)
是否稳定:否{5,5,3}
分析:堆排序是一种比较复杂的排序方法,很容易在笔试、面试中直接考察排序的代码。因此,同学们要认真掌握,达到可以默写的程度才可以哦。让我们剖析一下,堆排序的代码该怎么写。
实际上堆排序的过程可以分为两步:
1. 由n个元素的无序序列建大顶堆。实际上是从i/2(最后一个非叶子节点的下标是i/2)处向上调整堆,直到根节点。调整完后就得到一个大顶堆。
2. 将根节点(下标1)和最后一个节点(下标n)交换后,此时最大的元素一定位于数组末尾(下标n),除根节点外,其余的子树依然保持大顶堆的性质(节点1~n-1),因此只需要调整根节点就可以了。
可见,1,2两步都是在调整堆,只是调整的范围不一样:建堆时需要调整所有的非叶子节点,而选出一个最大元素后,只需调整剩下元素中的根节点就可以。我们设调整堆的函数为
void HeapAdjust(int Record[], int begin, int end)
根据以上分析,我们容易写出下面的代码:
//堆排序 void InsSortMethod::HeapSort(int Record[], int length) { //记录下标从1开始,建堆. for (int i = length / 2; i > 0; i--) HeapAdjust(Record, i, length); for (int j = length; j > 1; j--) { int temp = Record[1]; Record[1] = Record[j]; Record[j] = temp; HeapAdjust(Record, 1, j - 1); } }
注意,两次调用HeapAdjust()方法的参数不同,如果你理解了为什么不同,那么说明你已经理解了,请继续往下看。
现在,堆排序的主体框架已经有了,只剩下HeapAdjust方法没有实现了,下面要解决如何调整堆的问题,我们通过一个实例来看:
上图展示了建队过程中的一趟调整的详细过程,上述例子中共有8个节点,所以从4(8/2=4)号节点开始调整,每次调整完一个节点i后,都使得以i为根的子树成为大顶堆。
根据以上过程,写出HeapAdjust的实现:
void InsSortMethod::HeapAdjust(intRecord[], intbegin, intend) { int temp = Record[begin]; for (int i = begin * 2; i <= end; i *= 2) { if (i < end && Record[i] < Record[i + 1]) i++; if (temp >= Record[i]) break; Record[begin] = Record[i]; begin = i; } Record[begin] = temp; }
完整代码:
//堆排序 void InsSortMethod::HeapSort(int Record[], int length) { //记录下标从1开始,建堆. for (int i = length / 2; i > 0; i--) HeapAdjust(Record, i, length); for (int j = length; j > 1; j--) { int temp = Record[1]; Record[1] = Record[j]; Record[j] = temp; HeapAdjust(Record, 1, j - 1); } }
void InsSortMethod::HeapAdjust(intRecord[], intbegin, intend) { int temp = Record[begin]; for (int i = begin * 2; i <= end; i *= 2) { if (i < end && Record[i] < Record[i + 1]) i++; if (temp >= Record[i]) break; Record[begin] = Record[i]; begin = i; } Record[begin] = temp; }
原文链接:
http://www.tobebatman.com/2015/05/07/%E9%80%89%E6%8B%A9%E7%B1%BB%E6%8E%92%E5%BA%8F/
相关文章推荐
- 排序算法(四)、选择排序 —— 简单选择排序 和 堆排序
- 常见的五类排序算法图解和实现(选择类:简单选择排序,锦标赛排序,树形选择排序,堆排序)
- (五)选择排序:简单选择排序,堆排序
- 【Java常用排序算法】选择排序(简单选择排序、堆排序)
- 名称:选择排序---简单选择排序和堆排序
- 三 选择排序(简单选择排序和堆排序)
- 排序算法--选择排序(简单选择排序、堆排序)java实现
- 简单选择排序,树形选择排序,堆排序详解
- 算法实现之简单选择排序、二元选择排序和堆排序
- 常见的五类排序算法图解和实现(选择类:简单选择排序,锦标赛排序,树形选择排序,堆排序)
- 排序算法(二)选择类排序:简单选择排序,堆排序,锦标赛排序
- 选择排序之简单选择排序和堆排序
- 选择排序(简单选择排序--改进的简单选择排序--堆排序)
- java中的排序算法——简单选择排序,树形选择排序与堆排序(一)
- 常用排序算法之选择排序 ( 直接选择排序、堆排序 )
- java实现七种排序 (插入排序, 希尔排序, 插入排序, 快速排序, 简单选择排序, 堆排序, 归并排序)
- 几种常见的排序算法,选择排序,冒泡排序,希尔排序,堆排序,快速排序,归并排序,基数排序的比较
- Java实现——选择排序、shell排序、合并排序、堆排序
- 简单选择排序和二元选择排序的代码实现
- 快速排序、希尔排序、插入排序、选择排序、归并排序、堆排序总结