C++篇(14)二叉树进阶算法题

1、根据二叉树创建字符串

https://leetcode.cn/problems/construct-string-from-binary-tree

算法思路:左为空,右不为空,不能省略空括号。其他情况,空括号都可以省略。

参考题解

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    string tree2str(TreeNode* root) 
    {
        string str;
        if(root == nullptr)
            return str;

        str += to_string(root->val);

        // 左不为空,需要递归获取子树括号括起来
        // 左为空,右不为空,左边的空括号需要保留
        if(root->left || root->right)
        {
            str += '(';
            str += tree2str(root->left);
            str += ')';
        }
        
        // 右不为空,需要递归获取子树括号括起来
        // 右边只要是空,空括号就不需要了
        if(root->right)
        {
            str += '(';
            str += tree2str(root->right);
            str += ')';
        }
        
        return str;
    }
};

2、二叉树的层序遍历

https://leetcode.cn/problems/binary-tree-level-order-traversal

算法思路:在层序遍历的过程中,增加一个levelSize,记录每层的数据个数。树不为空的情况下,第一层levelSize = 1,循环控制,第一层出完了,第二层就都进队列了,队列中size就是第二层的数据个数。以此类推,假设levelSize为第n层的数据个数,因为层序遍历思想为当前层结点出队列,带入下一层结点(也就是子结点),循环控制第n层数据出完了,那么第n+1层结点都进队列了,队列的size就是下一层结点的levelSize。

参考题解

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> q;
        int LevelSize;

        if(root)
        {
            q.push(root);
            LevelSize = 1;
        }

        vector<vector<int>> vv;

        while(!q.empty())
        {
            vector<int> v;

            // 一层一层出
            while(LevelSize--)
            {
                TreeNode* front = q.front();
                q.pop();
                v.push_back(front->val);

                // 下一层孩子入队列
                if(front->left)
                {
                    q.push(front->left);
                }
                if(front->right)
                {
                    q.push(front->right);
                }         
            }
            vv.push_back(v);

            //当前层出完了,下一层结点都进队列了,队列数据个数就是下一层结点个数
            LevelSize = q.size();
        }

        return vv;
    }
};

这道题还有一个变式题:

https://leetcode.cn/problems/binary-tree-level-order-traversal-ii

算法思路 :刚刚是让我们自上而下来层序遍历,现在要自下而上来层序遍历,该怎么做呢?其实就是把我们刚刚得到的二维数组逆置一下即可。

参考题解

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) 
    {
        queue<TreeNode*> q;
        int levelSize = 0;

        if(root)
        {
            q.push(root);
            levelSize = 1;
        }

        vector<vector<int>> vv;

        while(!q.empty())
        {
            vector<int> v;

            while(levelSize--)
            {
                TreeNode* front = q.front();
                q.pop();
                v.push_back(front->val);

                if(front->left)
                    q.push(front->left);
                if(front->right)
                    q.push(front->right);
            }
            
            vv.push_back(v);

            levelSize = q.size();
        }

        reverse(vv.begin(), vv.end());

        return vv;
    }
};

3、二叉树的最近公共祖先

https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree

算法思路1:仔细观察一下,两个结点,最近公共祖先的特征就是一个结点在最近公共祖先的左边,一个结点在最近公共祖先的右边。比如,6和4的公共祖先有5和3,但是只有最近的公共祖先5满足6在左边,4在右边。其他公共祖先都不满足,只有最近公共祖先满足这个规则。

参考题解1

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool IsInTree(TreeNode* t, TreeNode* x)
    {
        if(t == nullptr)
            return false;

        return t == x
            || IsInTree(t->left, x)
            || IsInTree(t->right, x);
    }
    
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(root == nullptr)
            return nullptr;

        if(root == p || root == q)
            return root;

        bool pInLeft = IsInTree(root->left, p);
        bool pInRight = !pInLeft;

        bool qInLeft = IsInTree(root->left, q);
        bool qInRight = !qInLeft;

        // 1、p和q分别在左和右,root就是最近公共祖先
        // 2、p和q都在左,递归到左子树查找
        // 3、p和q都在右,递归到右子树查找
        if((pInLeft && qInRight) || (pInRight && qInLeft))
            return root;
        else if(pInLeft && qInLeft)
            return lowestCommonAncestor(root->left, p, q);
        else 
            return lowestCommonAncestor(root->right, p, q);
    }
};

