堆在逻辑结构上是顺序存储的完全二叉树,左孩子2i,右孩子2i+1,付结点是i/2。
大根堆,根>左右。小根堆,左右>根。
算法思想:
建堆:比如构造大根堆,就是将所有的非叶子结点(编号<=n/2)下坠做调整。小元素下坠与更大的结点交换。
排序: 将堆顶元素加入有序序列(堆顶元素与堆底元素互换),堆底元素到堆顶后,需要下坠调整,恢复成大根堆。上述过程重复n-1次。
空间复杂度:O(1)
时间复杂度:O(nlogn)
稳定性:不稳定
基于大根堆是递增序列,基于小根堆是递减序列。
建立堆
//排序
void HeadAdjust(int A[], int k, int len) //k是当前要处理的一个非叶结点
{
count++;
int i, j, max;
A[0] = A[k];//记录被调整的根结点
for (i = 2 * k; i <= len; i *= 2)//向自己的左右儿子遍历
{
if (i < len && A[i] < A[i + 1]) //比较左右儿子谁更大
{
i++;
}
if (A[0] >= A[i])break;//选出更大的与根节点比较
else
{
A[k] = A[i];//若大于根节点,则将交换儿子与根节点的值
k = i;//并且将此次的儿子作为根继续寻找下去
}
}
A[k] = A[0];//将最初根节点放入调整后该放入的位置
printf("第%d次调整后的顺序为:", count);
for (i = 1; i < 9; i++)
{
printf("%d ", A[i]);//从实际数组里第二个元素开始遍历
}
printf("\n");
}
//堆排序
void BulidMaxHeap(int A[], int len)
{
for (int i = len / 2; i > 0; i--)//从后向前调整堆
{
HeadAdjust(A, i, len);
}
}
将表长划分为2,前半部分是非根结点,从后向前将每一个根结点调整好。用A[0]保存当前要调整的根节点,然后比较根结点的两个孩子,选出更大的与跟结点比较,若小于根则直接结束,这也就是为什么从后往前,若从前往后,则根结点的孩子中是否有更大的结点就不知道了。若大于根节点则将孩子的值赋值给根结点,并且将要调整的根换做为这个孩子,然后遍历此孩子的孩子结点。最终确定出来最早的那个根实际要在的地方,将A[0]这个副本赋值过去,完成一次调整。
全部代码
int count=0;
void HeadAdjust(int A[], int k, int len) //k是当前要处理的一个非叶结点
{
count++;
int i, j, max;
A[0] = A[k];//记录被调整的根结点
for (i = 2 * k; i <= len; i *= 2)//向自己的左右儿子遍历
{
if (i < len && A[i] < A[i + 1]) //比较左右儿子谁更大
{
i++;
}
if (A[0] >= A[i])break;//选出更大的与根节点比较
else
{
A[k] = A[i];//若大于根节点,则将交换儿子与根节点的值
k = i;//并且将此次的儿子作为根继续寻找下去
}
}
A[k] = A[0];//将最初根节点放入调整后该放入的位置
printf("第%d次调整后的顺序为:", count);
for (i = 1; i < 9; i++)
{
printf("%d ", A[i]);//从实际数组里第二个元素开始遍历
}
printf("\n");
}
//堆排序
void BulidMaxHeap(int A[], int len)
{
for (int i = len / 2; i > 0; i--)//从后向前调整堆
{
HeadAdjust(A, i, len);
}
}
int main()
{
int i, j, A[9] = { 0,53,17,78,9,45,63,87,32 };
printf("初始数据为:");
for (i = 1; i < 9; i++)
{
printf("%d ", A[i]);//从实际数组里第二个元素开始遍历
}
printf("\n");
BulidMaxHeap(A, 8);
printf("最终序列为:");
for (i = 1; i < 9; i++)
{
printf("%d ", A[i]);//从实际数组里第二个元素开始遍历
}
}