当前位置: 首页 > news >正文

【C++笔记】C++常见二叉树OJ和拓扑排序

【C++笔记】C++常见二叉树OJ和拓扑排序

🔥个人主页大白的编程日记

🔥专栏C++笔记


文章目录

  • 【C++笔记】C++常见二叉树OJ和拓扑排序
    • 前言
    • 一.二叉树OJ
      • 1.1 根据二叉树创建字符串
      • 1.2 二叉树的层序遍历
      • 1.3 二叉树的最近公共祖先
      • 1.4 将二叉搜索树转化为排序的双向链表
      • 1.5 从前序与中序遍历序列构造二叉树
      • 1.6 从中序与后序遍历序列构造二叉树
      • 1.7 根据前序和后续遍历构建二叉树
      • 1.8 二叉树前序遍历的非递归
      • 1.9 二叉树中序遍历的非递归
      • 1.10 二叉树的后序遍历
    • 二. 拓扑排序
      • 2.1 动画演示
      • 2.2 课程表
      • 2.3 课程表2
      • 2.4 火星词典
    • 后言

前言

哈喽,各位小伙伴大家好!上期我们讲了C++的类型转换。今天我们来讲一下C++常见二叉树OJ和拓扑排序。话不多说,我们进入正题!向大厂冲锋
在这里插入图片描述

一.二叉树OJ

1.1 根据二叉树创建字符串

  • 题目:根据二叉树创建字符串
  • 思路分析

  • 代码实现
class Solution {
public:
   string tree2str(TreeNode* root) 
   {
       string ret;
       if(root==nullptr)
       {
           return "";
       }
       ret+=to_string(root->val);
       //左不为空进入
       //左为空 右不为空进入不省略空号
       if(root->right||root->left)
       {
           ret+='(';
           ret+=tree2str(root->left);
           ret+=')';
       }
       //右为空则不进入省略
       if(root->right)
       {
           ret+='(';
           ret+=tree2str(root->right);
           ret+=')';
       }
       return ret;
   }
};

在这里插入图片描述

1.2 二叉树的层序遍历

  • 题目:二叉树的层序遍历

  • 思路分析

  • 代码实现
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        vector<vector<int>> ret;
        if(root==nullptr)
        {
            return {};
        }
        queue<TreeNode*> q;
        q.push(root);
        int size=1;
        while(q.size())
        {
            vector<int> v;
            while(size--)
            {
                TreeNode* t=q.front();
                v.push_back(t->val);
                q.pop();
                if(t->left)
                {
                   q.push(t->left);
                }
                if(t->right)
                {
                   q.push(t->right);
                }
            }
            ret.push_back(v);
            size=q.size();
        }
        return ret;
    }
};

1.3 二叉树的最近公共祖先

  • 题目:二叉树的最近公共祖先
    =

  • 思路分析:

  • 代码实现
    方法一:
class Solution {
public:
    bool find(TreeNode* root,TreeNode* x)
    {
        if(root==nullptr)
        {
            return false;
        }
        return root==x||find(root->left,x)||find(root->right,x);
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(root==nullptr)
        {
            return nullptr;
        }
        if(root==p||root==q)
        {
            return root;
        }
        bool pinleft=find(root->left,p);
        bool pinright=!pinleft;
        bool qinleft=find(root->left,q);
        bool qinright=!qinleft;
        if((pinleft&&qinright)||(pinright&&qinleft))
        {
            return root;
        }
        else if(pinleft&&qinleft)
        {
            return lowestCommonAncestor(root->left,p,q);
        }
        else
        {
            return lowestCommonAncestor(root->right,p,q);
        }
    }
};

方法二:

class Solution {
public:
    bool GetPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& st)
    {
        if(root==nullptr)
        {
            return false;
        }
        st.push(root);
        if(root==x)
        {
            return true;
        }
        if(GetPath(root->left,x,st))
        {
            return true;
        }
        if(GetPath(root->right,x,st))
        {
            return true;
        }
        st.pop();
        return false;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        stack<TreeNode*> stl,str;
        GetPath(root,p,stl);
        GetPath(root,q,str);
        while(stl.size()!=str.size())
        {
            if(stl.size()>str.size())
            {
                stl.pop();
            }
            else
            {
                str.pop();
            }
        }
        while(stl.top()!=str.top())
        {
            stl.pop();
            str.pop();
        }
        return str.top();
    }
};

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

  • 题目:将二叉搜索树转化为排序的双向链表
  • 思路分析

  • 代码实现
    方法一
