将完全二叉树调整成堆
1. 首先,把完全二叉树存入队列中。
问题:这里为什么用队列,不用栈?
解答:因为是从根开始的,要按层遍历(逆着)取出来非叶子结点,然后这些结点和自己的子女进行比较调整。
结果:
1.所有元素都入队了
2.rear在最后一个元素上指着
3.front在第一个叶子节点上指着
4.end作为岗哨,为了最后输出排好的队列,因为后面删结点会用rear- - ,所以需要提前end = rear.
2. 开始转换堆
关键问题:
- 怎么找最小交换?
解:
首先,要清楚交换的是值 data ,不是结点。
其次,如果此结点要去和子女比较,那么它必定有左子女,而不一定有右子女。所以,比较的时候,直接和左子女比较值。然后判断是否有右子女,再考虑是否交换。
再者,需要标记是否进行过交换,如果这一次所有的非叶子结点都已经放好了,没有一个和子女交换,这次算是做完了,即找到了一个最小值。 - 删除结点时注意什么?
解:
如果即将砍的是左子女,需要不仅要rear- - 和 Q[front - 1]->left = NULL,还需要 front- - 。因为砍去了左子女,这个结点就是叶子结点了,下一循环不应该去和子女比较交换值。
如果砍去的是右子女,则只需要rear- - 和 Q[front - 1] -> right = NULL。
3. 代码
# include <stdio.h>
# include <stdlib.h>
// 定义结构体
typedef struct node {
int data;
struct node * left;
struct node * right;
}BTNode;
// 函数声明:
BTNode * CreatTree(int a[], int n);
void order(BTNode * root, int n);
int main (void){
int a[] = {3,2,13,8,12,7,9,1,6,11,4,5}; // 完全二叉树的结点值数组
int n = 12; // 数组中数的个数
BTNode * root; // 完全二叉树根结点
root = CreatTree(a,n); // 创建完全二叉树,并返回根结点
//堆排序,并输出排序后的结果(小根堆)
order(root, n);
return 0;
}
BTNode * CreatTree(int a[], int n){
BTNode * root;// 根结点
BTNode * p; // 新建的结点
BTNode * pa; // 双亲结点
int front = 0; // 队列的头
int rear = 0; // 队列的尾
// 创建队列
BTNode ** Q = (BTNode **)malloc((2*n)*sizeof(BTNode *));
// 创建根结点
root = (BTNode *)malloc(sizeof(BTNode));
root->data = a[0];
root->left = root->right = NULL;
Q[++rear] = root; // 根结点入队
// 创建其他结点(每次只创建一个)
int i ;
for(i = 1; i < n; i++){
pa = Q[front+1]; // 将双亲结点找到,即队首。
p = (BTNode *) malloc (sizeof(BTNode));
p->data = a[i];
p->left = p->right = NULL;
// 先往左放,再往右放
if(!(pa->left)){
pa->left = p;
}else {
pa->right = p;
pa = Q[front++];// 双亲出队
}
Q[++rear] = p; // 入队
}
free(Q);
return root;
}
void order(BTNode * root,int n){
/* 第一步:将完全二叉树存储在队列里面,并且标记相应的值 */
// 初始化队列
BTNode ** Q = (BTNode **)malloc(n * sizeof(BTNode *)); // 声明队列
int front = 0;
int rear = 0;
int end = 0;
// 根入队
Q[++rear] = root;
// 其他入队
BTNode * pa;
while(1){
pa = Q[++front]; // 取出队首
// 查找是否有左右子女(如果是叶子结点,则循环停止)
if(pa->left==NULL && pa->right==NULL) break;
if(pa->left) Q[++rear] = pa->left;
if(pa->right) Q[++rear] = pa->right;
}
end = rear; // end 是岗哨,为了最后输出排好序的结果,标志最后一个数的位置
/*第二步:排序*/
int tag, k;
while(front != 1){
while(1){
tag = 1; // 标志有无交换此次
for(k = front-1; k > 0; k--){ // 所有非叶子结点和自己的双亲进行比较,双亲中保存最小值
BTNode * p = Q[k]; // 取出双亲
BTNode * pmin = p; // pmin 中保存最小值结点
if((pmin -> data) > (p->left->data)) // 一定存在左子女,所以直接和左子女比较
pmin = p->left;
if(p->right) // 判断是否存在右子女
{
if(pmin->data > p->right->data) //和右子女进行比较
pmin = p->right;
}
if(p != pmin) // 判断是否需要交换
{
tag = 0; // 交换了,修改标记状态
int temp = p->data;
p->data = pmin->data;
pmin->data = temp;
}
}
if(tag) break; //如果没有进行交换,则此次已经调整好。根放的就是最小值
}
// 把根的值和最后一个叶子的值进行交换
int temp = root->data;
root->data = Q[rear]->data;
Q[rear]->data = temp;
// 判断即将删去的最后一个结点,是左子女还是右子女
if(Q[front-1]->left == Q[rear])
{
Q[front-1]->left = NULL;
front--; //删除左子女时,此结点也就不再非叶子节点队列中,下次就不用去调整这个了
}else{
Q[front-1]->right = NULL;
}
rear--; // 最后一个结点的位置也变了
}
/*第三步:输出队列*/
int i;
for(i = end; i > 0; i--){
printf("%d ",Q[i]->data);
}
}
4. 结果展示