您的位置:首页 > 其它

二叉树:层次遍历和应用

2016-03-19 19:12 309 查看

声明:

数据结构和功能函数如之前博客所述,如有疑问,详见系列博客

版权所有,如果转载,请注明出处http://blog.csdn.net/tubin100

一 层次遍历

层次遍历:需要队列作为数据结构

代码如下:

void LevelOrder(BiTree T) {
if (nullptr == T) {
Error();
return;
}

std::queue<BiTNode*> q;
BiTNode *p = T;

q.push(p);
while (!q.empty()) {
p = q.front();
q.pop();

visit(p);

if (p->lChild) {
q.push(p->lChild);
}
if (p->rChild) {
q.push(p->rChild);
}
}
}


二 层次遍历应用

1 从下到上,从右到左遍历二叉树

思路分析

层次遍历每一个节点,每次出队列同时,进入另一个栈,遍历结束后,顺序弹出栈中内容即可。

void LevelOrder_Invert(BiTree T) {
if (nullptr == T) {
Error();
return;
}

std::queue<BiTNode*> q;
std::stack<BiTNode*> st;
BiTNode *p = T;

q.push(p);
while (!q.empty()) {
p = q.front();
q.pop();

st.push(p);

if (p->lChild) {
q.push(p->lChild);
}
if (p->rChild) {
q.push(p->rChild);
}
}

while (!st.empty()) {
std::cout << (st.top())->data << " ";
st.pop();
}
}


2 判断是否为完全二叉树

思路分析

a 层次遍历二叉树,不判断孩子节点是否为空,直接入队。

b 每次出队时,判断是否为空,

c 一旦有一个节点为空,则根据完全二叉树的性质,队列中若还有元素,则全部应该为空;

d 若出现一个节点不为空,则立即判断不是完全二叉树;

bool IsCompleteTree(BiTree T) {
if (nullptr == T) {
Error();
return false;
}

std::queue<BiTNode*> q;
BiTNode *p = T;
q.push(p);

while (!q.empty()) {
p = q.front();
q.pop();

if (p) {
q.push(p->lChild);
q.push(p->rChild);
} else {
while (!q.empty()) {
p = q.front();
q.pop();

if (nullptr != p) {
return false;
}
}
}
}

return true;
}


3 删除以x为根节点的子树

3.1 递归删除二叉树

首先引入,递归删除二叉树的代码。

void DeleteTree(BiTNode* T) {
if (T) {
DeleteTree(T->lChild);
DeleteTree(T->rChild);
delete T;
}
}


3.2 删除以x为根节点的子树

为什么要用层次遍历?

因为删除一个以x为根的节点需要设置x的父节点的孩子域,所以得需要明确父子关系,层次遍历是简单的选择。

思路分析:

层次遍历二叉树,查找以x为儿子的节点,如果找到了,删除该子树,并设置孩子域为空。

代码如下:

bool DeleteXTree(BiTree T, BTElemType x) {
if (nullptr == T) {
Error();
return false;
}

std::queue<BiTNode*> q;
BiTNode *p = T;
q.push(p);

while (!q.empty()) {
p = q.front();
q.pop();

if (p->lChild && p->lChild->data == x) {
DeleteTree(p->lChild);
p->lChild = nullptr;
return true;
}
if (p->rChild && p->rChild->data == x) {
DeleteTree(p->rChild);
p->rChild = nullptr;
return true;
}
}
return false;
}


4 非递归算法求解树的高度

思路分析

a 用一个last指针指向每一层节点的最后一个元素

b 元素正常入队。

c 每次出队时,front指针会自增,每当front指针 ‘赶上’ 了 last指针时,说明此时一层已经完全出队。此时设置last指针指向rear指针,并且level自增

代码如下:

int BiTreeHeight_non_recur(BiTree T) {
if (!T) {
return 0;
}

//模拟队列
int front, rear;
BiTNode* q[50];    //假设元素个数小于50
front = rear = -1;

int level = 0;  //标识层次
int last = 0;   //标识当前层中最右端节点

BiTNode *p = T;
++rear;
q[rear] = p;

while (front < rear) {
++front;
p = q[front];

if (p->lChild) {
++rear;
q[rear] = p->lChild;
}
if (p->rChild) {
++rear;
q[rear] = p->rChild;
}

//关键点
if (front == last) {  //front 赶上了 last
++level;
last = rear;    //重新指向当前层的最右端节点
}
}

return level;
}


5 求二叉树宽度

思路分析:

a 定义结构体如下:

typedef struct WidthNode {
BiTNode *pointer;
int level;
}WidthNode;


b 每个节点都有层号。

设置根节点的层号为1,通过遍历的过程给每个节点设置层号,可以得到一组带有层号的节点队列。

c 层号出现次数最多,则出现的次数即为二叉树的宽度。

d 通过遍历该队列,将层号映射到数组中,通过一次遍历数组即可求得层号出现最多的次数。

代码如下:

int BiTreeWidth(BiTree T) {
if (!T) {
return 0;
}

WidthNode q[50];
int front, rear;
front = rear = -1;

WidthNode p;
p.pointer = T;
p.level = 1;    //根节点的层号为1

++rear;
q[rear] = p;

WidthNode temp;

while (front < rear) {
++front;
p = q[front];

if (p.pointer->lChild) {
temp.pointer = p.pointer->lChild;
temp.level = p.level + 1;

++rear;
q[rear] = temp;
}
if (p.pointer->rChild) {
temp.pointer = p.pointer->rChild;
temp.level = p.level + 1;

++rear;
q[rear] = temp;
}
}

//假设树高不超过10
//在一组数据中选择一个出现次数最大的数----
//数据的特点:递增,且范围在1-height之间,
//思路:把数据映射到数组中
int a[11] = {0};
for (int i = 0; i <= rear; ++i) {
++a[q[i].level];
}

int width = -1;
for (int i = 0; i < 11; i++) {
if (a[i] > width) {
width = a[i];
}
}

return width;
}


6 总结

1 在熟练掌握了层次遍历的基本框架之后,对于一些简单的应用还是比较熟练的用代码实现。

2 只是其中有些并没有考虑到扩展性,例如自定义的队列数组的大小,这个在实践中最好是不要用的额,因为很容易有各种问题(空间不足或者资源浪费)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息