代码随想录打卡第14天第18天

二叉树

1 二叉树部分的一些新知

(1)二叉树的定义,C++方法一定要知道,相对于链表而言,二叉树就是多了两个指针,即左右子节点

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

(2)第二个是二叉搜索树和平衡二叉搜索树,这里预计五一放假回来后补坑(已经有栈和队列在STL的底层、优先队列、大小顶堆、KMP算法的笔记总结要补了,一定补上!)

2 二叉树的遍历

(1)递归遍历:
前序、中序、后序。(这里就是根节点遍历的位置区别)

每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!

确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数,并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

确定终止条件: 写完了递归算法,
运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

void pre_traversal(TreeNode* cur, vector<int>& vec){
	if(cur == NULL) return;
	vec.push_back(cur->val); 
	traversal(cur->left,vec);
	traversal(cur->right,vec);
}
void mid_traversal(TreeNode* cur, vector<int>& vec){
	if(cur == NULL) return;
	traversal(cur->left,vec);
	vec.push_back(cur->val); 
	traversal(cur->right,vec);
}
void behind_traversal(TreeNode* cur, vector<int>& vec){
	if(cur == NULL) return;
	traversal(cur->left,vec);
	traversal(cur->right,vec);
	vec.push_back(cur->val); 
}

以上就是递归遍历了,但是这不是最难的。

(2)二叉树的迭代遍历
迭代法的前序遍历:
递归的本质就是每一次递归调用都会将函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数。
那么前序遍历首先需要处理的是中间节点,那么先将根节点压入栈中,然后弹出根节点,再压入这个根节点的右子,再加入左子。最后按照顺序进行弹出。
但是要注意如果是空节点不应该压入栈中,需要跳过这个节点。

vector<int> preorderTraversal(TreeNode *root){
	stack<TreeNode*> st;
	vector<int> result;
	if(root == NULL) return result;
	st.push(root);
	while(!st.empty()){
		TreeNode* node = st.top();
		st.pop();
		result.push_back(node->val);
		if(node->right) st.push(node->right);
		if(node->left) st.push(node->left);
	}
	return result;
}

迭代法的中序遍历
中序遍历是左中右的顺序,因此需要从树的顶点开始一层一层向下遍历,直到访问到左树的最底部,然后再处理节点。因此处理顺序和访问顺序并不相同。

vector<int> inorderTraversal(TreeNode *root){
	vector<int> result;
	stack<TreeNode*> st;
	TreeNode* cur = root;
	while(cur != NULL || !st.empty()){ //条件变化的原因是现在遍历和存储要分开,因此cur用于进行遍历
		if(cur != NULL){
			st.push(cur); //从顶部开始,一路向最左遍历过去
			cur = cur->left; //左节点
		}
		else {
			cur = st.top(); //如果已经到了最左了,那么会进入这个分支,此时开始存储,即弹出左节点
			st.pop();
			//到了这个分支,我们可以发现也就是遍历到的这个cur节点一定没有左子节点,因此要存入result
			result.push_back(cur->val);//此时应该把这个节点当作中间节点来看待,那么这个节点被弹出之后,要看一下有没有右子节点。
			cur = cur->right;
			//那么此时的顺序就是左中右
		}
	}
	return result;
}

迭代法的后序遍历
后序遍历实际上是前序遍历的反面顺序,所以只需要先进行前序遍历,然后再反转数组。

vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right); // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }

3 二叉树的层序遍历

3.1 二叉树的最大深度

在这里插入图片描述

可以使用递归法对此题进行求解,其实二叉树的深度可以选择左子树的深度与右子树的深度的最大值+1。因此可以使用递归法,求解每一个根节点对应的深度,再递归回去。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

使用后序遍历来计算树的高度。参数需要传入这个树的根节点,返回这个根的高度。分别求取左右子树的深度,最后取左右深度最大的数值再+1。

class Solution {
public:
    int getdepth(TreeNode* node) {
        if (node == NULL) return 0;
        int leftdepth = getdepth(node->left);       // 左
        int rightdepth = getdepth(node->right);     // 右
        int depth = 1 + max(leftdepth, rightdepth); // 中
        return depth;
    }
    int maxDepth(TreeNode* root) {
        return getdepth(root);
    }
};

3.2 二叉树的层序遍历

在这里插入图片描述
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。

需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。

而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。

遍历节点时,当这个节点要出去的时候,就应该查询其左右子,并放入队列。

vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        vector<vector<int>> result;
        while (!que.empty()) {
            int size = que.size();
            vector<int> vec;
            // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            result.push_back(vec);
        }
        return result;
    }

3.4 翻转二叉树

在这里插入图片描述
换个思路来看,就是将二叉树的层序遍历顺序颠倒,即先右后左,然后继续按队列的方式进行遍历

递归方法

  1. 确定递归函数的参数和返回值

参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。

返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode*。

TreeNode* invertTree(TreeNode* root)
  1. 确定终止条件
    当前节点为空的时候,就返回 if (root == NULL) return root;
  2. 确定单层递归的逻辑
    因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);

因此可以得到最后的代码:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        swap(root->left, root->right);  // 中
        invertTree(root->left);         // 左
        invertTree(root->right);        // 右
        return root;
    }
};

迭代方法
可以利用前序遍历,对代码结构进行调整:

TreeNode* invertTree(TreeNode* root){
	stack<TreeNode*> st;
	if(root != NULL) st.push(root);
	while(!st.empty()){
		TreeNode* node = st.top();
		if(node != NULL){
			st.pop();
			// 可以这么去理解,前序遍历中我们是从左到右进行遍历,那么我们需要由根节点获得左右子树,然后对左子树和右子树进行翻转即可。
			// 体现在代码当中就是push右子和左子之后将根放回去,然后在根前面夹一个NULL使得条件转移到交换代码当中,完成左右结点的交换。
			if(node->right) st.push(node->right);
			if(node->left) st.push(node->left);
			st.push(node);
			st.push(NULL);
		}
		else{
			st.pop();
			node = st.top();
			st.pop();
			swap(node->left,node->right);
		}
	}
	return root;
}

广度优先遍历

TreeNode* invertTree(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                swap(node->left, node->right); // 节点处理
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return root;
    }

3.5 对称二叉树

在这里插入图片描述
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
因此我们只需要对左子和右子进行比较即可,采用后序遍历。正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。

递归三部曲(参考代码随想录)

  • 确定递归函数的参数和返回值
    • 因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。
    • 返回值自然是bool类型。代码如下:bool compare(TreeNode* left, TreeNode* right)
  • 确定终止条件
    • 要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
    • 节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)
      • 左节点为空,右节点不为空,不对称,return false
      • 左不为空,右为空,不对称 return false
      • 左右都为空,对称,返回true
    • 此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
      • 左右都不为空,比较节点数值,不相同就return false
    • 此时左右节点不为空,且数值也不相同的情况我们也处理了。

代码如下:

if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
else if (left->val != right->val) return false; // 注意这里我没有使用else

注意上面最后一种情况,我没有使用else,而是else if, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。

  • 确定单层递归的逻辑
    此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
    • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
    • 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
    • 如果左右都对称就返回true ,有一侧不对称就返回false 。

代码如下:

bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
bool isSame = outside && inside;                    // 左子树:中、 右子树:中(逻辑处理)
return isSame;

最终代码

bool compare(TreeNode* left, TreeNode* right) {
        // 首先排除空节点的情况
        if (left == NULL && right != NULL) return false;
        else if (left != NULL && right == NULL) return false;
        else if (left == NULL && right == NULL) return true;
        // 排除了空节点,再排除数值不相同的情况
        else if (left->val != right->val) return false;

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
        bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
        bool isSame = outside && inside;                    // 左子树:中、 右子树:中 (逻辑处理)
        return isSame;

    }
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        return compare(root->left, root->right);
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SSL证书中DV、OV和EV有什么区别,又该如何选择

SSL&#xff08;安全套接层&#xff09;证书作为一种加密工具&#xff0c;确保了网站与其用户之间传输的信息的安全性。而在选择SSL证书时&#xff0c;我们通常会看到三种类型&#xff1a;域名验证&#xff08;DV&#xff09;、组织验证&#xff08;OV&#xff09;和扩展验证&a…

西门子数控网络IP设定配置

总结&#xff1a;menuselect-诊断-屏幕下方右翻页找到tcp/ip&#xff0c;进去选择tcp/ip诊断&#xff0c;进去选择x130网口&#xff0c;点击更改&#xff0c; 如果没有更改&#xff0c;menuselect-调试-口令&#xff0c;输入口令 sunrise 然后重新配置tcp/ip&#xff0c;配置完…

保姆级教学 基于Hexo搭建个人网站(Github)

文章目录 搭建Hexo静态博客介绍一、注册Github账号二、 安装前置软件包三、 绑定github仓库创建SSH私钥添加私钥连接Github仓库 四、安装hexo1. 更改npm镜像源2. 创建一个文件夹 在里面打开终端3. 初始化hexo 五、切换主题1. 安装主题2. 修改默认主题查看修改主题后的网站 六、…

杭州恒生面试,社招,3年经验

你好&#xff0c;我是田哥 一位朋友节前去恒生面试&#xff0c;其实面试问题大部分都是八股文&#xff0c;但由于自己平时工作比较忙&#xff0c;完全没有时间没有精力去看八股文&#xff0c;导致面试结果不太理想&#xff0c;HR说节后通知面试结果&#xff08;估计是凉了&…

vue2编写主体页面

目录 一. 导入两张图片 二. 新建主体vue 三. 修改路由 1. 新增主体界面Main.vue的路由 2. 完整router/index.js代码如下: 在Vue 2中编写一个主体页面通常意味着创建一个包含导航栏、侧边栏、内容区域等的布局。以下是使用Vue 2和Element UI框架来构建一个简单的主体页面的…

uniapp/微信小程序实现加入购物车点击添加飞到购物车动画

1、预期效果 2、实现思路 每次点击添加按钮时&#xff0c;往该按钮上方添加一个悬浮元素&#xff0c;通过位移动画将元素移到目标位置。 1. 为每个点击元素设置不同的class&#xff0c;才能通过uni.createSelectorQuery来获取每个元素的节点信息&#xff1b; 2. 添加一个与…

Springai入门

一、概述 1.1发展历史 1.2大模型 大模型&#xff0c;是指具有大规模参数和复杂计算结构的机器学习模型。这些模型通常由深度神经网络构建而成&#xff0c;拥有数十亿甚至数千亿个参数。其设计目的在于提高模型的表达能力和预测性能&#xff0c;以应对更加复杂的任务和数据&…

AWS宣布推出Amazon Q :针对商业数据和软件开发的生成性AI助手

亚马逊网络服务&#xff08;AWS&#xff09;近日宣布推出了一项名为“Amazon Q”的新服务&#xff0c;旨在帮助企业利用生成性人工智能&#xff08;AI&#xff09;技术&#xff0c;优化工作流程和提升业务效率。这一创新平台的推出&#xff0c;标志着企业工作方式的又一次重大变…

JetBrains DataSpell v2024.1 激活版 (数据科学家开发环境)

