二叉树进阶OJ题

news2024/11/24 17:16:24

目录

一、前序遍历非递归

二、中序遍历非递归

三、后序遍历非递归

四、二叉树转链表

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

六、二叉树的层序遍历1

七、二叉树的层序遍历2


一、前序遍历非递归

题目描述:写出二叉树前序遍历的非递归形式。

链接:前序遍历

思路:任何一颗树都可以认为分为左路节点,左路节点的右子树。先访问左路节点,再来访问左路节点的右子树。

先定义栈s存放节点,vector v来存放结点的数值,TreeNode* cur,cur初始化为root。

当cur不为空或者栈不为空的时候(一开始栈是空的,cur不为空),循环继续:先把左路节点存放进栈中,同时把值存入v中,一直循环,直到此时的左路节点为空,访问结束。在弹出栈顶元素top,把top->right赋值给我们的cur,就可以转化成子问题去访问左路节点的右子树了。

~  栈s不为空说明此时还有左路节点的右子树还没访问,cur不为空说明此时还有树要去访问。当两个同时为空时,循环结束,最终得到前序遍历。
~  一个节点出栈说明这个节点及其左子树已经访问完了,因为我们是先把左路节点存入栈中,此时还剩右子树没有访问。

代码实现:

class Solution 
{
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur = root;
        while(cur || !s.empty())
        {
            while(cur)
            {
                s.push(cur);
                v.push_back(cur->val);

                cur = cur->left;
            }

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

            cur = top->right;
        }
        return v;
    }
};

二、中序遍历非递归

题目描述:写出二叉树中序遍历的非递归形式。

链接:中序遍历

思路:中序遍历是左、根、右。左子树访问完之后才能去访问根。左路节点一直走直到左子树访问完,入栈的过程中不去进行访问(存放数值到v中),当左路节点出栈之后,也就是从栈中弹出进行访问。

代码实现:

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

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

            TreeNode* top = s.top();
            v.push_back(top->val);
            s.pop();
            cur = top->right;
        }
        return v;
    }
};

三、后序遍历非递归

题目描述:写出二叉树后序遍历的非递归形式。

链接:后序遍历

思路:我们定义一个栈,在栈里面取到一个节点时:右子树是否访问过,如果没有访问,迭代子问题访问,如果访问过了,则访问这个根节点,pop出栈。

如果top的右子树为空或者右子树已经访问过了(上一个访问节点是右子树的根),那么说明右子树不用访问或者访问过了,可以访问根top;当右子树不为空,且没有访问过,则迭代子问题访问。

通过prev来判断上一次访问的节点:如果prev等于top->right时,表示栈顶节点的右子树已经访问过了,可以弹出栈顶节点并访问它。

代码实现:

class Solution 
{
public:
    vector<int> postorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur = root;
        TreeNode* prev = nullptr;
        while(cur || !s.empty())
        {
            while(cur)
            {
                s.push(cur);
                cur = cur->left;
            }

            TreeNode* top = s.top();
            if(top->right == nullptr || top->right == prev)
            {
                v.push_back(top->val);
                prev = top;
                s.pop();
            }
            else
            {
                cur = top->right;
            }
        }
        return v;
    }
};

四、二叉树转链表

题目描述:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。

注意:
1、要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继
2、返回链表中的第一个节点的指针
3、函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构
4、你不用输出双向链表,程序会根据你的返回值自动打印输出

链接:二叉树转链表

代码实现:

class Solution 
{
public:
	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) 
    {
        TreeNode* prev = nullptr;
		InorderConvert(pRootOfTree, prev);

		TreeNode* head = pRootOfTree;

        while(head && head->left)
		{
			head = head->left;
		}
		return head;
    }
};

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

题目描述:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

链接:二叉树的公共祖先 

方法一:

思路:把根到对应节点的路径存储起来,在找出相交的结点即是最近的公共结点。

代码实现:

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;

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

        path.pop();
        return false;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        stack<TreeNode*> pPath, qPath;
        FindPath(root, p, pPath);
        FindPath(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 qPath.top();
    }
};

方法二:

思路:我们先分析下题干,可以知道:要找两个节点的公共祖先,我们通过规律发现这两个节点一定是分别在它们的公共祖先的左子树和右子树。

那么有了这个规律,我们就可以这样去找:当遍历到一个节点时,如果p在它的右子树且q在它的左子树或者p在左子树且q在右子树,那么它就是公共祖先。如p,q在同一侧,那么该节点一定不是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) 
    {
        if(root == nullptr)
            return nullptr;

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

        bool pInleft, pInright, qInleft, qInright;
        pInleft = Find(root->left, p);
        pInright = !pInleft;
        qInleft = Find(root->left, q);
        qInright = !qInleft;

        if((pInleft && qInright) || (pInright && qInleft))
            return root;
        else if(pInleft && qInleft)
            return lowestCommonAncestor(root->left, p, q);
        else if(pInright && qInright)
            return lowestCommonAncestor(root->right, p, q);
        else
            return nullptr;
    }
};

六、二叉树的层序遍历1

题目描述:给你二叉树的根节点 root ,返回其节点值的 层序遍历。

链接:二叉树的层序遍历 

思路:既然是层序遍历,根据我们之前的经验,我们可以使用队列+循环的方式进行控制。但是,我们需要把每一层的数放在同一个数组中(不同层的数要在不同的数组中)。那么我们应该怎么来表示当前层的数已经全部进入同一个数组了呢?

以下面的图为例。首先,我们要明确的是:第一层只有一个节点,所以第一个数组只有一个数,所以只循环一次;在第一层的节点出队列后,第二层的节点已经进入队列,此时队列的元素个数就是第二层的元素个数,而第二层有两个节点,需要循环两次。

这里我们可以定义一个变量 LevelSize,来帮助我们记录每一层的节点个数。根据上面的规律,我们知道:每一层要循环节点个数次,当LevelSize为0时,表示该层已经遍历完,然后更新LevelSize的大小为队列元素个数。

代码实现:

class Solution 
{
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        queue<TreeNode*> q;
        vector<vector<int>> vv;
        int LeverSize = 1;

        if(root != nullptr)
        {
            q.push(root);
        }

        while(!q.empty())
        {
            vector<int> v;
            while(LeverSize--)
            {
                TreeNode* cur = q.front();

                if(cur->left != nullptr)
                    q.push(cur->left);

                if(cur->right != nullptr)
                    q.push(cur->right);

                q.pop();
                v.push_back(cur->val);
            }
            vv.push_back(v);
            LeverSize = q.size();
        }
        return vv;
    }
};

七、二叉树的层序遍历2

题目描述:给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。

链接:二叉树的层序遍历2

思路:最简单的方法就是使用题目六中的遍历方法,最后用逆置算法将数组逆置即可。

代码实现:

class Solution 
{
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) 
    {
        queue<TreeNode*> q;
        vector<vector<int>> vv;
        int LeverSize = 1;

        if(root != nullptr)
        {
            q.push(root);
        }
        while(!q.empty())
        {
            vector<int> v;
            while(LeverSize--)
            {
                TreeNode* cur = q.front();

                if(cur->left != nullptr)
                    q.push(cur->left);

                if(cur->right != nullptr)
                    q.push(cur->right);

                q.pop();
                v.push_back(cur->val);
            }
            vv.push_back(v);
            LeverSize = q.size();
        }
        reverse(vv.begin(), vv.end());
        return vv;
    }
};

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

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

相关文章

css之svg 制作圆及旋转

1.代码 <template><div class"loading-box"><div class"circle-container"><svg width"75" height"75" class"move-left-to-right"><circle cx"37.5" cy"37.5" r"26&…

leetcode42接雨水问题

接雨水 题目描述 题目分析 核心思想&#xff1a; 代码 java版本&#xff1a; package com.pxx.leetcode.trapRainWaterDoublePoniter;public class Solution1 {public int trap(int[] height) {if (height.length 0) {return 0;}int n height.length;int left 0;int righ…

Linux之高级IO

目录 IO基本概念五种IO模型钓鱼人例子五种IO模型高级IO重要概念同步通信 VS 异步通信阻塞 VS 非阻塞其他高级IO阻塞IO非阻塞IO IO基本概念 I/O&#xff08;input/output&#xff09;也就是输入和输出&#xff0c;在著名的冯诺依曼体系结构当中&#xff0c;将数据从输入设备拷贝…

2023亚马逊云科技re:Invent,与全球合作伙伴探索更多发展可能

一年一度的全球云计算、科技圈的狂欢“Party”又双叒叕要来了&#xff01;2023年11月27日&#xff0c;2023亚马逊云科技re:Invent正式向全球云计算从业者、合作伙伴发出邀请&#xff0c;相聚拉斯维加斯&#xff0c;共同开启一场创新探索之旅&#xff01; 全球合作伙伴相约拉斯维…

ffmpeg开发 环境配置

ffmpeg开发简图 1 下载ffmpeg开发包 https://ffmpeg.org/download.html 包含三个版本&#xff1a;Static、Shared以及Dev Static --- 包含3个应用程序&#xff1a;ffmpeg.exe , ffplay.exe , ffprobe.exe&#xff0c;体积都很大&#xff0c;相关的DLL已经被编译到exe里面去…

【Java】ThreadPoolExecutor类参数简述

