1123. Is It a Complete AVL Tree (30)

1123. Is It a Complete AVL Tree (30)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property. Figures 1-4 illustrate the rotation rules.

    

    

Now given a sequence of insertions, you are supposed to output the level-order traversal sequence of the resulting AVL tree, and to tell if it is a complete binary tree.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (<= 20). Then N distinct integer keys are given in the next line. All the numbers in a line are separated by a space.

Output Specification:

For each test case, insert the keys one by one into an initially empty AVL tree. Then first print in a line the level-order traversal sequence of the resulting AVL tree. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line. Then in the next line, print "YES" if the tree is complete, or "NO" if not.

Sample Input 1:
5
88 70 61 63 65
Sample Output 1:
70 63 88 61 65
YES
Sample Input 2:
8
88 70 61 96 120 90 65 68
Sample Output 2:
88 65 96 61 70 90 120 68
NO


题意:输入n个数据,要求创建avl平衡树,再层序输出并判断是否是完全二叉树

分析:我觉得层序输出问题不大,主要在于avl的创建和判断完全二叉树

  avl的创建分为两步 1.插入 2.平衡

          插入比较好做,根据值的大小左右走,走到空的时候开始创建节点

          插入节点可能会导致失衡,而平衡的方式根据插入节点与被破坏的节点的位置关系分为四种情况

           1.插入点是被破坏点左孩子的左孩子,如题目中Figure1,这时候需要左旋,即把88节点顺时针旋转成70节点的右孩子。(LL)

                2.插入点是被破坏点右孩子的右孩子,如题目中Figure2,这时候需要右旋,即把88节点逆时针旋转成96节点的左孩子(RR)

                3.插入点是被破坏点右孩子的左孩子,如题目中Figure3,你会发现这个似乎有点不一样,插入节点和被破坏的节点中间隔了两个,这时候怎么旋转呢?(RL)    RL是LL与RR之和,先对被破坏点的右孩子(96)进行LL,在对被破坏点(70)进行RR.

Figure的中间图应该是这样的

可以看出旋转的时候,88的子节点发生了变化,具体下面讲

                  4.插入点是被破坏点左孩子的右孩子,如题目中Figure4(LR),先对61-65做RR旋转,在对70-70的左孩子(旋转后为65) 做左旋

 再对LL做一个解释,以Figure1 的88-70-61为例,此时的头为88,我们希望旋转后的头的70,并且70的左为61,右为88。 我们假设此时的70节点有值为33的右孩子,那么88占了70的右子树以后,33该往哪放呢?   显而易见,88的左子树。 因为原先88的左孩子是70,旋转以后左孩子就会空出来,那么就把88所占去的33放在空出来的这里。

示例代码:

struct node* LL(struct node *root) {
	struct node *p = root->lchild;//p=70,root=88
	root->lchild = p->rchild;//88的左孩子=33
	p->rchild = root;//70的右孩子=88
	root->high = max(get_high(root->lchild), get_high(root->rchild)) + 1;
	p->high = max(get_high(p->lchild), root->high) + 1;//重新计算高度
	return p;
}
struct node *RL(struct node *root) {
	root->rchild = LL(root->rchild);
	return RR(root);
}
         RL,LR就是 RR ,LL 的组合。

        接下来是完全二叉树的判断,
        完全二叉树怎么讲呢,比起满二叉树的完整金字塔,完全二叉树允许金字塔底部挖空,并且空之前必须全部填满,空之后必须全部为空。“偷”了一张百度的图片,图1是满二叉树,图2、3是完全二叉树,图4 、5就不是完全二叉树。   所以,假设一颗完全二叉树有n个顶点,如果第x个顶点有左子树,并且x*2>n,那么我们可以判断出它不是完全二叉树。同理,如果第x个顶点有右子树,并且x*2+1>n,那么我们可以判断它不是完全二叉树。



代码:

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
struct node {
	int data;
	int high;
	struct node *lchild, *rchild;
};
int judge = 1, cnt = 0, n;
void level_out(struct node root) {
	queue<struct node> q;
	q.push(root);
	while (q.size()) {
		if (q.front().lchild) q.push(*q.front().lchild);
		if (q.front().rchild) q.push(*q.front().rchild);
		if (q.front().lchild)
			if ((cnt + 1) * 2 > n) judge = 0;
		if (q.front().rchild)//判断是否为 完全二叉树
			if ((cnt + 1) * 2 + 1 > n) judge = 0;
		cnt == 0 ? cout << q.front().data : cout << " " << q.front().data;
		q.pop();
		cnt++;
	}
	cout << endl;
	judge == 1 ? cout << "YES" : cout << "NO";
}
int get_high(struct node *root) {
	if (root) return max(get_high(root->lchild), get_high(root->rchild)) + 1;
	else return 0;
}
struct node* LL(struct node *root) {
	struct node *p = root->lchild;
	root->lchild = p->rchild;
	p->rchild = root;
	root->high = max(get_high(root->lchild), get_high(root->rchild)) + 1;
	p->high = max(get_high(p->lchild), root->high) + 1;
	return p;
}
struct node *RR(struct node *root) {
	struct node *p = root->rchild;
	root->rchild = p->lchild;
	p->lchild = root;
	root->high = max(get_high(root->lchild), get_high(root->rchild)) + 1;
	p->high = max(root->high, get_high(p->rchild)) + 1;
	return p;
}
struct node *RL(struct node *root) {
	root->rchild = LL(root->rchild);
	return RR(root);
}
struct node *LR(struct node *root) {
	root->lchild = RR(root->lchild);
	return LL(root);
}
struct node* insert(struct node* root, int data) {
	if (!root)
	{
		root = new struct node();//使用默认的构造函数
		root->data = data;
	}
	else if (data < root->data)
	{
		root->lchild = insert(root->lchild, data);
		if (get_high(root->lchild) - get_high(root->rchild) == 2) //失衡 
			if (data < root->lchild->data) root = LL(root);
			else root = LR(root);
	}
	else if (data >root->data)
	{
		root->rchild = insert(root->rchild, data);
		if (get_high(root->rchild) - get_high(root->lchild) == 2)
			if (data > root->rchild->data) root = RR(root);
			else root = RL(root);
	}
	root->high = max(get_high(root->lchild), get_high(root->lchild)) + 1;
	return root;
}
int main()
{
	int tmp;
	cin >> n;
	struct node *root = NULL;
	for (int i = 0; i<n; i++) {
		cin >> tmp;
		root = insert(root, tmp);
	}
	level_out(*root);
}

转载于:https://www.cnblogs.com/childwang/p/8280267.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值