您的位置:首页 > 其它

排序之堆排序(Heap Sort)

2013-12-07 19:18 627 查看
堆的定义:

具有n个元素的序列{k1 , k2 , k3 ......kn},当且仅当满足条件:



此时序列成为堆;

若用数组存储堆,则堆可以看作完全二叉树,只不过其任一非叶节点满足上述性质,即:

Key[i] <= key[2i] && Key[i] <= Key[2i + 1](小顶堆) 或者Key[i] => Key[2i] && Key[i] => Key[2i + 1](大顶堆)

(1)所有非叶节点均不大于或者均不小于其左右子节点;

(2)所有节点的左子树或者右子树皆满足上述条件;

举例:



                                             
            大顶堆逻辑结构;           



堆一般用数组存储,一般节点i的父节点下标为(i - 1) / 2; 左孩子节点下标为(2*i + 1),右孩子节点下标为(2*i + 2);

堆排序思想:

    利用大顶堆(小顶堆)堆顶记录的是最大(小)关键字这一特性,我们只需逐渐从无序序列中选择最大(小值);

       其基本思想(大顶堆):

        1)将初始待排关键字序列{K1 , K2 , K3 ,......Kn}构建为大顶堆,此时此堆为初始的无序区;

        2)将堆顶元素K[1] 与 最后一个元素K
交换,此时形成新的无序区{K1 , K2 , K3 ,......Kn-1} 和新的有序区{Kn}  , 并且数组满足K[1 , 2 , 3 .....n-1] <= K
;

        3) 由于交换后新的堆顶K[1]可能违反大顶堆的性质,故需要对当前的无序序列{K1 , K2 , K3 ,......Kn-1}重新调整,然后再次将新的堆顶K[1] 与最后一个元素交换;

              得到新的无序区{K1 , K2 , K3 ,......Kn-2} 和有序区{Kn-1 , Kn},重复上述操作,直至有序区的个数满足n-1;

堆排序操作重点:构建初始堆 和 调整交换后的堆;

其实构建初始堆也是一个调整的过程,只不过调整对象皆为非叶子节点;

下面将举例:

给定数组data[] = {23 , 14 , 5 , 34 , 56 , 89};

根据给定数组构建完全二叉树:



如上述(1)--(4),展现了如何初始构建大顶堆的;其中的步骤(4),在将(89 ,23)互相交换后,因为(23 > 5)子树根节点23,满足大顶堆性质故不再对子树进行调整;

即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。有了初始堆之后就可以进行排序了。

再初始构建完毕后,进入调整排序操作

将堆顶元素与堆末尾元素进行交换,









代码实现:

#include<iostream>
using  namespace std;
//-------------------------------------------------------堆排序 --------------------------------------------------------------------------
void MaxHeapAdjust(int* data , int i , int Length);

/*
**data:被调整的数组data
**dLength:数组长度
**函数功能:将数组data调整为排序数组
*/
void heapSort(int* data , int dLength)
{
if(data == NULL || dLength <= 0)
return;

//初始构建大顶堆
for(int i = (dLength - 1) / 2; i >= 0; i--)
MaxHeapAdjust(data , i , dLength);

for(int i = (dLength - 1); i > 0; i--)
{
//交换首尾元素
swap(data[i] , data[0]);
//去除最后元素,对剩余新的堆进行调整,以便满足大顶堆性质
MaxHeapAdjust(data , 0 , i - 1);
}
}

/*
***int i :子树的根节点
***int Length:子树的长度
***函数功能:将data[i .....Length]调整为大顶堆
*/
void MaxHeapAdjust(int* data , int i , int Length)
{
int temp = data[i];
//以节点i为根的子树,child指向左子节点
int child;
for( child = 2*i ; child <= Length; child *= 2)
{
//若子节点值 < 右节点值,child指向右节点
if(child < Length && data[child] < data[child +1])
child++;
if(temp >= data[child])
break;

data[i] = data[child];
//此时i指向了较大的孩子节点
i = child;
}

data[i] = temp;
}

int main()
{
int data[] = {5 , 1 , 4, 3 };
int dLength = sizeof(data) / sizeof(int);
cout<<"初始数组:";
for(int i = 0; i < dLength; i++)
cout<<data[i]<<'\t';
cout<<'\n';
cout<<"排序数组:";
heapSort(data , dLength);
for(int i = 0; i < dLength; i++)
cout<<data[i]<<'\t';
cout<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: