二叉树相关OJ - C++

news2024/10/5 21:16:26

文章目录:

  • 根据二叉树创建字符串
  • 二叉树的层序遍历
  • 二叉树的最近公共祖先
  • 二叉搜索树与双向链表
  • 从前序与中序遍历序列构造二叉树
  • 从中序与后序遍历序列构造二叉树
  • 二叉树的前序遍历(非递归)
  • 二叉树的中序遍历(非递归)
  • 二叉树的后序遍历(非递归)

根据二叉树创建字符串

题目链接:leetcode606.根据二叉树创建字符串

题目:
在这里插入图片描述
分析:

此题我们可以递归创建字符串,只是递归的时候需要注意什么时候需要加括号或者不加括号。这题的节点数据使用 to_string 函数转换成字符串。

1、节点左右子树都为空时,或者右子树是空树时,括号省略掉。
2、左子树为空,右子树不为空,则括号不能省略。

注意:此题中,我们使用子函数进行递归创建字符串,str使用传引用接收,减少拷贝从而增加效率。

题解:

class Solution {
public:
    // 使用传引用接收,减少拷贝,提高效率
    void _tree2str(TreeNode* root,string& str)
    {
        // 树为空
        if(root==nullptr)
            return;

        // str加上节点数据转出的字符串
        str+=to_string(root->val);

        // 递归左子树创建字符串
        if(root->left) // 左子树不为空
        {
            str+='(';
            _tree2str(root->left,str);
            str+=')';
        }
        else if(root->right) // 左子树为空,但右子树不为空
        {
            str+="()";
        }

        // 递归右子树创建字符串
        if(root->right) // 右子树不为空
        {
            str+='(';
            _tree2str(root->right,str);
            str+=')';
        }
    }

    string tree2str(TreeNode* root) {
        string str;
        _tree2str(root,str);
        return str;
    }
};

二叉树的层序遍历

题目链接:leetcode102.二叉树的层序遍历

题目:
在这里插入图片描述
分析:
用 levelSize 标记每一层节点的数量,每出一个节点,就将当前所出节点的左右节点尾插到队列中,直到队列为空则二叉树遍历完成。
在这里插入图片描述
题解:

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> vv;
        
        // 若二叉树为空,则返回一个空的匿名对象
        if(root==nullptr)
            return vector<vector<int>>();
           //return vv;

        // 定义一个队列,用来存储二叉树的节点
        queue<TreeNode*> q;
        // 将第一层如队列
        int levelSize=1;
        q.push(root);
        // 队列不为空,则表明二叉树结点没有出完,直到为空则遍历完成
        while(!q.empty())
        {
            // 存储每一层的数据
            vector<int> v;
            while(levelSize--)// 控制当前层的数据出完
            {
                // 取出队头结点,并将其数据尾插到v中
                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;
    }
};

二叉树的最近公共祖先

题目链接:leetcode236.二叉树的最近公共祖先

题目:
在这里插入图片描述
分析:
除了最后一种情况,p和q中本身有一个节点就是最近公共祖先,除此之外,p和q两个节点一定分别在最近公共祖先节点的左右两侧。
在这里插入图片描述
题解:

class Solution {
public:
   bool find(TreeNode* root,TreeNode* x)
    {
        if(root==nullptr)
            return false;

        if(root==x)
            return true;
        
        return find(root->left,x)||find(root->right,x);
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        // p节点或q节点为当前根节点,则它就是最近公共祖先
        if(q==root||p==root)
            return root;

        // 确定p和q节点在当前节点的左子树还是右子树
        bool pInLeft,qInLeft,qInRight,pInRight;

        // 因为p和q一定在二叉树中,所以p和q节点不是在根节点左边,那么就是在根节点右边
        pInLeft=find(root->left,p);
        pInRight=!pInLeft;

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

        // p和q节点都在当前根节点左边,则递归左子树继续查找
        if(pInLeft&&qInLeft)
            return lowestCommonAncestor(root->left,p,q);
        // p和q节点都在当前根节点右边,则递归右子树继续查找
        else if(pInRight&&qInRight)
            return lowestCommonAncestor(root->right,p,q);
        // p和q节点在当前节点的左右两侧,则当前节点即是最近公共祖先
        else
            return root;
    }
};

上面的解法在一些特殊的情况下就是比较慢,时间复杂度最坏可达到O(N^2),因此我们需要对其进行优化处理,如下介绍第二种方法。

1、可将此题转化为链表相交问题,用两个栈分别记录根节点到p和q的路径。
2、让分别记录p和q的栈种长度较大的那个先走差距步。
3、然后一起走,直到找到相同的节点即使他们的最近公共祖先。

递归左右子树,遇到节点就入栈,入当前节点的左右子树均没有目标节点,则该节点出栈,最终栈里面的就是p或q到根节点路径上的节点。
在这里插入图片描述
题解:

class Solution {
public:
    bool FindPath(TreeNode* root,TreeNode* x,stack<TreeNode*>& path)
    {
        // 空树,直接返回
        if(root==nullptr)
            return false;

        // 将当前节点入栈,然后与要查找的节点进行对比
        path.push(root);
        if(root==x)
            return true;

        // 若在左子树或者右子树中找到了待查找节点,就返回true
        if(FindPath(root->left,x,path))
            return true;
        if(FindPath(root->right,x,path))
            return true;

        // root不是要找的结点x,左子树和右子树都没有找到,那么root不是x的路径中结点
        path.pop();
        return false;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        // 查找p和q的路径,存储在对于的栈中
        stack<TreeNode*> pPath,qPath;
        FindPath(root,p,pPath);
        FindPath(root,q,qPath);

        // 让长的那个栈先走差距步
        while(pPath.size()<qPath.size())
        {
            qPath.pop();
        }
        while(qPath.size()<pPath.size())
        {
            pPath.pop();
        }

        // 两个栈同时走,并取栈顶元素对比,若两个栈栈顶元素相等,则栈顶元素就是最近公共祖先
        while(pPath.top()!=qPath.top())
        {
            pPath.pop();
            qPath.pop();
        }

        // 返回任意一个栈的栈顶元素
        return qPath.top();
    }
}

二叉搜索树与双向链表

题目链接: 牛客JZ36.二叉搜索树与双向链表

题目:

在这里插入图片描述
分析:
使用中序遍历,定义一个cur指针指向当前中序遍历的结点,prev指向cur的前一个结点,如图所示:第一次cur指向二叉树的最左节点,prev指向空。为了将节点用原有的指针排序成一个双向链表,cur的左指针指向prev,prev的右指针指向cur。
在这里插入图片描述
题解:

class Solution {
public:
	// 这里的prev使用传引用接收,让全程只有一个prev,
	// 若用传值接收,则递归时另外一层栈帧中prev的改变不影响上一层
	void InorderConvert(TreeNode* cur,TreeNode*& prev)
	{
		// 当前节点为空,直接返回
		if(cur==nullptr)
			return;
		
		InorderConvert(cur->left, prev);

		// 当前节点的左指针指向前驱节点,若前驱节点不为空,则前驱节点的右指针指向当前节点
		cur->left=prev;
		if(prev)
			prev->right=cur;
		// 向后递归之前,先更新前驱节点
		prev=cur;

		InorderConvert(cur->right, prev);
	}

    TreeNode* Convert(TreeNode* pRootOfTree) {
		// 为空树的情况
        if(pRootOfTree==nullptr)
			return nullptr;

		// 定义一个前驱节点
		TreeNode* prev=nullptr;
		InorderConvert(pRootOfTree,prev);

		// 寻找头节点
		TreeNode* head=pRootOfTree;
		while(head->left)
		{
			head=head->left;
		}
		return head;
    }
};

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

题目链接: leetcode105.从前序与中序遍历序列构造二叉树

题目:
在这里插入图片描述
分析: 根据前序序列可以确定出二叉树的根,找到根在中序遍历中所处的位置,可确定树的左子树和右子树区间,然后根据前序和中序递归创建二叉树。
在这里插入图片描述
题解:

class Solution {
public:
    TreeNode* _buildTree(vector<int>& preorder,int& prei,vector<int>& inorder,int inBegin,int inEnd)
    {
        // 区间不符合规范,返回nullptr
        if(inBegin>inEnd)
            return nullptr;

        // 创建根节点
        TreeNode* root=new TreeNode(preorder[prei]);
        ++prei;

        // 划分中序的左右子区间
        int rooti=inBegin;
        while(rooti<=inEnd)
        {
            if(root->val==inorder[rooti])
                break;
            else
                ++rooti;
        }

        // 递归构建左右子树
        // [inBegin,rooti-1] [rooti] [rooti+1,inEnd]
        root->left=_buildTree(preorder,prei,inorder,inBegin,rooti-1);
        root->right=_buildTree(preorder,prei,inorder,rooti+1,inEnd);

        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int prei=0,inBegin=0,inEnd=inorder.size()-1;
        return _buildTree(preorder,prei,inorder,inBegin,inEnd);
    }
};

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

题目链接:leetcode106.从中序与后序遍历序列构造二叉树

题目:
在这里插入图片描述
分析: 此题和上面一题是类似的,思路相同,只是按照后序来确定根。

题解:

class Solution {
public:
    TreeNode* _buildTree(vector<int>& postorder,int& posti,vector<int>& inorder,int inBegin,int inEnd)
    {
        // 递归区间不符合规范,返回nullptr
        if(inBegin>inEnd)
            return nullptr;

        // 创建根节点
        TreeNode* root=new TreeNode(postorder[posti]);
        --posti;

        // 划分中序遍历中该节点的左右子区间
        int rooti=inBegin;
        while(rooti<=inEnd)
        {
            if(root->val==inorder[rooti])
                break;
            else
                rooti++;
        }

        // 先递归创建右子树,再递归创建左子树
        root->right = _buildTree(postorder,posti,inorder,rooti+1,inEnd);
        root->left = _buildTree(postorder,posti,inorder,inBegin,rooti-1);

        return root;           
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int posti=postorder.size()-1,inBegin=0,inEnd=inorder.size()-1;
        return _buildTree(postorder,posti,inorder,inBegin,inEnd);
    }
};

二叉树的前序遍历(非递归)

题目链接:leetcode144.二叉树的前序遍历

题目:
在这里插入图片描述
分析:

前序遍历: 根 左子树 右子树

非递归遍历一棵树:

  1. 左路节点
  2. 左路节点的右子树 – 子问题

在这里插入图片描述
题解:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur=root;
        while(!s.empty()||cur) // cur指向哪个节点就表示前序访问这棵树
        {
            // 遍历左路节点,左路节点入栈 -- 访问一棵树的开始
            while(cur)
            {
                v.push_back(cur->val);
                s.push(cur);
                cur=cur->left;
            }

            // 依次去栈中取左路节点的右子树来访问
            TreeNode* top=s.top();
            s.pop();
            // 访问左路节点的右子树 -- 转化为子问题
            cur=top->right;
        }
        return v;
    }
};

二叉树的中序遍历(非递归)

题目链接: leetcode94.二叉树的中序遍历

题目:
在这里插入图片描述
分析:

中序遍历:左子树 根 右子树

与上题类似,只是根节点的访问需要它的左子树已访问完,因此当前数据在当前节点左子树访问完成之后出栈。

题解:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur=root;

        while(!s.empty()||cur) // cur指向哪个节点就表示前序访问这棵树
        {
            // 左路节点入栈
            while(cur)
            {
                s.push(cur);
                cur=cur->left;
            }

            // 依次取栈中左路节点,这时表示这个节点的左子树已经访问完了,
            // 先访问它,再以子问题的形式去访问它的右子树
            TreeNode* top=s.top();
            s.pop();
            v.push_back(top->val);

            // 子问题的形式去访问这棵右子树
            cur=top->right;
        }
        return v;
    }
};

二叉树的后序遍历(非递归)

题目链接:leetcode145.二叉树的后序遍历

