C++ 二叉树的构建和遍历

发布时间 2023-06-18 02:22:35作者: WhatAnyWay

二叉树算是一个常见的数据结构了。从纸面上理解二叉树不难,关键是二叉树如何再代码中实现?比如如何构建二叉树?二叉树的递归与非递归遍历?如何根据遍历的顺序确定一个二叉树?

//二叉树节点的定义 一个结构体外加左右子树,存储一个int类型的数据,还有别忘了初始化。
typedef struct list_tree
{
    int val;
     list_tree* left_ch;
     list_tree* right_ch;
     list_tree(int x) :val(x),left_ch(nullptr),right_ch(nullptr){}
};

二叉树单独的节点有了,下面就是在计算里面构造出来这个结构。

我们向计算机输入的是一组这样的二叉树数据,比如{1,2,3,4,5,6,7} ,我们的脑海中,二叉树是这样的

 但对计算机而言,这就是一个数组{1,2,3,4,5,6,7},存储在数组结构中的,计算机怎么知道哪个数据对应哪个节点呢?这就需要我们自己定义一个根据输入数组确定二叉树的函数。

(默认我们输入的数据按照二叉树层级的关系排序。就是广度优先遍历的顺序,广度优先遍历简称BFS)

代码:

list_tree* build_tree(std::vector<int>& nums)//-1为空
{
    if (nums.empty()) return nullptr;
    list_tree* root = new list_tree(nums.front());//根为数组内第一个元素

    std::queue<list_tree*> q;
    q.push(root);//临时存放树节点的数组
    int i = 1;//元素计数

    while (i < nums.size())//构造树
    {
        list_tree* cur = q.front();
        
        if (i < nums.size())
        {
            cur->left_ch = new list_tree(nums[i++]);
            q.push(cur->left_ch);
        }
        if (i < nums.size())
        {
            cur->right_ch = new list_tree(nums[i++]);
            q.push(cur->right_ch);
        }
        q.pop();
    }
return root; }
//补充个细节,这里没有销毁临时使用的queue容器,如果想要清空,请把在最后把它赋值为空或者在他为空之前不停的pop()
//这么做的原因是在你构建了一个超级超级大的二叉树的时候,queue存储了树最后一层的所有元素,虽然函数执行完之后会自动释放queue里面的东西,但是多函数同时运行就会占用超大量的内存空间。
//而且,假如我么将queue定义为静态的,构造多个树的时候,就不用不停的开辟出新的queue然后销毁,再开辟,再销毁。这时候手动释放其内存就很有必要。不过若多函数同时运行的话,就需要加上互斥锁了,不然会乱的

解释下原理,首先,判断是不是空树,是就返回nullptr,不是就开始造二叉树,造二叉树过程我们需要借助一个叫做队列的结构(STL内的queue)。

造树第一步,找根节点。这里我们的根节点就是数组里的第一个数1,好了,根节点root有了。

把这个根节点压入队列。现在队列里面有一个节点了,我们就要对这个队列内的第一个节点进行操作,就是补充此第一个节点的左孩子和右孩子,补充的同时也把补充进去的节点压入队列里面,然后出队第一个元素,无限重复这个操作直到所有数据均已进入二叉树(即数组内的数据遍历完成)。

举个例子,假设我们构造的树为{1,2,3},根节点就是1,那么节点1进入队列;开始补充1的左孩子2,节点2入队列,补充1的右孩子3,3进入队列,出队节点1;

假如2有孩子,下一个补充的就会是2的左右孩子,出队节点2,这个顺序是不会乱的。

 

有了构造二叉树的函数,我们就要讨论下二叉树的遍历了。就是前,中,后序的非递归以及递归方法,还有深度优先(DFS),广度优先(BFS);

关于递归方式的前中后序遍历很简单,因为对于每个节点来说,除了它本身的值,只有两个左右孩子,只要从根节点开始,递归它的左右子孩子,就能完整的遍历整个树。

而它前序,中序,后序,就是每次递归时,输出值语句位置不同而造成的。

将他的顺序打印出来,就可以得到这些遍历顺序的规律

先序:根左右

中序:左根右

后续:左右根

代码如下

//先序遍历递归
void xianxubianlidg(list_tree* root) {
    
    if (root == nullptr) return;
    
    std::cout<<root->val<<",";
    xianxubianlidg(root->left_ch);
    xianxubianlidg(root->right_ch);
}
//中序遍历递归
void zhongxubianlidg(list_tree* root)
{
    if (root == nullptr) return;

    zhongxubianlidg(root->left_ch);
    std::cout << root->val << ",";
    zhongxubianlidg(root->right_ch);
}
//后序遍历递归
void houxubianlidg(list_tree* root)
{
    if (root == nullptr) return;

    houxubianlidg(root->left_ch);
    houxubianlidg(root->right_ch);
    std::cout << root->val << ",";
}

为了深入理解这些遍历顺序,最好实现一下非递归遍历的方式

代码如下

//非递归先,中,后序遍历
void xianxu(list_tree* root)
{
    if (root==nullptr) return;

    std::stack<list_tree*> xx;
    list_tree* bl;
    bl = root;
    while (!xx.empty() || bl)
    {
        if (bl)
        {
            std::cout << bl->val<<",";
            xx.push(bl);
            bl = bl -> left_ch;
        }
        else
        {
            bl = xx.top();
            xx.pop();
            bl = bl->right_ch;
        }
    }


}
void zhongxu(list_tree* root)
{
    if (root==nullptr) return;

    list_tree* bl = root;
    std::stack<list_tree*> zx;
    while (!zx.empty() || bl)
    {
        if (bl) {
            zx.push(bl);
            bl = bl->left_ch;
        }
        else {
            bl = zx.top();
            std::cout << zx.top()->val<<",";
            bl = zx.top()->right_ch;
            zx.pop();
        }
    }
}
void houxu(list_tree* root)
{
    if (root == nullptr) return;

    std::stack<list_tree*> s1;
    std::stack<list_tree*> s2;
    s1.push(root);


    while (!s1.empty())
    {
        root = s1.top();
        s1.pop();
        s2.push(root);

        if (root->left_ch != nullptr)
        {
            s1.push(root->left_ch);
        }
        if (root->right_ch != nullptr)
        {
            s1.push(root->right_ch);
        }
    }
    while (!s2.empty())
    {
        std::cout << s2.top()->val << ",";
        s2.pop();
    }

 咋一直是草稿,我是不是忘写点啥??想起来再说!