class Solution {
public:
    Node* treeToDoublyList(Node* root) 
    {
        if(root==nullptr)
        {
            return root;
        }
        vector<Node*> v;
        auto dfs=[&](this auto&& dfs,Node* root)->void
        {
            if(root==nullptr)
            {
                return ;
            }
            dfs(root->left);
            v.push_back(root);
            dfs(root->right);
        };
        dfs(root);
        for(int i=0;i<v.size()-1;i++)
        {
            v[i]->right=v[i+1];
            v[i+1]->left=v[i];
        }
        v[0]->left=v[v.size()-1];
        v[v.size()-1]->right=v[0];
        return v[0];
    }
};

方法二

class Solution {
public:
    void InOrderCovert(Node*cur,Node*& prev)//中序遍历同时修改指针prev为中序前一个cur当前中序
    {
        if(cur==nullptr)
        {
            return;
        }
         InOrderCovert(cur->left,prev);
         cur->left=prev;
         if(prev)
         {
            prev->right=cur;
         }//修改前后指针
         prev=cur;//更新prev
         InOrderCovert(cur->right,prev);
    }
    Node* treeToDoublyList(Node* root) {
        if(root==nullptr)
        {
            return root;
        }
        Node* prev=nullptr;
        InOrderCovert(root,prev);
        Node* head=root;
        while(head->left)
        {
            head=head->left;
        }
        head->left=prev;
        prev->right=head;//首尾相连
        return head;
    }
};

  • 下面的这三道题都是我看灵神的题解 发现讲的特别好 思路也很清晰 通俗易通。 直接上按照灵神的思路图了。

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

  • 题目:从前序与中序遍历序列构造二叉树

  • 思路分析

  • 代码实现

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (preorder.empty()) { // 空节点
            return nullptr;
        }
        int rootValue = preorder[0];
        int left_size = 0;
        // 使用for循环查找根节点在中序遍历中的位置
        for (int i = 0; i < inorder.size(); ++i) {
            if (inorder[i] == rootValue) {
                left_size = i; // 找到根节点的位置,即左子树的大小
                break;
            }
        }
        vector<int> pre1(preorder.begin() + 1, preorder.begin() + 1 + left_size);
        vector<int> pre2(preorder.begin() + 1 + left_size, preorder.end());
        vector<int> in1(inorder.begin(), inorder.begin() + left_size);
        vector<int> in2(inorder.begin() + left_size + 1, inorder.end());
        TreeNode* left = buildTree(pre1, in1);
        TreeNode* right = buildTree(pre2, in2);
        return new TreeNode(preorder[0], left, right);
    }
};

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

  • 题目:从中序与后序遍历序列构造二叉树
    、
  • 思路分析
  • 代码实现
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int n = inorder.size();
        unordered_map<int, int> index;
        for (int i = 0; i < n; i++) {
            index[inorder[i]] = i;
        }

        function<TreeNode*(int, int, int, int)> dfs = [&](int in_l, int in_r, int post_l, int post_r) -> TreeNode* {
            if (post_l > post_r) { // 空节点,注意左闭右闭区间的判断条件
                return nullptr;
            }
            int rootValue = postorder[post_r];
            int rootIndex = index[rootValue];
            int left_size = rootIndex - in_l; // 左子树的大小

            TreeNode* root = new TreeNode(rootValue);
            root->left = dfs(in_l, rootIndex - 1, post_l, post_l + left_size - 1);
            root->right = dfs(rootIndex + 1, in_r, post_l + left_size, post_r - 1);
            return root;
        };

        return dfs(0, n - 1, 0, n - 1); // 左闭右闭区间
    }
};

1.7 根据前序和后续遍历构建二叉树

  • 题目:根据前序和后续遍历构建二叉树

  • 思路分析

  • 首先说明,如果只知道前序遍历和后序遍历,这棵二叉树不一定是唯一的,如下图。

  • 注:如果二叉树的每个非叶节点都有两个儿子,知道前序和后序就能唯一确定这棵二叉树。

  • 题目说,如果存在多个答案,我们可以返回其中任何一个。那么不妨规定:无论什么情况,在前序遍历中,preorder[1] 都是左子树的根节点值。

  • 代码实现
