天天看点

pat1123 Is It a Complete AVL Tree,平衡二叉树的建立,完全二叉树判断,层序

本题解决两个问题,一个是建立平衡二叉树,第二个数判断此二叉树是否为完全二叉树。至于二叉树的层序遍历,可以采用经典的方法,以队列为辅助数据结构,层序输出结点,并且正好可以在这一步里来解决“完全二叉树”的判定。

AVL Tree首先是一棵BST(Binary Search Tree),它的定义是递归的。除了满足左子树上的结点都小于根结点,右子树上的结点都大于根结点之外,还应保证左右子树的树高之差的绝对值不能超过1。

具体定义可以参考《数据结构》课本,总体感觉AVL Tree是一棵特殊的BST,它的树形更规范,比较对称,不会出现“一头沉”的状况。

众所周知,BST在二分查找的时候应用广泛,但是如果BST的树形很差,退化成只有左子树或右子树,就变成单链表了,这时的查找就退化成线性的了。为了弥补这种残缺,才出现了AVL Tree,AVL Tree可以保证不会出现那种退化的现象。

但是需要付出代价,就是树形不好时,要做出调整,具体调整策略被概括为4种,LL,LR,RR,RL。

建立AVL Tree的主要函数是createAVL,函数的结尾调用adjustAVL来调整树。

再说判断Complete Binary Tree,只需要在经典层序levelOrder的代码里面设置两个bool型变量,emptyNode记录第一个出现的空孩子,ret用来记录空孩子后是否还有孩子。

本代码亮点:

第一:node结点里面定义level变量,将树的高度信息分散在结点里,省去了getLevel递归调用对时间的消耗。属于“以土地换和平”的策略,

第二:node结点定义了析构函数,在程序结尾释放内存。可以采用crtdbg的方法来查看内存是否泄漏。相关代码在注释行,需要在debug模式下运行。

#include<algorithm>
using std::max;
#include<queue>
using std::queue;
#include<vector>
using std::vector;
#include<stdio.h>

struct node{
	int val;
	node *lch = NULL, *rch = NULL;
	int level;
	node(int val_ = -1) :val(val_), level(1) {}
	~node() {
		if (lch) {
			delete lch;
			lch = NULL;
		}//if
		if (rch) {
			delete rch;
			rch = NULL;
		}//if
	}//~node
};//node
int getLevel(node*root) {
	return root == NULL ? 0 : root->level;
}//getLevel
node *LL(node*root) {
	node *ret = root->lch;
	root->lch = ret->rch;
	ret->rch = root;
	root->level = max(getLevel(root->lch), getLevel(root->rch)) + 1;
	ret->level = max(getLevel(ret->lch), getLevel(ret->rch)) + 1;
	return ret;
}//LL
node *RR(node*root) {
	node *ret = root->rch;
	root->rch = ret->lch;
	ret->lch = root;
	root->level = max(getLevel(root->lch), getLevel(root->rch)) + 1;
	ret->level = max(getLevel(ret->lch), getLevel(ret->rch)) + 1;
	return ret;
}//RR
node *LR(node*root) {
	node *child = root->lch;
	root->lch = RR(child);
	return  LL(root);
}//LR
node *RL(node*root) {
	node *child = root->rch;
	root->rch = LL(child);
	return RR(root);
}//RL
node* adjustAVL(node*root) {
	int diff = getLevel(root->lch) - getLevel(root->rch);
	if (diff >= -1 && diff <= 1)return root;
	int diff2(0);
		if (diff > 1) diff2 = getLevel(root->lch->lch) - getLevel(root->lch->rch);
		else if(diff < -1) diff2 = getLevel(root->rch->lch) - getLevel(root->rch->rch);
		if (diff == 2) {
			if (diff2 == 1)root = LL(root);
			else if(diff2 == -1) root = LR(root);
		}
		else if(diff == -2) {
		if (diff2 == -1)root = RR(root);
		else if(diff2 == 1) root = RL(root);
	}//if else if
	return root;
}//adjustOfAVL
node* createAVL(node*root, const int&tmpI) {
	if (root == NULL)root = new node(tmpI);
	else if(tmpI<root->val)root->lch = createAVL(root->lch, tmpI);
	else if(tmpI>root->val)root->rch = createAVL(root->rch, tmpI);
	root->level = max(getLevel(root->lch), getLevel(root->rch)) + 1;
	root = adjustAVL(root);
	return root;
}//createAVL
bool levelOrder(const node * const root, vector<int>&order) {
	queue<const node*> que;
	que.push(root);
	bool ret(true);
	bool emptyNode(false);
	while (que.empty() == false) {
		const node *cur = que.front();
		que.pop();
		order.push_back(cur->val);
		if (cur->lch) {
			que.push(cur->lch);
			if (emptyNode == true)ret = false;
		}
		else emptyNode = true;
		if (cur->rch) {
			que.push(cur->rch);
			if (emptyNode == true)ret = false;
		}
		else emptyNode = true;
	}//while
	return ret;
}//levelOrder
void printVec(const vector<int> &order) {
	int size = (int)order.size();
	for (int i(0); i < size - 1; ++i)printf("%d ", order[i]);
	printf("%d\n", order[size - 1]);
	return;
}//printVec

//#include<crtdbg.h>
//#defineCRTDBG_MAP_ALLOC
int main() {
	//_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF| _CRTDBG_LEAK_CHECK_DF);
#ifdef ONLINE_JUDGE
#else
	freopen("in.txt", "r", stdin);
#endif
	int N(-1);
	scanf("%d", &N);
	node *root(NULL);
	for (int i(0); i < N; ++i) {
		int tmpI(-1);
		scanf("%d", &tmpI);
		root = createAVL(root, tmpI);
	}//for i
	vector<int> order;
	bool flag = levelOrder(root, order);
	printVec(order);
	flag ? puts("YES") : puts("NO");
	if (root) {
		delete root;
		root = NULL;
	}//if
	return 0;
}//main
           

深入了解AVL Tree的调整策略,做了一个尝试。

对于LR和RL,我用自己的实现,不需要借助LL和RR。

如果按照我下面这个实现,还需要更新三个结点的level。

结果和借助LR和RL的一样。

但效率会比它高。

简单分析一下就能明白,他们需要做4次结点level的更新,其中有一个结点被做了两次。

我只需要做3次更新。

细节:LR里面的三个结点的更新顺序一定root,tmpLch,ret。因为在新树的结构下,ret的左孩子是tmpLch,右孩子是root,只有孩子们先更新完了level,母亲才能更新level。

顺便一提,LL里面的root和ret的更新顺序,也是root在前,ret在后。

node *LR(node *root) {

   node *ret= root->lch->rch;

   node*tmpLch = root->lch;

   tmpLch->rch= ret->lch;

   root->lch= ret->rch;

   ret->rch= root;

   ret->lch= tmpLch;

   root->level= max(getLevel(root->lch), getLevel(root->rch)) + 1;

   tmpLch->level= max(getLevel(tmpLch->lch), getLevel(tmpLch->rch)) + 1;

   ret->level= max(getLevel(ret->lch), getLevel(ret->rch)) + 1;

   returnret;

}//LR

node *RL(node *root) {

   node *ret= root->rch->lch;

   node*tmpRch = root->rch;

   tmpRch->lch= ret->rch;

   root->rch= ret->lch;

   ret->lch= root;

   ret->rch= tmpRch;

   root->level= max(getLevel(root->rch), getLevel(root->lch)) + 1;

   tmpRch->level= max(getLevel(tmpRch->rch), getLevel(tmpRch->lch)) + 1;

   ret->level= max(getLevel(ret->rch), getLevel(ret->lch)) + 1;

   returnret;

}//RL

Gentle Dong,Second Version,20170224



继续阅读