算法思路2 :如果能求出两个结点到根结点的路径,那么就可以转换为链表相交问题。比如:6到3的路径为6->5->3,4到3的路径为4->2->5->3,那么看做两个链表找交点,交点5就是最近公共祖先。如果是三叉链的话就很好获取路径,但是这里是二叉链,该怎么得到路径呢?这里就要用到前序遍历 的思路,用来记录查找路径,遇到结点先入栈,因为该结点就算不是我们要找的结点x,但是也有可能是分支结点。如果左右子树都没有结点x,那么说明入栈的结点不在路径上,所以要出栈,继续去其他分支路径进行查找。

参考题解2

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool GetPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
    {
        //深度遍历的前序查找,顺便用栈记录路径
        if(root == nullptr)
            return false;
        
        path.push(root);

        if(root == x)
            return true;

        if(GetPath(root->left, x, path))
            return true;

        if(GetPath(root->right, x, path))
            return true;

        path.pop();
        return false;
    }
    
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        stack<TreeNode*> pPath, qPath;
        GetPath(root, p, pPath);
        GetPath(root, q, qPath);

        //找交点
        while(pPath.size() != qPath.size())
        {
            //长的先走
            if(pPath.size() > qPath.size())
            {
                pPath.pop();
            }
            else
            {
                qPath.pop();
            }
        }

        //再同时走找交点
        while(pPath.top() != qPath.top())
        {
            pPath.pop();
            qPath.pop();
        }

        return pPath.top();
    }
};

4、将二叉搜索树转化为排序的双向链表

https://leetcode.cn/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof

算法思路1:中序遍历搜索二叉树,遍历顺序是有序的,将二叉树的结点指针放到一个vector中,再把前后结点的连接关系进行修改。这个思路最简单,但是要消耗O(N)的空间复杂度。

算法思路2 :依旧中序遍历搜索二叉树,遍历顺序是有序的,遍历过程中修改左指针为前驱右指针为后继。记录一个cur和prev,cur为当前中序遍历遍历到的结点,prev为上一个中序遍历的结点,cur->left指向prev,prev->right指向cur。

参考题解

cpp 复制代码
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    void InOrderConvert(Node* cur, Node*& prev)
    {
        if(cur == nullptr)
            return;

        InOrderConvert(cur->left, prev);

        // cur中序
        // left指向中序前一个,左变成前驱
        cur->left = prev;
        // 中序前一个节点的右指向cur,右变成后继
        if(prev)
            prev->right = cur;

        prev = cur;

        InOrderConvert(cur->right, prev);
    }

    Node* treeToDoublyList(Node* root) 
    {
        if(root == nullptr)
            return nullptr;

        Node* prev = nullptr;
        InOrderConvert(root, prev);

        Node* head = root;
        while(head->left)
        {
            head = head->left;
        }

        //循环链表
        head->left = prev;
        prev->right = head;

        return head;
    }
};

5、从前序与中序遍历序列构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal

算法思路:前序确定根,中序分割出左右子树。

参考题解

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int& prei, int inbegin, int inend)
    {
        if(inbegin > inend)
            return nullptr;
        
        // 前序遍历根
        TreeNode* root = new TreeNode(preorder[prei]);

        // 中序分割出左右子树
        int rooti = inbegin;
        while(rooti <= inend)
        {
            if(preorder[prei] == inorder[rooti])
                break;
            else
                rooti++;
        }
        prei++;

        // [inbegin, rooti-1]  rooti  [rooti+1, inend]
        root->left = build(preorder, inorder, prei, inbegin, rooti - 1);
        root->right = build(preorder, inorder, prei, rooti + 1, inend);

        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
    {
        int i = 0;
        return build(preorder, inorder, i, 0, inorder.size() - 1);
    }
};

6、从中序与后序遍历序列构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal

算法思路:和上一题类似,只不过这次是通过后序遍历序列确定根,先递归构建右子树,再构建左子树。