class Solution {
public:
    TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
        int n = preorder.size();
        vector<int> index(n + 1); // 用于快速查找 postorder 中元素的索引
        for (int i = 0; i < n; i++) {
            index[postorder[i]] = i;
        }

        // 使用左闭右闭区间 [pre_l, pre_r] 和 [post_l, post_r]
        auto dfs = [&](this auto&& dfs, int pre_l, int pre_r, int post_l, int post_r) -> TreeNode* {
            if (pre_l > pre_r) { // 空节点
                return nullptr;
            }
            if (pre_l == pre_r) { // 叶子节点
                return new TreeNode(preorder[pre_l]);
            }
            int left_size = index[preorder[pre_l + 1]] - post_l + 1; // 左子树的大小
            TreeNode* left = dfs(pre_l + 1, pre_l + left_size, post_l, post_l + left_size - 1);
            TreeNode* right = dfs(pre_l + left_size + 1, pre_r, post_l + left_size, post_r - 1);
            return new TreeNode(preorder[pre_l], left, right);
        };

        return dfs(0, n - 1, 0, n - 1); // 左闭右闭区间
    }
};

1.8 二叉树前序遍历的非递归

  • 题目:二叉树的前序遍历

  • 思路分析

  • 代码实现

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        vector<int> v;
        TreeNode* cur=root;
        //把遍历分为遍历左子树和右子树
        while(cur||!st.empty())//当前右子树不为空或还有右子树待访问
        {
            while(cur)//访问根 左子树
            {
                v.push_back(cur->val);//访问根
                st.push(cur);
                cur=cur->left;
            }
            TreeNode* tmp=st.top();
            st.pop();//弹出避免二次递归并且更新上上级递归
            cur=tmp->right;//转为子问题访问右子树
        }
        return v;
    }
};

1.9 二叉树中序遍历的非递归

  • 题目:二叉树的中序遍历

  • 思路分析
    在这里插入图片描述

  • 代码实现

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        vector<int> v;
        TreeNode* cur = root;
        while (cur || !st.empty())//右子树不为空或者当前子树还有根和根子树没有访问
        {
            while (cur)//访问左子树
            {
                st.push(cur);
                cur = cur->left;
            }
            TreeNode* tmp = st.top();
            st.pop();//弹出避免二次递归并且更新上上级递归
            v.push_back(tmp->val);//访问根
            cur = tmp->right;//转为子问题访问右子树
        }
         return v;
    }
};

在这里插入图片描述

1.10 二叉树的后序遍历

  • 题目:二叉树的后序遍历

  • 思路分析

  • 代码实现

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        vector<int> v;
        TreeNode* cur = root;
        while (cur || !st.empty())//左子树不为空或者还有根和右子树没有访问
        {
            while (cur)//访问根 右子树
            {
                st.push(cur);
                v.push_back(cur->val);//访问根
                cur = cur->right;//访问右子树
            }
            TreeNode* root = st.top();
            st.pop();//弹出避免二次递归并且更新上上级递归
            cur = root->left;//转为子问题访问左子树
        }
        reverse(v.begin(),v.end());//逆置
         return v;
    }
};

二. 拓扑排序

拓扑排序是一种对有向无环图(DAG,Directed Acyclic Graph)的顶点进行线性排序的方法,使得对于图中的每一个边 (u,v),顶点 u 在排序中都出现在顶点 v 的前面。拓扑排序在实际应用中非常重要,例如在任务调度、项目管理、编译原理等领域。

2.1 动画演示


2.2 课程表

  • 题目:课程表
  • 思路分析
    同上 判断是否有环
  • 代码实现
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) 
    {
        vector<int> in(numCourses);
        unordered_map<int,vector<int>> hash;
        for(auto& x:prerequisites)//维护每个节点的入度
        {
            int a=x[0],b=x[1];
            hash[b].push_back(a);
            in[a]++;
        }
        queue<int> q;
        for(int i=0;i<numCourses;i++)//维护每个节点的边信息
        {
            if(in[i]==0)
            {
                q.push(i);
            }
        }
        while(q.size())//bfs
        {
            int i=q.front();
            q.pop();
            for(auto& x:hash[i])
            {
                in[x]--;
                if(in[x]==0)
                {
                    q.push(x);
                }
            }
        }
        for(auto& x:in)//判断是否有环
        {
            if(x)
            {
                return false;
            }
        }
        return true;
    }
};