题目:
,
分析:
在这里插入图片描述
题解:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> v;
        stack<TreeNode*> st;
        TreeNode* prev=nullptr;
        TreeNode* cur=root;
        while(!st.empty()||cur)
        {
            // 左路节点保存到栈
            while(cur)
            {
                st.push(cur);
                cur=cur->left;
            }

            // 取到一个栈顶元素,说明它的左路节点已经访问完了
            // 如果它的右为空,或者右子树已经访问完了(上一个访问的节点是top的右子树),那么就可以访问栈顶元素了
            TreeNode* top=st.top();
            if(top->right==nullptr||top->right==prev)
            {
                v.push_back(top->val);
                prev=top;
                st.pop();
            }
            // top->right!=nullptr,且右子树还没有访问,子问题迭代访问
            else
            {
                cur=top->right;
            }
        }
        return v;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/28505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode与《代码随想录》】数组篇:做题笔记与总结-Java版

代码随想录地址 是学习过程中的笔记&#xff01;图来自代码随想录。 文章目录理论题目704. 二分查找35. 搜索插入位置34. 在排序数组中查找元素的第一个和最后一个位置69. x 的平方根367.有效的完全平方数理论 数组是存放在连续内存空间上的相同类型数据的集合。 数组下标都是…

[附源码]java毕业设计新能源汽车租赁管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于armv8的kvm实现分析(一)虚拟化介绍

本文基于以下软硬件假定&#xff1a; 架构&#xff1a;AARCH64 内核版本&#xff1a;5.14.0-rc5 1 什么是虚拟化 虚拟化就是把一台物理计算机虚拟成多台逻辑计算机&#xff0c;每台逻辑计算机里面可以运行不同操作系统&#xff0c;而相互之间不受影响&#xff0c;其典型架构…

面试了个 985 毕业的同学,回答“性能调优”题时表情令我毕生难忘

又逢“金九银十”&#xff0c;年轻的毕业生们满怀希望与忐忑&#xff0c;去寻找、竞争一个工作机会。已经在职的开发同学&#xff0c;也想通过社会招聘或者内推的时机争取到更好的待遇、更大的平台。 然而&#xff0c;面试人群众多&#xff0c;技术市场却相对冷淡&#xff0c;面…

JavaIO流:概述

在接触 IO 流前&#xff0c;无论是 变量的声明、数组的创建&#xff0c;又或者是复杂的并发设计还是 Jvm 的性能调优&#xff0c;我们更多的还是和内存打交道。但我们知道计算机组成包括运算器&#xff0c;控制器&#xff0c;存储器&#xff0c;输入设备&#xff0c;输出设备。…

springcloud4:服务注册中心Eureka

直接调用即可&#xff0c;为什么用Eureka什么是服务治理&#xff1f; 多个服务调用&#xff0c;需要有依赖中心管理什么是服务注册&#xff1f; 有一个注册中心&#xff0c;当服务器启动时&#xff0c;会把自己的信息注册到注册中心上什么是服务发现&#xff1f; Client通过注册…

electron打包ffi-napi报错 npm ERR! gyp reason: read ECONNRESET

问题描述 这个问题用了我两天的时间&#xff0c;所以记录一下。 我们项目是使用electronvue&#xff0c;做支付功能的时候需要使用到ffi-napi依赖包。 最后打包的时候ffi-napi报错了&#xff0c;在package.json中去掉ffi-napi就可以打包&#xff0c;但是打包运行后提示缺少ff…

re:Invent 2022,探秘亚马逊云科技的重量级计算创新——Nitro

诞生于16年前的亚马逊云科技&#xff0c;开创了一个全新的云计算领域。秉持着创新与探索精神&#xff0c;自2012年开始&#xff0c;在每年一度的re:Invent全球大会上&#xff0c;亚马逊云科技都会发布最新的云计算技术。对IT产业演进产生了革命性的影响&#xff0c;Nitro系统就…

Java笔记(工厂模式、动态代理、XML)

一、工厂模式 软件设计模式&#xff08;Design pattern&#xff09;&#xff0c;又称设计模式&#xff0c;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的复用性。 什么…

代码随想录算法训练营第四十三天| LeetCode1049. 最后一块石头的重量 II、LeetCode494. 目标和、LeetCode474. 一和零

一、LeetCode1049. 最后一块石头的重量 II 1&#xff1a;题目描述&#xff08;1049. 最后一块石头的重量 II&#xff09; 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将…

