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