堆是一种完全二叉树,有最小堆和最大堆之分,最小堆是指根节点的值一定小于左子树和右子树所有元素的值,最大堆则相反(当你从小到大排序时, 可以选择最小堆反之,则选择最大堆)
1.如何建立一个最小堆呢:由于堆是一个完全二叉树,所以满足以下关系(我们将元素的顺序从0开始排,第i个节点称之为Ki, 那么元素就有K0, K1, K2…),Ki的父节点一定是K(i-1)/2, Ki的左孩子节点一定是Ki2+1, 右孩子节点一定是Ki2+2。因此我们可以不用向普通二叉树那样,用指针来访问其左孩子节点和右孩子节点(本次程序中是以指针来访问的,虽然有点多此一举,就当锻炼一下自己使用指针的能力吧)。然而最重要的问题是,假如我们一开始面临的是一个堆,我们又如何将其变成一个最小堆呢,那就需要我们进行向上调整(后面会具体讲)
2.如何在一个最小堆中插入一个元素呢: 为了不破坏堆已有的结构,在堆尾部插入一个元素后,会破坏堆的结构,如何进行调整呢?可以采用向上调整
3.如何删除一个元素呢:堆就像是一个队列,在尾部插入,在头部删除,再删除掉整个树的根节点后,会破坏堆的结构,为了有效利用堆已有的结构,将堆尾部的元素移到根节点位置,然后进行向下调整(后面会讲向下调整)
4.向下调整:将尾部节点移到根节点位置,若该节点数值小于左孩子节点和有孩子节点数值,就无需调整,否则就需将数值最小的孩子节点与根节点互换元素值,然后对数值发生改变的孩子节点进行同样的调整。(堆的优异性就体现在这里,对于数值没有发生改变的孩子节点就无需进行调整,这很可能一下子跳过了一大堆节点,所以二叉树的时间复杂性与数的高度有关)
5.向上调整:在尾部插入一个元素后,将该元素与其父节点比,若父节点的值大于该孩子节点,则进行互换元素值,对于堆来说,假如有n个节点,则有n / 2个节点是有孩子节点的,则K0至Kn/2都需要进行调整,注意是从Kn/2至K0的顺序来依此调整的,从树的结构来看就是先上的,同时当改变孩子节点元素值是,若该孩子节点同时也有孩子时要进行向下调整,由于堆无非是根,左孩子和右孩子,可以通过递归来实现,所以代码不是太复杂
关于调整的总结:我们可以发现向上调整和向下调整大致思路是一样的,都是比较根节点与其孩子节点数值的大小以保证最小堆(或最大堆)的要求,所不同的是向上调整与向上调整的调整方向是相反的,向上调整是在尾部插入元素要进行的调整,向下调整是在删除根节点并将尾部节点补为根节点后要进行的调整
6.堆的一个应用就是进行排序:由于根节点的元素值是整个树的最小的,我们可以建立一个堆后,删除该根节点,将余下的节点进行向下调整,得到新的堆,重新进行上述步骤。就可以得到从小到大排列的结果了(从大到小只需建立最大堆即可)。
看到网上经典的堆排序代码,只有不到一百行,不得不反思一下自己的程序为何这么冗长:
(1).堆本可以映射到一个一维数组,无需像普通二叉树那样采用指针来访问根节点,本程序既用指针又用数组(用数组是完全二叉树满足的关系(1中所说)对编程很有用)
(2).向上调整和向下调整其本质差不多,可以合并,这里写成两个接口是为了插入,删除时调用方便,如果只是排序,则可不必这么写
(3).加了一些测试代码时用到的借口(谁让自己菜呢,写程序总有bug),如show(),getlength()等;
(4).不得不感叹经典就是经典,编程方面还得加油啊
#include <stdio.h>
#include <stdlib.h>
#define SIZE 20
typedef int ElemType;
typedef struct treenode
{
ElemType data;
struct treenode * left;
struct treenode * right;
}Node, *pNode;
void show(pNode * tree)
{
if (tree == NULL)
return ;
int i;
for (i = 0; tree[i] != NULL; i++)
printf("%-3d", tree[i]->data);
printf("\n");
}
void getlength(pNode * tree)
{
if (tree == NULL)
printf("get length failure\n");;
int i;
for (i = 0; tree[i] != NULL; i++) ;
printf("tree has %d nodes\n", i);
}
pNode * CreateTree(ElemType *e, int length)
{
if (e == NULL || length <= 0 || length > SIZE)
return ;
pNode *str = (pNode *)malloc(sizeof(pNode) * SIZE);
int i, j;
for ( i = 0; i < length; i++)
{
str[i] = (pNode)malloc(sizeof(Node) * 1);
if (str[i] == NULL)
{
for (j = 0; j < i; j++)
{
free(str[j]);
}
free(str);
printf("堆建立失败\n");
return NULL;
}
else
str[i]->data = e[i];
}
for (i = 0; i < length; i++)
{
if (2 * i + 1 < length)
str[i]->left = str[2 * i + 1];
else
str[i]->left = NULL;
if (2 * i + 2 < length)
str[i]->right = str[2 * i + 2];
else
str[i]->right = NULL;
}
return str;
}
void ExchangeData(ElemType *a, ElemType * b)
{
if (a == NULL || b == NULL)
return;
printf("exchange %d and %d\n", *a, *b);
ElemType temp;
temp = *a;
*a = *b;
*b = temp;
}
void SiftUp(pNode root)
{
if (root == NULL || (root->left == NULL && root->right == NULL))
return;
ElemType e;
int c;
if (root->left == NULL)
{
c = 0;
e = root->right->data;
}
else if(root->right == NULL)
{
c = 1;
e = root->left->data;
}
else
{
c = root->left->data > root->right->data? 0 : 1;
e = root->left->data > root->right->data? root->right->data : root->left->data;
}
if (root->data > e)
{
if (c == 0)
{
ExchangeData(&(root->data), &(root->right->data));
SiftUp(root->right);
}
else if (c == 1)
{
ExchangeData(&(root->data), &(root->left->data));
SiftUp(root->left);
}
}
}
void SiftDown(pNode root)
{
if (root == NULL || (root->left == NULL && root->right == NULL))
return;
ElemType e;
int c;
if (root->left == NULL)
{
c = 0;
e = root->right->data;
}
else if(root->right == NULL)
{
c = 1;
e = root->left->data;
}
else
{
c = root->left->data > root->right->data ? 0 : 1;
e = root->left->data > root->right->data? root->right->data : root->left->data;
}
if (root->data > e)
{
if (c == 0)
{
ExchangeData(&(root->data), &(root->right->data));
SiftDown(root->right);
}
else if (c == 1)
{
ExchangeData(&(root->data), &(root->left->data));
SiftDown(root->left);
}
}
}
void Delete(pNode * tree)
{
if (tree == NULL || tree[0] == NULL)
return;
int i;
for (i = 0; tree[i] != NULL; i++) ;
i--;
if (i == 0)
{
free(tree[0]);
return;
}
tree[0]->data = tree[i]->data;
free(tree[i]);
tree[i] = NULL;
if (i % 2 == 1)
tree[(i - 1) / 2]->left = NULL;
else
tree[(i - 1) / 2]->right = NULL;
SiftDown(tree[0]);
}
void Insert(pNode *tree, ElemType e)
{
if (tree == NULL)
{
printf("insert failure\n");
return ;
}
int i, j;
for ( i = 0; tree[i] != NULL; i++);
if (i > SIZE)
{
printf("insert failure\n");
return;
}
printf("i = %d\n", i);
tree[i] = (pNode)malloc(sizeof(Node) * 1);
if (tree[i] == NULL)
{
printf("insert failure\n");
return;
}
tree[i]->data = e;
tree[i]->left = NULL;
tree[i]->right = NULL;
if (i % 2 == 1)
tree[(i - 1) / 2]->left = tree[i];
else
tree[(i - 1) / 2]->right = tree[i];
for (j = (i + 1) / 2 - 1; j >= 0; j--)
SiftUp(tree[j]);
}
int main()
{
ElemType res[SIZE];
int i;
ElemType e;
ElemType a[SIZE] = {2, 5, 6, 23, 45, 55, 67, 32, 56, 11, 1};
/* printf("please input 10 numbers\n");
for (i = 0; i < 10; i++)
scanf("%d", a + i);
*/
pNode * tree = CreateTree(a, 10);
if (tree == NULL)
{
printf("create tree failure\n");
exit(1);
}
for (i = 10 / 2 - 1; i >= 0; i--)
SiftUp(tree[i]);
show(tree);
for(i = 0; i < 5; i++)
{
printf("please input the num which you want to insert\n");
scanf("%d", &e);
Insert(tree, e);
show(tree);
}