声明:
数据结构和功能函数如之前博客所述,如有疑问,详见系列博客
版权所有,如果转载,请注明出处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 只是其中有些并没有考虑到扩展性,例如自定义的队列数组的大小,这个在实践中最好是不要用的额,因为很容易有各种问题(空间不足或者资源浪费)。