参考题解

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* build(vector<int>& inorder, vector<int>& postorder, int& posti, int inbegin, int inend)
    {
        if(inbegin > inend)
            return nullptr;

        TreeNode* root = new TreeNode(postorder[posti]);

        int rooti = inbegin;
        while(rooti <= inend)
        {
            if(inorder[rooti] == postorder[posti])
                break;
            else
                rooti++;
        }
        posti--;

        root->right = build(inorder, postorder, posti, rooti + 1, inend);
        root->left = build(inorder, postorder, posti, inbegin, rooti - 1);

        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        int i = postorder.size() - 1;
        return build(inorder, postorder, i, 0, inorder.size() - 1);
    }
};

7、二叉树的非递归前序遍历

https://leetcode.cn/problems/binary-tree-preorder-traversal

算法思路:先访问左路结点,再访问左路结点的右子树。

参考题解

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        vector<int> v;

        while(cur || !st.empty())
        {
            //每次循环开始代表访问一棵树的开始
            //访问左路结点,左路结点入栈
            while(cur)
            {
                v.push_back(cur->val);
                st.push(cur);

                cur = cur->left;
            }

            //取一个左路结点的右子树出来访问
            TreeNode* top = st.top();
            st.pop();

            //循环子问题的方式访问右子树
            cur = top->right;
        }

        return v;
    }
};

8、二叉树的非递归中序遍历

https://leetcode.cn/problems/binary-tree-inorder-traversal

算法思路:中序遍历和前序遍历思想是类似的,就是访问根的时候不一样。

参考题解

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        vector<int> v;

        while(cur || !st.empty())
        {
            while(cur)
            {
                st.push(cur);
                cur = cur->left;
            }

            TreeNode* top = st.top();
            st.pop();

            v.push_back(top->val);
            
            cur = top->right;
        }
        return v;
    }
};

9、二叉树的非递归后序遍历

https://leetcode.cn/problems/binary-tree-postorder-traversal

算法思路:取到一个左路结点时,左子树已经访问过了。如果左路结点右子树不为空,右子树没有访问,那么上一个访问结点是左子树的根。如果左路结点右子树不为空,右子树已经访问过了,那么上一个访问结点是右子树的根。

参考题解

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* prev = nullptr;
        vector<int> v;

        while(cur || !st.empty())
        {
            while(cur)
            {
                st.push(cur);
                cur = cur->left;
            }

            // 取一个左路结点的右子树出来访问,此时代表左路结点的左子树已经访问过了
            TreeNode* top = st.top();

            // 右子树为空或者上一个访问的结点是右子树的根,代表右子树也访问过了
            // 可以访问当前根结点
            if(top->right == nullptr || prev == top->right)
            {
                v.push_back(top->val);
                st.pop();

                prev = top;
            }
            else
            {
                // 循环子问题的方式访问右子树
                cur = top->right;
            }
        }

        return v;
    }
};
相关推荐
小邓儿◑.◑3 小时前
贪心算法 | 每周8题(三)
算法·贪心算法
十五年专注C++开发3 小时前
QDarkStyleSheet: 一个Qt应用的暗色主题解决方案
开发语言·c++·qt·qss
2401_841495643 小时前
【数据结构】最长的最短路径的求解
java·数据结构·c++·python·算法·最短路径·图搜索
小龙报3 小时前
《算法每日一题(1)--- 连续因子》
c语言·开发语言·c++·windows·git·算法·visual studio
祁同伟.3 小时前
【C++】异常
开发语言·c++
第七序章3 小时前
【C + +】红黑树:全面剖析与深度学习
c语言·开发语言·数据结构·c++·人工智能
夜晚中的人海3 小时前
【C++】滑动窗口算法习题
开发语言·c++·算法
AA陈超4 小时前
虚幻引擎5 GAS开发俯视角RPG游戏 P06-09 玩家等级与战斗接口
c++·游戏·ue5·游戏引擎·虚幻
violet-lz4 小时前
数据结构四大简单排序算法详解:直接插入排序、选择排序、基数排序和冒泡排序
数据结构·算法·排序算法