浅谈堆排序
什么是堆
1.堆的特点
(1).堆是一颗完全二叉树
(2).堆的父节点大于左右子节点(大根堆)左右子节点大小无序
const int MaxSize=1000;
int heap[MaxSize]; //下标从1开始
int n; //表示堆中的节点的个数,也是heap数组的长度
2.基本知识
对于一颗用数组(下标从1开始)存储的堆,假设某节点为 heap[ i ]
则父节点为 heap[ i / 2] 左儿子节点为 heap[ i * 2 ] 右儿子节点为 heap[ i * 2 +1]
怎么建堆
1.先介绍几个基本操作
(1)Swap操作
交换下标为 i , j 的节点的值
void Swap(int heap[],int i,int j)
{
int temp=heap[i];
heap[i]=heap[j];
heap[j]=temp;
}
(2)Adjust操作
一个以index为根节点的堆,将index与更大的儿子节点向下交换,直到为叶子节点或者当前节点大于左右儿子节点。
void Adjust(int heap[],int n,int index)
{
while(index<=n)
{
int left=index*2; //左儿子
int right=index*2+1; //右儿子
//当右儿子存在,且最大,根节点与右儿子交换,并且新的根节点是右儿子
if(right<=n&&heap[right]>heap[left]&&heap[right]>heap[index])
{
Swap(heap,index,right);
index=right;
}
//左儿子大于根节点,右儿子不存在或右儿子小于左儿子
else if(left<=n&&heap[left]>heap[index]&&(right>n||heap[left]>heap[right]))
{
Swap(heap,index,left);
index=left;
}
else
break;
}
}
(3)Heap_Insert操作
对于一个已经建好的堆,插入一个值为x的新节点
先将x放在堆的最后新加的一个节点,然后逐层向上比较交换
void Heap_Insert(int heap[],int n,int x)
{
int temp=n+1;
heap[temp]=x;
while(temp>1&&heap[temp]>heap[temp/2]) //当前节点大于父节点 且当前节点不是堆顶 就继续向上交换
{
Swap(heap,temp,temp/2);
temp/=2;
}
}
2.两种建堆的方法
(1)多次调整建堆
从最后一个节点的父节点开始,递减的对每个节点进行一次Adjust
void Build_Heap(int heap[],int n)
for(int i=n/2;i>0;i--)
Adjust(heap,n,i);
}
(2)逐个insert建堆
逐个将后面的节点加入前面已经建好的堆
void Build_Heap(int heap[],int n)
{
for(int i=1;i<n;i++)
Heap_Insert(heap,i,heap[i+1]);
}
给堆排序
我们知道,虽然堆本身有一定的特点,父节点大于子节点,但是从上往下遍历不一定是有序的。
堆排序是对于一个已经建好的堆,进行两步操作
1.交换堆顶节点和最后一个节点,把最后一个节点剔出堆
2.然后新的堆顶元素进行调整
void Heap_sort(int heap[],int n)
{
for(int i=n;i>0;i--)
{
Swap(heap,1,i);
Adjust(heap,i-1,1);
}
}
注意:大根堆排序后是从小到大,小根堆排序后是从大到小