二叉树:层次遍历和应用

来源:互联网 发布:如何查找电脑的mac地址 编辑:程序博客网 时间:2024/06/05 15:07

声明:

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

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

0 0
原创粉丝点击