在这里插入图片描述

2.3 课程表2

  • 题目:
  • 思路分析
    同上 拓扑排序
  • 代码实现
class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) 
    {
        vector<int> in(numCourses),ret;
        unordered_map<int,vector<int>> hash;
        for(auto& x:prerequisites)
        {
            int a=x[0],b=x[1];
            hash[b].push_back(a);
            in[a]++;
        }
        queue<int> q;
        for(int i=0;i<numCourses;i++)
        {
            if(in[i]==0)
            {
                q.push(i);
                ret.push_back(i);
            }
        }
        while(q.size())
        {
            int i=q.front();
            q.pop();
            for(auto& x:hash[i])
            {
                in[x]--;
                if(in[x]==0)
                {
                    q.push(x);
                    ret.push_back(x);
                }
            }
        }
        for(auto& x:in)
        {
            if(x)
            {
                return {};
            }
        }
        return ret;
    }
};

在这里插入图片描述

2.4 火星词典

  • 题目:火星词典

  • 思路分析

  • 代码实现

class Solution {
public:
    string alienOrder(vector<string>& words) 
    {
        string ret;
        int n=words.size();
        unordered_map<char,unordered_set<char>> edge;//出度信息
        unordered_map<char,int> in;//入度信息
        for(auto& s:words)
        {
            for(auto ch:s)
            {
                in[ch]=0;
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                int len1=words[i].size();
                int len2=words[j].size();
                int l=0,r=0;
                while(l<len1&&r<len2)
                {
                    while(l<len1&&r<len2&&words[i][l]==words[j][r])//找到不相等的位置
                    {
                        l++;
                        r++;
                    }
                    char a=words[i][l],b=words[j][r];
                    if(l<len1&&r<len2&&(!edge.count(a)||!edge[a].count(b)))//防止重复信息
                    {
                        edge[a].insert(b);
                        in[b]++;
                    }
                     if(r==len2&&l<len1)//处理边界情况
                    {
                        return "";
                    }
                    break;
                }
            }
        }
        queue<char> q;
        for(auto [a,b]:in)//入度为0的入队列
        {
            if(b==0)
            {
                q.push(a);
            }
        }
        while(q.size())
        {
            char t=q.front();
            ret+=t;
            q.pop();
            for(auto x:edge[t])
            {
                if(--in[x]==0)//出度--判断是否入度为0
                {
                    q.push(x);
                }
            }
        }
        for(auto [a,b]:in)
        {
            if(b!=0)
            {
                return "";//判断是否有向无环图
            }
        }
        return ret;
    }
};

后言

这就是C++常见二叉树OJ和拓扑排序。大家自己好好消化!今天就分享到这!感谢各位的耐心垂阅!咱们下期见!拜拜~

相关文章:

  • 31天Python入门——第19天:再学面向对象·一切皆对象
  • Css:如何解决绝对定位子元素内容被父级元素overflow:hidden属性剪裁
  • 可实现黑屏与蓝屏反应的屏幕隐私保护软件分享
  • 《高校辅导员》考试考哪些内容?
  • nacos-sdk-go v2.29 中一个拼写错误,我定位了3个小时 ……
  • 主流车辆监控管理系统对比分析
  • 极客说|重大发布:vLLM V1
  • 2011-2019年各省地方财政国债还本付息支出数据
  • 【Tauri2】011——菜单menu(2)
  • 什么是 Java 泛型
  • 0-1背包问题和最长公共子序列
  • Java打卡-Day23-文件、IO流
  • 探索 Kubernetes 网络穿透:如何从外部访问 K8s Pod 地址
  • 【MySQL】InnoDB的索引为什么用B+树而不用B树?
  • Python----计算机视觉处理(Opencv:道路检测之道路透视变换)
  • 蓝桥杯2023年第十四届省赛真题-棋盘
  • stack与queue和deque
  • Unicode统一码及实现方式的全面讲解
  • fbx bip互转 测试OK
  • 容器 = 命名空间 + Cgroups + 文件系统
  • 简单的手机网站模板免费下载/app关键词优化
  • WordPress站群模版/2024免费网站推广大全
  • 哪个网站做任务赚钱的/seo网站搜索优化
  • wordpress 媒体库角色权限/长沙百度快照优化排名
  • 网站开发后端技术/百度推广点击一次多少钱
  • 建设南大街小学网站/推广排名seo