ThreadPoolExecutor类继承自AbstractExecutorService类&#xff0c;而AbstractExecutorService实现了ExecutorService接口 ThreadPoolExecutor类是Executor类中重要的实现类 1、ThreadPoolExecutor构造方法参数 在手册中&#xff0c; 一共有四种参数列表不同的构造方法。我们…

【文末送书】程序员如何化解35岁危机?

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

网易云音频数据如何爬取?

在当今数字化时代&#xff0c;音频数据的获取和处理变得越来越重要。本文将详细介绍如何使用Objective-C语言构建音频爬虫程序&#xff0c;以爬取网易云音乐为案例。我们将从Objective-C的基础知识开始&#xff0c;逐步深入到爬取思路分析、构建爬虫框架、完整爬取代码等方面&a…

android trace文件的抓取与查看方法

本地手机抓取trace 解压android trace文件的抓取与查看方法 找到config.pbtx文件&#xff0c;连接手机push进去 # push config.pbtx &#xff0c;/data/local/tmp/为自定义push到的目录 adb push config.pbtx /data/local/tmp/ adb shell # 抓取trace&#xff0c; /data/loc…

Redis原理之五种数据类型笔记

目录 String List Set ZSet ​ Hash String List Set ZSet Hash

CTF图片隐写

1.题目给出的zip文件给出提示如下。 2.用 ARCHPR爆破出密码。 3.解压后发现1.png&#xff0c;为图片隐写。 4.使用010editor打开图片&#xff0c;发现缺少png文件头。 010editor官方下载链接&#xff1a;sweetscape.com/download/010editor/ 5.添加文件头保存。 6.使用图片隐写…

vue3+highlight.js代码高亮插件使用

先安装 npm install highlight.jsmain.js中引入&#xff0c;并注册自定义指令 ..... import hljs from highlight.js window.hljs hljs import highlight.js/styles/atom-one-light.css import highlight.js/lib/common import mitt from mitt .....app.directive(highlight…

HTML5+CSS3+JS小实例:九宫格图片鼠标移入移出方向感知特效

实例:九宫格图片鼠标移入移出方向感知特效 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport&…

鲲鹏测评:电视盒子什么品牌好?双十二口碑电视盒子品牌排行榜

最近我们收到很多网友的私信不知道电视盒子怎么选&#xff0c;想要我们推荐双十二值得入手的电视盒子&#xff0c;本年度我们已经共计测评过18款电视盒子&#xff0c;通过对比测评结果&#xff0c;我们整理了电视盒子品牌排行榜&#xff0c;双十二想买电视盒子的朋友们可不要错…

充电桩绝缘检测原理与示例

1、背景 充电桩绝缘检测是保证电动车充电安全的重要环节&#xff0c;通过对充电桩绝缘检测单租的测量和评估&#xff0c;来判断充电桩是否存在漏电等安全隐患&#xff0c;从而保证用户及周围环境的电器安全。 绝缘电阻&#xff1a;是指在特定的条件下&#xff0c;电气设备与接…

【Python数据结构与算法】--- 递归算法的应用 ---[乌龟走迷宫] |人工智能|探索扫地机器人工作原理

&#x1f308;个人主页: Aileen_0v0 &#x1f525;系列专栏:PYTHON数据结构与算法学习系列专栏&#x1f4ab;"没有罗马,那就自己创造罗马~" 目录 导言 解决过程 1.建立数据结构 2.探索迷宫: 算法思路 递归调用的“基本结束条件” 3.乌龟走迷宫的实现代码: …

【兔子王赠书第9期】ChatGPT进阶:提示工程入门

文章目录 写在前面ChatGPT推荐图书关键点编辑推荐内容简介推荐理由 粉丝福利写在后面 写在前面 人类一直在寻找、制造并使用工具&#xff0c;以扩展我们的能力&#xff0c;适应我们的环境&#xff0c;甚至超越我们的生物限制。现在&#xff0c;我们正站在一个历史性的分水岭之…

Doris-Stream Load(二十六)

Stream load 是一个同步的导入方式&#xff0c;用户通过发送 HTTP 协议发送请求将本地文件或数据流导入到 Doris 中。Stream load 同步执行导入并返回导入结果。用户可直接通过请求的返回体判断本次导入是否成功。 适用场景 Stream load 主要适用于导入本地文件&#xff0c;或…

【VUE】There are multiple modules with names that only differ in casing.

报错 There are multiple modules with names that only differ in casing. This can lead to unexpected behavior when compiling on a filesystem with other case-semantic. Use equal casing. Compare these module identifiers: 图示原因&#xff1a;大小写&#xff0c;有…

如何使用cpolar+Jellyfin自建私人影音平台【内网穿透】

&#x1f3a5; 个人主页&#xff1a;深鱼~ &#x1f525;收录专栏&#xff1a;cpolar &#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpo…