【刷题(14)】二叉树

一、二叉树基础

/*** 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> rightSideView(TreeNode* root) {vector<int> ret;if(root){queue<TreeNode*> que;que.push(root);while(!que.empty()){int size=que.size();while(size--){//1.取当前层的auto node=que.front();que.pop();//2.让入下一层的//左if(node->left) que.push(node->left);//右if(node->right) que.push(node->right);//中//3.业务逻辑//加入每一层的最后一个节点if(size==0) ret.push_back(node->val);}}}return ret;}
};
class Solution {
private://统计遍历的节点个数int cnt=0;//存储第k小的节点值int res=-1;void dfs(TreeNode* node,int k){if(!node) return;//左dfs(node->left,k);//中//业务逻辑//如果遍历的节点树到达k,说明这个节点就是第k小的节点if(++cnt==k){res=node->val;return;}//右dfs(node->right,k);}
public:int kthSmallest(TreeNode* root, int k) {dfs(root,k);return res;}
};

技巧:使用成员变量,让递归中不使用额外变量参与递归。
二叉树的创建
二叉树是非线性的结构,创建一棵二叉树必须首先确定树中结点的输入顺序,常有的方法是先序创建和层序创建。

TreeNode* buildTree(vector<int>& nums, int left,int right)
{if(left>=right) return nullptr;int mid=left+((right-left)>>1);//创建根节点并递归生成子树return new TreeNode(nums[mid],buildTree(nums,left,mid),buildTree(nums,mid+1,right));}

(一)先序创建
/按先序遍历创建二叉树/

typedef struct BinNode
{ElementType data;BinNode *Left;BinNode *Right;
}BinTree;BinTree *CreatePre(BinTree* &BT)
{char ch;cout<<"ch=";cin>>ch;if(ch=='0')BT=NULL;else{BT=new BinTree;BT->data=ch;BT->Left=CreatePre(BT->Left);BT->Right=CreatePre(BT->Right);}return(BT);
}

树的遍历方式总体分为两类:深度优先搜索(DFS)、广度优先搜索(BFS)。

  • 常见 DFS : 先序遍历、中序遍历、后序遍历。
  • 常见 BFS : 层序遍历(即按层遍历)。
- /*** 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:int maxDepth(TreeNode* root) {//特殊处理if(root==nullptr) return 0;//初始化vector<TreeNode*> que;que.push_back(root);int res=0;//循环遍历: 当 queue 为空时跳出。while(!que.empty()){//初始化一个空列表 tmp ,用于临时存储下一层节点。vector<TreeNode*> tmp;//遍历队列: 遍历 queue 中的各节点 node ,并将其左子节点和右子节点加入 tmp。for(TreeNode* node:que){if(node->left!=nullptr) tmp.push_back(node->left);if(node->right!=nullptr) tmp.push_back(node->right);}//更新队列: 执行 queue = tmp ,将下一层节点赋值给 queue。que=tmp;//统计层数: 执行 res += 1 ,代表层数加 111。res++;}//返回值: 返回 res 即可。return res;}
};

二叉树遍历:

顺着一条搜索路径访问二叉树中的节点,每个节点均被访问一次,且只被访问一次。

遍历目的:

得到树中所有节点的一个线性排列。

遍历用途:

是二叉树元素增删改查等操作的前提。
  在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//定义节点
typedef struct BiNode{ElemType data;      //数据域struct BiNode *lchild, *rchild;         //左右孩子指针
}BiNode, *BiTree;
//二叉树先序遍历算法 - 根 左 右int PreOrderTraverse(BiTree T){if( T ==   NULL){       //若是空二叉树,则直接返回0return 1;}else{visit(T);       //访问根节点(自定义visit()方法,比如获取该节点的数据域)PreOrderTraverse(T->lchild);        //遍历递归左子树PreOrderTraverse(T->rchild);        //遍历递归右子树}}//二叉树中序遍历算法 - 左 根 右int InOrderTraverse(BiTree T){if( T == NULL ){        //若是空二叉树,则直接返回return 1;}else{InOrderTraverse(T->lchild);     //遍历递归左子树visit(T);       //访问根节点InOrderTraverse(T->rchild);     //遍历递归右子树}}//二叉树后序遍历算法 - 左 右 根int PostOrderTraverse(BiTree T){if( T == NULL ){return 1;}else{PostOrderTraverse(T->lchild);       //遍历递归左子树PostOrderTraverse(T->rchild);       //遍历递归右子树visit(T);       //访问根节点}}

二、94. 二叉树的中序遍历

1 题目

在这里插入图片描述

2 解题思路

(1)二叉树的 中序遍历: 从根节点开始,首先遍历左子树,然后访问根节点,最后访问右子树。然后在遍历左子树的时候,同样首先遍历左子节点的左子树,然后访问根节点,最后遍历左子节点的右子树…

图解这个遍历过程:
在这里插入图片描述
(2)递归法
我们从根节点开始,我们先处理根节点的左子树,再处理根节点,最后处理根节点的右子树。而处理根节点的左子树时,我们把根节点的左子节点当成根节点,先处理左子节点的左子树,再处理左子节点,最后处理左子节点的右子树…依次类推

我们可以看到,对于每个节点的处理过程是一致的。先处理这个节点的左子树,再处理这个节点,最后处理这个节点的右子树,那么我们可以把这个过程封装成一个函数:

递归处理节点的左子节点;
添加节点的值;
递归处理节点的右子节点;
递归的关键是递归终点:当处理的节点是一个空节点时,说明以这个节点为根的子树是个空子树,无法处理,递归结束。
(3)迭代法
递归法其实隐式维护了一个栈结构:我们一直递归寻找最左侧的节点,直到找到后,再处理之前找到的节点。

因此迭代法我们可以使用一个栈结构,迭代的寻找当前节点的左子节点,直到找到最左侧的子节点,弹出处理。然后再处理这个节点的右子节点。

图解这个算法过程:
在这里插入图片描述

3 code

递归法

/*** 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 {
private:void dfs(TreeNode* node, vector<int>& res){if(!node) return;//先处理左子树dfs(node->left,res);//再处理当前根节点res.emplace_back(node->val);//最后处理右子树dfs(node->right,res);}
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> res;dfs(root,res);return res;}
};

迭代法

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> res;  TreeNode* node = root;stack<TreeNode*> st;// 节点不为空或栈内有节点时,说明还有节点未遍历while(!st.empty() || node){// 中序遍历,优先遍历当前node为根的子树的最左侧节点while(node){st.push(node);node = node->left;}node = st.top();    // 获取当前节点st.pop();           // 弹出栈顶节点res.emplace_back(node->val);node = node->right;  // 遍历node的右子树}return res;}
};

三、104. 二叉树的最大深度

1 题目

在这里插入图片描述

2 解题思路

方法一:后序遍历(DFS)

树的后序遍历 / 深度优先搜索往往利用 递归 或 栈 实现,本文使用递归实现。
关键点: 此树的深度和其左(右)子树的深度之间的关系。显然,此树的深度 等于 左子树的深度 与 右子树的深度中的 最大值 +1+1+1 。
在这里插入图片描述

算法解析:

终止条件: 当 root​ 为空,说明已越过叶节点,因此返回 深度 000 。
递推工作: 本质上是对树做后序遍历。
计算节点 root​ 的 左子树的深度 ,即调用 maxDepth(root.left)。
计算节点 root​ 的 右子树的深度 ,即调用 maxDepth(root.right)。
返回值: 返回 此树的深度 ,即 max(maxDepth(root.left), maxDepth(root.right)) + 1。

在这里插入图片描述

方法二:层序遍历(BFS)

树的层序遍历 / 广度优先搜索往往利用 队列 实现。

关键点: 每遍历一层,则计数器 +1+1+1 ,直到遍历完成,则可得到树的深度。

算法解析:

特例处理: 当 root​ 为空,直接返回 深度 000 。
初始化: 队列 queue (加入根节点 root ),计数器 res = 0。
循环遍历: 当 queue 为空时跳出。
初始化一个空列表 tmp ,用于临时存储下一层节点。
遍历队列: 遍历 queue 中的各节点 node ,并将其左子节点和右子节点加入 tmp。
更新队列: 执行 queue = tmp ,将下一层节点赋值给 queue。
统计层数: 执行 res += 1 ,代表层数加 111。
返回值: 返回 res 即可。
在这里插入图片描述

3 code

方法一:后序遍历(DFS)

/*** 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:int maxDepth(TreeNode* root) {if(root==nullptr) return 0;int l=maxDepth(root->left);//error//int depth=max(l,r)+1;int r=maxDepth(root->right);//业务逻辑需要用到左右子树,所以只能后序遍历int depth=max(l,r)+1;return depth;}
};

方法二:层序遍历(BFS)

/*** 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:int maxDepth(TreeNode* root) {//特殊处理if(root==nullptr) return 0;//初始化vector<TreeNode*> que;que.push_back(root);int res=0;//循环遍历: 当 queue 为空时跳出。while(!que.empty()){//初始化一个空列表 tmp ,用于临时存储下一层节点。vector<TreeNode*> tmp;//遍历队列: 遍历 queue 中的各节点 node ,并将其左子节点和右子节点加入 tmp。for(TreeNode* node:que){if(node->left!=nullptr) tmp.push_back(node->left);if(node->right!=nullptr) tmp.push_back(node->right);}//更新队列: 执行 queue = tmp ,将下一层节点赋值给 queue。que=tmp;//统计层数: 执行 res += 1 ,代表层数加 111。res++;}//返回值: 返回 res 即可。return res;}
};

四、226. 翻转二叉树

1 题目

在这里插入图片描述

2 解题思路

方法一:深度优先搜索(DFS)【递归法/自底向上】

自底向上依次翻转每一个节点的左右子节点。
在这里插入图片描述

方法二:广度优先搜索(BFS)【迭代法/自顶向下】

自顶向下一层一层的翻转每一个节点的左右子节点。
在这里插入图片描述

3 code

方法一:深度优先搜索(DFS)【递归法/自底向上】

class Solution {
public:TreeNode* invertTree(TreeNode* root) {if(!root) return root;auto temp = invertTree(root->left);root->left=invertTree(root->right);root->right=temp;return root;}
};

方法二:广度优先搜索(BFS)【迭代法/自顶向下】

class Solution {
public:TreeNode* invertTree(TreeNode* root) {if(!root) return root;queue<TreeNode*> q;q.push(root);int size;TreeNode* node;TreeNode* temp;while(!q.empty()){//获取每一层的节点数size=q.size();//依次弹出这一层的size个节点while(size-->0){node=q.front();q.pop();//交换节点的左右子节点temp=node->left;node->left=node->right;node->right=temp;//左右子节点不为空的,加入队列作为下一层处理的节点if(node->left) q.push(node->left);if(node->right) q.push(node->right);}}return root;}
};

四、101. 对称二叉树

1 题目

在这里插入图片描述

2 解题思路

这道题要判断一个二叉树是否对称,关键有两点:

如何构成对称;
如何找到对应位置的节点。即找到每次要判断是否对称的两个节点;
第一步比较容易解决,对称的依据是 要么对应位置上的节点都为空,要么都不为空且值相等。
而如何找到每次要判断对称的两个节点呢?从图上来看:

在这里插入图片描述
每一次比较对称的节点对,都是当前节点对一个的左子节点和另一个的右子节点。而初始比较对称的节点对为根节点的左右子节点。

方法一:深度优先搜索(DFS)【递归法/自底向上】

每一次判断两个节点是否对称:

  1. 两个节点都为空,对称;
  2. 存在一个为空一个不为空,或者两个不为空但值不相等,不对称;
  3. 否则就递归比较两个节点的子节点,即节点1的左子节点和节点2的右子节点,以及节点1的右子节点和节点2的左子节点。

方法二:广度优先搜索(BFS)【迭代法/自顶向下】

广度优先搜索对节点判断是否对称的方式不变,而是遍历的方式发生了改变。通过队列存储每一层需要比较对称的节点。

即每一次从队列取出两个节点,由于队列中不能存储空节点,因此我们取出的两个节点并不是要比较的节点对,而是已经比较过的节点对。真正要比较的是这两个节点的子节点,即节点1的左子节点和节点2的右子节点,以及节点1的右子节点和节点2的左子节点。

初始我们比较根节点的左右子节点是否对称,非空且对称,则加入队列进行其子节点的比较。
在这里插入图片描述

3 code

方法一:深度优先搜索(DFS)【递归法/自底向上】

class Solution {
public:bool isSymmetric(TreeNode* root) {if(!root) return true;return check(root->left,root->right);}bool check(TreeNode* node1,TreeNode* node2){//当前判断对称的两个节点:节点1和节点2if(!node1 && !node2) return true;//接下来要判断对称的每一对节点//1.节点1的左子节点和节点2的右子节点//2.节点1的右子节点和节点2的左子节点if(!node1 || !node2 || node1->val != node2->val) return false;//两个节点相同值相同--对称,递归比较其子节点return check(node1->left,node2->right) && check(node1->right,node2->left);}
};

方法二:广度优先搜索(BFS)【迭代法/自顶向下】

class Solution {
public:queue<TreeNode*> q;bool isSymmetric(TreeNode* root) {//判断根节点if(!root) return true;//判断根节点的左右子节点if(!check(root->left,root->right)) return false;while(!q.empty()){//从根节点的左右子节点开始,依次比较TreeNode* node1=q.front();q.pop();TreeNode* node2=q.front();q.pop();if(!check(node1->left,node2->right)) return false;if(!check(node1->right,node2->left)) return false;}return true;}bool check(TreeNode* node1,TreeNode* node2){//当前判断对称的两个节点:节点1和节点2if(!node1 && !node2) return true;//接下来要判断对称的每一对节点//1.节点1的左子节点和节点2的右子节点//2.节点1的右子节点和节点2的左子节点if(!node1 || !node2 || node1->val != node2->val) return false;q.emplace(node1);q.emplace(node2);return true;}
};

五、543. 二叉树的直径

1 题目

在这里插入图片描述

2 解题思路

之前应该做过一个很类似的题,还是用遍历加改变节点值的方法。

首先明确怎么确定这个最大路径,很简单可以思考到,这个最大路径肯定有一个’根节点’,这个根节点不一定是原二叉树的根节点,意思是从该根节点往左右节点延申到叶子节点得到的路径肯定是最长的路径,因为如果没有这个根节点,只算一边肯定不能是最大路径,因为还可以往另一边延申,如果不到叶子节点肯定也不对,因为还可以向下走,就不能算最大路径。

lz最开始的思路是用前序遍历,从根节点遍历每一个节点,算每个节点左右子树的最大高度之和就是从该根节点能达到的最大路径,但是这种方式我们每次都要重新遍历节点,每次都要重新算从某个节点往下延伸的最大高度,这样上一次算出来的最大路径在后面也不能用,所以考虑使用后序遍历的方式,先遍历最下面的,然后将算出来的高度作为节点的值,这样上面一个节点的最大路径就是左节点的高度加上右节点的高度由于我们是从下往上算的,就不需要重复算,直接读我们存的值就可以了。

3 code

class Solution {
public:int ans;//后序遍历int depth(TreeNode* node){if(!node) return 0;int L=depth(node->left);int R = depth(node->right);//后序遍历,最大深度int dep = max(L,R)+1;//用ans变量记录最长距离,为左右子树的最大深度之和ans = max(ans,L+R+1);//返回最大深度return dep;}int diameterOfBinaryTree(TreeNode* root) {ans=1;depth(root);return ans-1;}
};

六、102. 二叉树的层序遍历

1 题目

在这里插入图片描述

2 解题思路

对二叉树的层序遍历,其实是广度优先搜索的一种表现形式。
假设我们已经维护了某一层的节点,当我们在遍历这一层节点的时候,这一层所有节点的子节点即为下一层要遍历的节点。

因此我们在遍历这一层节点的同时,还需要把其子节点(如果存在的话)储存起来作为下一层遍历的节点。由于这一层先遍历到的节点,其子节点先储存并且在下一层也应该是先遍历到的。即符合“ 先入先出 ”,应该使用 队列 进行存储。

因此我们一边从队首中弹出当前层的节点进行遍历,一边把下一层的节点加入队尾作为下一层遍历的节点。由于队列中的节点个数始终在变,因此我们应该明确这一层节点和下一层节点的交界。
很明显,当我们遍历完一层时,队列中的节点应该全是下一层遍历的节点。因此在下一层开始遍历之前,我们先记录队列中的节点个数,一旦弹出的节点个数达到记录值,这一层遍历结束。

初始的时候,我们把根节点入队,队列中储存第一层的唯一元素。
在这里插入图片描述

3 code

class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {//所有层结果vector<vector<int>> ret;if(root==nullptr) return ret;queue<TreeNode*> que;que.push(root);while(!que.empty()){//一层结果vector<int> tmp;int sz=que.size();while(sz--){TreeNode* node_ =que.front();que.pop();tmp.push_back(node_->val);if(node_->left) que.push(node_->left);if(node_->right) que.push(node_->right);}ret.push_back(tmp);}return ret;}};

七、102. 二叉树的层序遍历

1 题目

在这里插入图片描述

2 解题思路

这道题要根据给定的升序数组构造一个二叉搜索树。

二叉搜索树的特征是:对于树上的任意一个节点,其左子树的所有节点值都小于它,其右子树的所有节点值都大于它。而给定数组 nums 是一个严格递增的数组,对于其中任意一个数字,其左侧的数字都小于它,其右侧的数字都大于它。
在这里插入图片描述
因此我们可以选择数组 [0, n) 中任意一个元素 nums[i] 作为根节点,那么其左子树就由 [0, i) 的 nums 的元素值构成,其右子树就由 [i+1, n) 的 nums 的元素值构成。

而对于左子树 [0, i) 或者右子树 [i+1, n) 要选择一个元素作为子树的根节点,其方法与上述生成整棵树的根节点的方法一致。

分治

现在问题就是选择范围里哪个元素作为根节点?

题目要求得到一棵高度平衡的二叉搜索树,即任意一个节点的左右子树高度差绝对值不超过1。我们可以假设绝对一点,根节点的左右子树都是链式结构,那么左右子树的高度就是左右子树节点个数。要使高度差绝对值不超过 1,即 左右子树的节点数差值绝对值不超过 1。

而左右子树的节点数就对应选取元素两侧的元素数,因此应该选择 [0, n) 的中点坐标 n / 2 (结果向下取整) 作为根节点:

  • n 为奇数,n / 2 刚好中间元素的索引,左右区间的元素个数相等;
  • n 为偶数,n / 2 为中间两个元素靠右的那个元素,左区间比右区间多一个元素。
    在这里插入图片描述

而对于根节点的左右子树,我们可以以同样的策略生成子树的根节点,作为根节点的左子节点和右子节点。即我们每次对区间 [left, right) 的元素构建子树。当区间不存在时(left > right),即为递归终点
在这里插入图片描述

3 code

/*** 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* buildTree(vector<int>& nums, int left,int right){if(left>=right) return nullptr;int mid=left+((right-left)>>1);//创建根节点并递归生成子树return new TreeNode(nums[mid],buildTree(nums,left,mid),buildTree(nums,mid+1,right));}TreeNode* sortedArrayToBST(vector<int>& nums) {return buildTree(nums,0,nums.size());}
};

八、98. 验证二叉搜索树

1 题目

在这里插入图片描述

2 解题思路

首先我们要知道的一点:二叉搜索树的中序遍历的结果是一个升序序列。【二叉搜索树任意一个节点的左子树的节点值都小于当前节点,其右子树的节点值都大于当前节点。而中序遍历刚好是先处理左子树再处理当前节点最后处理右子树】

在这里插入图片描述
根据 中序遍历 的处理策略,我们 只需要替换对当前节点的处理策略,即在完成对二叉搜索树的中序遍历同时进行其他操作。

对于这道题,我们要判断二叉树是否是一颗二叉搜索树,而二叉搜索树的中序遍历是一个升序序列。因此我们可以判断当前节点的值是否大于前一个遍历节点的值,满足了说明这个节点是满足二叉搜索树条件的,否则就不满足。

即我们记录上一个遍历节点的值,对当前节点的处理策略就是比较当前节点值和上一个节点值,前者大于后者即为满足条件。

类似题目
530.二叉搜索树的最小绝对差
230. 二叉搜索树中第K小的元素

代码

由于首个节点没有上一个节点,为了统一计算,我们可以初始上一个节点为一个极小值,这样首个节点更新时,其与上一个节点的差值将是一个极大值,从而不会影响最小绝对差的更新。
这道题节点的取值范围为int,因此我们可以使用更大的数据类型 long 来获得最小值。

3 code

/**

  • 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 {
    private:
    long long lastVal=LONG_MIN;
    bool ret=true;
    //中序遍历
    bool dfs(TreeNode
    node)
    {
    if(!node) return true;

     //左if(!dfs(node->left) || lastVal>=node->val) return false;//中lastVal=node->val;//右bool xx=dfs(node->right);return xx;
    

    };

public:
bool isValidBST(TreeNode* root)
{
ret=dfs(root);
return ret;
}
};

# 九、230. 二叉搜索树中第K小的元素
## 1 题目
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e2e74839203545bda1bb0477bea5323c.png)## 2 解题思路
首先我们要知道的一点:二叉搜索树的中序遍历的结果是一个升序序列。【二叉搜索树任意一个节点的左子树的节点值都小于当前节点,其右子树的节点值都大于当前节点。而中序遍历刚好是先处理左子树再处理当前节点最后处理右子树】![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d280691714a74ff8878fa67dc3902e6c.png)
根据 中序遍历 的处理策略,我们 只需要替换对当前节点的处理策略,即可完成对二叉搜索树的中序遍历。对当前节点的处理策略就是我们要记录当前遍历到的节点是第几个节点,如果遍历到第 k 个节点则返回当前节点的值。因此我们需要一个变量统计遍历到第几个节点。![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d7f581c10a10481fb835ede3a45f0100.png)## 3 code```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 {
private://统计遍历的节点个数int cnt=0;//存储第k小的节点值int res=-1;void dfs(TreeNode* node,int k){if(!node) return;//左dfs(node->left,k);//中//业务逻辑//如果遍历的节点树到达k,说明这个节点就是第k小的节点if(++cnt==k){res=node->val;return;}//右dfs(node->right,k);}
public:int kthSmallest(TreeNode* root, int k) {dfs(root,k);return res;}
};

九、199. 二叉树的右视图

1 题目

在这里插入图片描述

2 解题思路

(1)从顶部到底部,说明要使用BFS广度优先搜索
(2)最右侧的值可以通过一个成员变量来维护
在这里插入图片描述

3 code

/*** 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> rightSideView(TreeNode* root) {vector<int> ret;if(root){queue<TreeNode*> que;que.push(root);while(!que.empty()){int size=que.size();while(size--){//1.取当前层的auto node=que.front();que.pop();//2.让入下一层的//左if(node->left) que.push(node->left);//右if(node->right) que.push(node->right);//中//3.业务逻辑//加入每一层的最后一个节点if(size==0) ret.push_back(node->val);}}}return ret;}
};

十、114. 二叉树展开为链表

1 题目

在这里插入图片描述

2 解题思路

这道题咋一看思路比较简单,就是根据二叉树前序遍历的顺序,将节点依次串起来。
我们可以使用一个节点node表示当前展开后链表的尾部节点。那么我们每遍历到一个节点,只要把节点追加到node的尾部即可。
先序遍历(前序遍历)的遍历顺序为:中左右【即先处理当前节点,再依次处理其左子节点和右子节点】

在这里插入图片描述
但是这其中有个问题,展开链表的各个节点是通过right进行来连接的。因此在展开的过程中,部分节点的right被更新,导致原有的右子树信息丢失而无法进行正确的前序遍历。【如上图的节点1,其右子树还没有遍历,其右子节点已经变成了节点2】

因此我们在进行递归之前,需要对当前节点的右子树进行暂存。保证在左子树递归后,该节点的右子树信息还能找到,从而进行右子树的递归。

3 code

/*** 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* node;//前序遍历void flatten(TreeNode* root) {if(!root) return;//中//业务逻辑if(!node){node=root;}else{   //左节点置空node->left=nullptr;node->right=root;node=node->right;}//左//暂存节点的右子树,避免递归过程中右子树信息丢失TreeNode* r = root->right;flatten(root->left);//右flatten(r);}
};

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

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

相关文章

【并发程序设计】12.内存映射

12.内存映射 使一个磁盘文件与内存中的一个缓冲区相映射&#xff0c;进程可以像访问普通内存一样对文件进行访问&#xff0c;不必再调用read,write&#xff0c;更加高效。 用到的函数 mmap函数 原型&#xff1a; #include <sys/mman.h> void* mmap(void* start, size_…

【GD32】05 - PWM 脉冲宽度调制

PWM PWM (Pulse Width Modulation) 是一种模拟信号电平的方法&#xff0c;它通过使用数字信号&#xff08;通常是方波&#xff09;来近似地表示模拟信号。在PWM中&#xff0c;信号的占空比&#xff08;即高电平时间占整个周期的比例&#xff09;被用来控制平均输出电压或电流。…

MFC 解决Enter回车键和Esc取消键默认关闭窗口的三种方法

文章目录 问题描述问题原因解决办法方法一&#xff1a;在重载的PreTranslateMessage 函数中屏蔽回车和ESC 的消息方法二&#xff1a;重载OnOK函数方法三&#xff1a;将所有按钮类型设为普通按钮&#xff0c;并设置其中一个按钮为默认按钮 问题描述 一般情况下编写的MFC对话框程…

HTML语义化标签

<header> 主要用于网页整体顶部&#xff0c;<article>头部&#xff0c;<section>头部 <nav> 导航&#xff0c;一般有主要导航&#xff0c;路径导航&#xff0c;章节导航&#xff0c;内容目录导航 <main> 网页主要区域&#xff0c;一般一个网页…

【运维项目经历|025】企业高效邮件系统部署与运维项目

目录 项目名称 项目背景 项目目标 项目成果 我的角色与职责 我主要完成的工作内容 本次项目涉及的技术 本次项目遇到的问题与解决方法 本次项目中可能被面试官问到的问题 经验教训与自我提升 展望未来 项目名称 企业高效邮件系统部署与运维项目 项目背景 随着企业…

AI之下 360让PC商业生态大象起舞

时隔7年&#xff0c;淘宝PC版在前不久迎来重磅升级&#xff0c;在产品体验、商品供给、内容供给等方面做了全面优化&#xff0c;以全面提升PC端的用户体验&#xff1b;当大家都以为移动互联网时代下APP将成为主流时&#xff0c;PC端却又成为了香饽饽。其实PC端被重视&#xff0…

3389,为了保障3389端口的安全,我们可以采取的措施

3389端口&#xff0c;作为远程桌面协议&#xff08;RDP&#xff09;的默认端口&#xff0c;广泛应用于Windows操作系统中&#xff0c;以实现远程管理和控制功能。然而&#xff0c;正因为其广泛使用&#xff0c;3389端口也成为许多潜在安全威胁的入口。因此&#xff0c;确保3389…

go 针对 time类型字段,前端查询,后端返回数据格式为UTC时间

测试代码 package mainimport ("context""log""net/http""time""github.com/gin-gonic/gin""go.mongodb.org/mongo-driver/bson""go.mongodb.org/mongo-driver/bson/primitive""go.mongodb.org/m…

鸿蒙ArkTS声明式开发:跨平台支持列表【显隐控制】 通用属性

显隐控制 控制组件是否可见。 说明&#xff1a; 开发前请熟悉鸿蒙开发指导文档&#xff1a; gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本…

docker部署owncloud进行管理

目录 一.拉取镜像 1.使用mysql和owncloud最新版镜像&#xff0c;构建个人网盘 2.查看是否已经正确监听端口 二.使用浏览器进行测试 1.使用IP:8080进行访问&#xff0c;用admin运行容器时设置的密码登录 2.查看到已经有的文件 3.文件上传对应的位置 4.在web页面进行简单…

liunx文件系统与日志分析

文章目录 一、基本概念二、日志分析三、实验 一、基本概念 文件是存储在硬盘上的&#xff0c;硬盘上的最小存储单位是扇区每个扇区大小事512字节 inode&#xff1a;元信息&#xff08;文件的属性 权限 创建者 创建日期&#xff09; block&#xff1a;块 连续八个扇区组成一块…

[RK3588-Android12] 关于BQ25703充电IC+CW2017电量计调试

问题描述 BQ25703充电ICCW2017电量计调试 解决方案&#xff1a; 附上dts配置文件 &i2c6 {clock-frequency <400000>;status "okay";// CONFIG_BATTERY_CW2017cw2017: cw201763 {status "okay";compatible "cellwise,cw2017";re…

Java操作Excel文档进行读取和写入

目录 读出Excel文档 写入Excel文档 读出Excel文档 使用EasyExcel读取Excel文件: 需要在maven项目中导入EasyExcel依赖 <!-- EasyExcel依赖包 --> <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><v…

长难句打卡5.31

In a workplace that’s fundamentally indifferent to your life and its meaning, office speak can help you figure out how you relate to your work—and how your work defines who you are. 在一个对你的生活和生活意义漠不关心的工作场所中&#xff0c;办公室语言可以…

揭秘蓝牙AOA定位系统:精准定位,开启智能导航新时代

随着科技的飞速发展&#xff0c;人们对于定位技术的需求也日益增长。在众多定位技术中&#xff0c;蓝牙AOA定位系统以其高精度、低通信开销的特点&#xff0c;逐渐受到广泛关注。接下来给大家简单介绍一下关于蓝牙AOA定位系统的原理、优势及应用场景&#xff0c;带大家领略其带…

情感读本期刊万方收录综合期刊投稿

《情感读本》杂志是由国家新闻出版总署批准&#xff0c;湖北省新闻出版广电局主管&#xff0c;湖北省期刊协会主办的正规综合类期刊。《情感读本》是一本以推动和发展情感教育、素质教育、人文教育为己任&#xff0c;奉行“立足教育&#xff0c;服务社会”的办刊宗旨&#xff0…

Jmeter性能测试-【关联,提取器】

新知识点 关联&#xff1a; 正则表达式提取器 边界提取器 XPath提取器 JSON提取器 梳理框架 1. Jmeter基础 定义&#xff1a;Jmeter是一个开源的性能测试工具&#xff0c;主要用于Web应用和各种服务的性能测试。 主要功能&#xff1a;可以模拟多用户并发访问&#xff0c;测…

Vue3兼容低版本浏览器(ie11,chrome63)

1、插件安装 为了使你的项目兼容 Chrome 63&#xff0c;你需要确保包含适当的 polyfills 和插件配置。你已经在使用 legacy 插件&#xff0c;但在代码中可能缺少一些配置或插件顺序有问题。以下是几个可能的改进&#xff1a; 安装 vitejs/plugin-legacy 插件&#xff1a; 确保…

(2024,Video2Game,NeRF,Mesh,物理模块,游戏引擎)通过单个视频实现实时、交互、逼真且兼容浏览器的环境

Video2Game: Real-time, Interactive, Realistic and Browser-Compatible Environment from a Single Video 公众号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 相关工作 3. Video…