二叉树:层次遍历和应用

声明:

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

版权所有,如果转载,请注明出处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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值