让你快速高效的掌握linux内核编译过程

Linux内核编译 一、linux内核的配置与编译&#xff1a; 1.配置内核 1)导入默认配置&#xff1a; make xxxx_defconfig 注1&#xff1a;xxxx表示内核支持的芯片的名称 比如make exynos_defconfig 注2&#xff1a;内核源码中对每个支持的芯片都有默认的配置&#xff0c;默认配置很…

【博学谷学习记录】超强总结,用心分享|架构师-RabbitMQ消息可靠性保障

文章目录一、生产者保证1.1 失败通知1.2 发送方确认1.3 Broker丢失消息二、消费方消息可靠性2.1 消费者手动确认消息依靠三个对象&#xff1a;生产者、消费者、broker一、生产者保证 生产者发送消息到broker时&#xff0c;要保证消息的可靠性&#xff0c;主要的方案有&#xf…

5.28 综合案例2.0-简易起夜灯

HaaS506 - 简易起夜灯简介准备硬件连接图功能实现1.继电器使用说明2. 5.8G雷达感应传感器模块说明3.简易代码3.1测试log简介 案例为了解决晚上起床找不到灯的问题。当你从床上起来时&#xff0c;雷达感应传感器检测到你的活动后自动打开电灯。省去了寻找电灯开关的麻烦。 准备…

java学习笔记 day07-Java基础-综合练习

练习一&#xff1a;飞机票 需求: ​ 机票价格按照淡季旺季、头等舱和经济舱收费、输入机票原价、月份和头等舱或经济舱。 ​ 按照如下规则计算机票价格&#xff1a;旺季&#xff08;5-10月&#xff09;头等舱9折&#xff0c;经济舱8.5折&#xff0c;淡季&#xff08;11月到来…

表白墙(web版)

文章目录前言一、需求分析1.表白墙页面设计2.表白墙功能二、实现1.客户端2.服务器端3.连接数据库前言 前面前端部分写过一个表白墙页面&#xff0c;但是它不能存储提交信息&#xff0c;为了能够让它在提交信息后可以保存其信息&#xff0c;页面刷新后信息依然存在&#xff0c;…

itk配准整理(1)

示例地址&#xff1a; itk\ITK\Examples\RegistrationITKv4\ImageRegistration7.cxx 说明&#xff1a;itk二维图像的配准&#xff1a;平移旋转缩放 效果图&#xff1a; 运行结果&#xff1a; 52 53.6213 [0.8333298229719548, -0.17450270771316403, -12.806452097490313, -1…

在Win10中使用YAMAHA S-YXG50软波表

曾经非常经典的一款软波表YAMAHA S-YXG50我个人非常的喜欢。在XP系统的时代&#xff0c;是我必装的软件&#xff0c;用来听一些MIDI音质和效果很好。而如今玩MIDI的人越来越少了&#xff0c;软波表的时代也被人渐渐的遗忘了。 如今想要怀旧的话&#xff0c;只能够找一台老电脑…

LINUX的XEN和KVM到底区别在什么地方?

本文调研的是 Completely Fair Scheduler 算法, 它当前是 Linux 中 SCHED_NORMAL(非实时任务) 一类 task 的默认调度器. 实际上, 运行在 Guest OS 中的应用程序线程还受到 Guest OS 的调度, 分时运行在 vCPU 上, 但这不在本文调研范围内. 本文仅调研 vCPU 被如何调度到 pCPU 上…

WebDAV之葫芦儿·派盘+无聊笔记

无聊笔记 支持webdav方式连接葫芦儿派盘。 无聊笔记是一款深受用户认可的系统工具,随时都可以进行软件的操作,也可以在线进行笔记的记录,系统会进行智能的保存,满足了用户日常中的需求,可以把软件作为备忘录,笔记本,摘抄录等。 无聊笔记是一个功能强大的记录工具,您…

代码随想录算法训练营第四十二天| LeetCode416. 分割等和子集

一、LeetCode416. 分割等和子集 1&#xff1a;题目描述&#xff08;416. 分割等和子集&#xff09; 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 2&#xff1a;解题思路 本题需要使用01背包的…