您的位置:首页 > 其它

选择类排序-堆排序 简单选择排序

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)

是否稳定:否

//简单选择排序
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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