JetBrains系列软件安装目录 一、JetBrains IntelliJ IDEA v2024.1 安装教程 (Java集成开发IDE) 二、JetBrains WebStorm v2024.1 激活版 (JavaScript集成开发IDE) 三、JetBrains PhpStorm v2024.1 安装教程 (PHP集成开发IDE) 四、JetBrains PyCharm Pro v2024.1 安装教程 (…

burp靶场sql注入通关—下

第十一关&#xff08;布尔盲注&#xff09;&#xff1a; 1.根据提示修改包含 TrackingId cookie的请求&#xff0c;先抓包并修改这个值&#xff0c;在后面加上永真式发现出现Welcome back TrackingIdxxxx and 11 再修改这个值为永假式看看&#xff0c;发现没有Welcome back&am…

第二证券|A股,突现两大重磅!

大摩又看上新标的&#xff01; 就在刚才&#xff0c;五粮液和泸州老窖传来重磅音讯&#xff1a;摩根士丹利将泸州老窖目标价从194元上调至206元&#xff0c;将五粮液目标价从180元上调至182元。按这个目标价核算&#xff0c;五粮液还有20%的空间。而摩根士丹利最近似有一种魔力…

【触摸案例-手势解锁案例-按钮高亮 Objective-C语言】

一、我们来说这个self.btns,这个问题啊,为什么不用_btns, 1.我们说,在懒加载里边儿,经常是写下划线啊,_btns,为什么不写,首先啊,这个layoutSubviews:我们第一次,肯定会去执行这个layoutSubviews: 然后呢,去懒加载这个数组, 然后呢,接下来啊,走这一句话, 第一次…

rust容器、迭代器

一&#xff0c;std容器 1&#xff0c;Vec&#xff08;向量、栈&#xff09; use std::vec::Vec; &#xff08;1&#xff09;用作vector let nums:Vec<i32>vec![1,2,4,3];assert_eq!(nums.len(),4);assert_eq!(nums[3],3);assert_eq!(nums.is_empty(),false); 遍历&…

山西·长治大广赛赛题详解

大广赛新命题又又又又来啦&#xff0c;它就是山西长治&#xff0c;让我们一起来看看命题详情吧~ 2024年第16届全国大学生广告艺术大赛山西长治命题素材和资料下载&#xff1a; 命题素材下载https://js.design/f/ZRLbti?sourcecsdn&planbttss507 广告主题&#xff1a; 一…

美易官方:美股周一收高,道指连续第四个交易日上涨

收盘之际&#xff0c;美股市场周一的表现可圈可点&#xff0c;各大股指纷纷走高&#xff0c;道指更是连续第四个交易日实现上涨。这一积极态势不仅凸显了投资者对于全球经济的信心&#xff0c;也反映了市场对于未来前景的乐观预期。 道指涨176.59点&#xff0c;涨幅为0.46%&…

探索5个独特AI工具:它们是否值得独立存在?

在这个“地下AI”系列的最新一集中&#xff0c;我们深入挖掘了一些鲜为人知的AI工具。这些工具并非出自OpenAI、微软或谷歌等科技巨头之手&#xff0c;而是独立创造者和小型团队的智慧结晶。我们的目标是发现利用最新AI技术的独特工具。但这次有个新玩法&#xff1a;我们玩一个…

使用 OpenNJet 分分钟完成打地鼠小游戏部署

文章目录 OpenNJet应用引擎什么是应用引擎什么是OpenNJet独特优势技术架构 OpenNJet安装RPM安装 部署打地鼠小游戏配置OpenNJet部署打地鼠小游戏启动 NJet访问打地鼠小游戏 总结 今天阿Q打算使用OpenNJet应用引擎来部署一下我们的打地鼠小游戏。在开始部署之前&#xff0c;我们…

FME学习之旅---day26

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 【由于上周&#xff0c;上班状态不是很好&#xff0c;事情多又杂&#xff0c;没有学习的劲头&#xff0c;就短暂的休息了一下下。双休爬山&#xff0c;给自己上了强度&#xff0c;今天才缓过来…

PMP培训一般要多久?

考过PMP很久了&#xff0c;学习时长还是记得很清楚的。因为有一部分的项目经验&#xff0c;报了威班PMP的培训&#xff0c;看了宣传是50天通过PMP&#xff0c;但是我仅仅用了一个月出头就搞定了&#xff0c;算下来才四十天不到就已经学完在准备冲刺参加考试了&#xff0c;最后5…

气膜冰球馆助力冰雪运动高速发展—轻空间

冰上运动一直备受人们热爱&#xff0c;其中冰球更是广受欢迎。近年来&#xff0c;随着技术的飞速发展&#xff0c;气膜冰球馆成为了冰上运动领域的新宠。本文将探讨气膜冰球馆在冰上运动中的重要性&#xff0c;以及其未来发展的前景。 气膜冰球馆具有明显的优势。相比传统冰球馆…