二叉树DFS、BFS

目录

1,DFS遍历

2,DFS遍历OJ实战

力扣 144. 二叉树的前序遍历

力扣 145. 二叉树的后序遍历

力扣 94. 二叉树的中序遍历

力扣 105. 从前序与中序遍历序列构造二叉树

力扣 106. 从中序与后序遍历序列构造二叉树

力扣 889. 根据前序和后序遍历构造二叉树

CSU 1283 Binary Tree Traversals

力扣 100. 相同的树

力扣 404. 左叶子之和

力扣 250. 统计同值子树

力扣 545. 二叉树的边界

力扣 814. 二叉树剪枝

力扣 1026. 节点与其祖先之间的最大差值

力扣 1261. 在受污染的二叉树中查找元素

力扣 1430. 判断给定的序列是否是二叉树从根到叶的路径

力扣 1973. 值等于子节点值之和的节点数量

力扣 1110. 删点成林

力扣 1120. 子树的最大平均值

力扣 1379. 找出克隆二叉树中的相同节点

力扣 1448. 统计二叉树中好节点的数目

力扣 1469. 寻找所有的独生节点

力扣 1612. 检查两棵二叉表达式树是否等价

力扣 437. 路径总和 III

力扣 993. 二叉树的堂兄弟节点

力扣 314. 二叉树的垂直遍历

力扣 987. 二叉树的垂序遍历

力扣 2673. 使二叉树所有路径值相等的最小代价

力扣 2581. 统计可能的树根数目

力扣 LCP 26. 导航装置

力扣 面试题 04.06. 后继者

力扣 331. 验证二叉树的前序序列化

力扣 572. 另一棵树的子树

3,模板LevelOrderTraversal实战

力扣 102. 二叉树的层序遍历

力扣 103. 二叉树的锯齿形层序遍历

力扣 107. 二叉树的层序遍历 II

力扣 2583. 二叉树中的第 K 大层和

力扣 面试题 04.03. 特定深度节点链表

力扣 2641. 二叉树的堂兄弟节点 II

力扣 513. 找树左下角的值

力扣 1161. 最大层内元素和

剑指 Offer 32 - I. 从上到下打印二叉树

4,其他BFS遍历OJ实战

力扣 297. 二叉树的序列化与反序列化

力扣 1602. 找到二叉树中最近的右侧节点

力扣 117. 填充每个节点的下一个右侧节点指针 II

力扣 2368. 受限条件下可到达节点的数目


1,DFS遍历

ACM模板——二叉树

仅由1种遍历无法确定二叉树,中序 + 先序或后序 可以确定二叉树,
先序 + 后序 可能可以确定二叉树,也可能不可以。

大学数据结构实验:

根据输入的数据建立一个二叉树;
分别采用前序、中序、后序的遍历方式显示输出二叉树的遍历结果

代码:

#include<iostream>
using namespace std;
 
struct node			//定义结点
{
	char ch;
	struct node *lchild;
	struct node *rchild;
};
 
node * create(struct node *p)				//利用递归函数,按照先序创建二叉树,以0代表空
{
	char c;
	cin >> c;
	if (c == '0')p = NULL;
	else
	{
		p = new  node;
		p->ch = c;
		p->lchild = create(p->lchild);
		p->rchild = create(p->rchild);
	}
	return p;
}
 
void preOrder(node *p)			//利用递归函数,先序遍历
{
	if (p)
	{
		cout << p->ch << " ";
		preOrder(p->lchild);
		preOrder(p->rchild);
	}
}
 
void inOrder(node *p)			//利用递归函数,中序遍历
{
	if (p)
	{
		inOrder(p->lchild);
		cout << p->ch << " ";
		inOrder(p->rchild);
	}
}
 
void postOrder(node *p)			//利用递归函数,后序遍历
{
	if (p)
	{
		postOrder(p->lchild);
		postOrder(p->rchild);
		cout << p->ch << " ";
	}
}
 
int main()
{
	node *root = new  node;
	cout << "输入二叉树存储的字符,以0表示不存在" << endl;
	root = create(root);			//易错点1
	cout << "先序遍历是    ";
	preOrder(root);
	cout << endl << "中序遍历是    ";
	inOrder(root);
	cout << endl << "后序遍历是    ";
	postOrder(root);
	system("pause>nul");
	return 0;
}

用来测试的二叉树:

运行结果:

2,DFS遍历OJ实战

力扣 144. 二叉树的前序遍历

题目:

给定一个二叉树,返回它的 前序 遍历。

 示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?

代码:

class Solution {
public:
	vector<int> preorderTraversal(TreeNode* root) {
		return PreorderTraversal(root);
	}
};

力扣 145. 二叉树的后序遍历

题目:

给定一个二叉树,返回它的 后序 遍历。

示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?

代码:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        return PostorderTraversal(root);
    }
};

力扣 94. 二叉树的中序遍历

题目:

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
   1
    \
     2
    /
   3

输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?

代码:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        return InorderTraversal(root);
    }
};

力扣 105. 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return BuildTreePre(preorder,inorder);
    }
};

力扣 106. 从中序与后序遍历序列构造二叉树

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        return BuildTreePost(inorder,postorder);
    }
};

力扣 889. 根据前序和后序遍历构造二叉树

返回与给定的前序和后序遍历匹配的任何二叉树。

 pre 和 post 遍历中的值是不同的正整数。

示例:

输入:pre = [1,2,4,5,3,6,7], post = [4,5,2,6,7,3,1]
输出:[1,2,3,4,5,6,7]
 

提示:

1 <= pre.length == post.length <= 30
pre[] 和 post[] 都是 1, 2, ..., pre.length 的排列
每个输入保证至少有一个答案。如果有多个答案,可以返回其中一个。

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder,int s1, vector<int>& inorder, int s2, int len) {
        if(len<=0)return NULL;
        TreeNode *ans=new TreeNode;
        ans->val=preorder[s1];
        if(len==1)return ans;
        auto loc=find(inorder.begin()+s2,inorder.begin()+s2+len,preorder[s1+1]);
        ans->left=buildTree(preorder,s1+1,inorder,s2,loc-inorder.begin()-s2+1);
        ans->right=buildTree(preorder,loc-inorder.begin()-s2+s1+2,inorder,loc-inorder.begin()+1,len-(loc-inorder.begin()-s2)-2);
        return ans;
    }
    TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& inorder) {
        return buildTree(preorder,0,inorder,0,inorder.size());
    }
};

CSU 1283 Binary Tree Traversals

题目:

Description

给定一棵二叉树先序遍历得到的先序序列和后续遍历得到的后序序列,能够唯一确定这棵二叉树吗?

Input

输入数据的第一行包含一个整数T (1 <= T <= 100),表示接下来一共有T组测试数据。

对于每组测试数据,第一行包含一个正整数N (1 <= N <= 26),表示这棵二叉树一共有N个节点。接下来两行每行均包含N个互不相同的大写字母,字符之间没有空格,分别表示对这棵二叉树进行先序遍历和后序遍历后得到的先序序列和后序序列。数据保证根据先序序列和后续序列至少能够构造出一棵二叉树。

Output

对于每组测试数据,如果根据先序序列和后续序列能够唯一确定一棵二叉树,用一行输出N个大写字母,字符之间不应有空格,表示这棵二叉树的中序序列。如果不能唯一确定一棵二叉树,则用一行输出“-1”(不包括引号)。

Sample Input

2
2
AB
BA
3
ABC
BCA

Sample Output

-1
BAC

代码:

#include<iostream>
using namespace std;
 
char s1[27], s2[27], s3[27];
 
bool f(int k1, int k2, int k3, int len)
{
	if (len == 1)
	{
		s3[k3] = s1[k1];
		return true;
	}
	char c = s1[k1 + 1];
	for (int i = k2; i < k2 + len; i++)
	{
		if (s2[i] != c)continue;
		if (i == k2 + len - 2)return false;
		if (!f(k1 + 1, k2, k3, i - k2 + 1))return false;
		if (!f(k1 - k2 + i + 2, i + 1, k3 - k2 + i + 2, k2 + len - 2 - i))return false;
		s3[k3 - k2 + i + 1] = s1[k1];
	}
	return true;
}
 
int main()
{
	int t, n;
	cin >> t;	
	while (t--)
	{
		cin >> n >> s1 >> s2;
		s3[n] = '\0';
		if (f(0, 0, 0, n))cout << s3 << endl;
		else cout << "-1\n";
	}
	return 0;
}

力扣 100. 相同的树

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:p = [1,2,3], q = [1,2,3]
输出:true

示例 2:

输入:p = [1,2], q = [1,null,2]
输出:false

示例 3:

输入:p = [1,2,1], q = [1,1,2]
输出:false

提示:

  • 两棵树上的节点数目都在范围 [0, 100] 内
  • -104 <= Node.val <= 104
class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        return IsSameTree(p,q);
    }
};

力扣 404. 左叶子之和

给定二叉树的根节点 root ,返回所有左叶子之和。

示例 1:

输入: root = [3,9,20,null,null,15,7] 
输出: 24 
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
示例 2:

输入: root = [1]
输出: 0
 

提示:

节点数在 [1, 1000] 范围内
-1000 <= Node.val <= 1000

class Solution {
public:
	void sumOfLeftLeaves(TreeNode* root, bool isLeft) {
		if (!root->left && !root->right) {
			if(isLeft)s += root->val;
			return;
		}
		if (root->left)sumOfLeftLeaves(root->left, true);
		if (root->right)sumOfLeftLeaves(root->right, false);
	}
	int sumOfLeftLeaves(TreeNode* root) {
		if (!root)return 0;
		s = 0;
		sumOfLeftLeaves(root, false);
		return s;
	}
private:
	int s;
};

力扣 250. 统计同值子树

给定一个二叉树,统计同值子树个数。

同值子树是指该子树的所有节点都拥有相同的数值。

示例:

输入: root = [5,1,5,5,5,null,5]

              5
             / \
            1   5
           / \   \
          5   5   5

输出: 4
class Solution {
public:
    int countUnivalSubtrees(TreeNode* root) {
        ans=0;
        if(root)dfs(root);
        return ans;
    }
    vector<int> dfs(TreeNode* root)
    {
        int minx=root->val,maxx=root->val;
        if(root->left){
            auto v=dfs(root->left);
            minx=min(minx,v[0]);
            maxx=max(maxx,v[1]);
        }
        if(root->right){
            auto v=dfs(root->right);
            minx=min(minx,v[0]);
            maxx=max(maxx,v[1]);
        }
        ans+=(minx==maxx);
        return vector<int>{minx,maxx};
    }
    int ans;
};

力扣 545. 二叉树的边界

二叉树的 边界 是由 根节点 左边界 、按从左到右顺序的 叶节点 和 逆序的右边界 ,按顺序依次连接组成。

左边界 是满足下述定义的节点集合:

  • 根节点的左子节点在左边界中。如果根节点不含左子节点,那么左边界就为  。
  • 如果一个节点在左边界中,并且该节点有左子节点,那么它的左子节点也在左边界中。
  • 如果一个节点在左边界中,并且该节点 不含 左子节点,那么它的右子节点就在左边界中。
  • 最左侧的叶节点 不在 左边界中。

右边界 定义方式与 左边界 相同,只是将左替换成右。即,右边界是根节点右子树的右侧部分;叶节点 不是 右边界的组成部分;如果根节点不含右子节点,那么右边界为  。

叶节点 是没有任何子节点的节点。对于此问题,根节点 不是 叶节点。

给你一棵二叉树的根节点 root ,按顺序返回组成二叉树 边界 的这些值。

示例 1:

输入:root = [1,null,2,3,4]
输出:[1,3,4,2]
解释:
- 左边界为空,因为二叉树不含左子节点。
- 右边界是 [2] 。从根节点的右子节点开始的路径为 2 -> 4 ,但 4 是叶节点,所以右边界只有 2 。
- 叶节点从左到右是 [3,4] 。
按题目要求依序连接得到结果 [1] + [] + [3,4] + [2] = [1,3,4,2] 。

示例 2:

输入:root = [1,2,3,4,5,6,null,null,null,7,8,9,10]
输出:[1,2,4,7,8,9,10,6,3]
解释:
- 左边界为 [2] 。从根节点的左子节点开始的路径为 2 -> 4 ,但 4 是叶节点,所以左边界只有 2 。
- 右边界是 [3,6] ,逆序为 [6,3] 。从根节点的右子节点开始的路径为 3 -> 6 -> 10 ,但 10 是叶节点。
- 叶节点从左到右是 [4,7,8,9,10]
按题目要求依序连接得到结果 [1] + [2] + [4,7,8,9,10] + [6,3] = [1,2,4,7,8,9,10,6,3] 。

提示:

  • 树中节点的数目在范围 [1, 104] 内
  • -1000 <= Node.val <= 1000

class Solution {
public:
	vector<int>v;
	vector<int> boundaryOfBinaryTree(TreeNode* root) {
		if (!root)return vector<int>{};
		if (!root->left&&!root->right)return vector<int>{root->val};
		v = getLeft(root);
		dfs(root);
		addRight(root);
		return v;
	}
	vector<int> getLeft(TreeNode* p) {
		vector<int>v;
		if (p->left) {
			while (p) {
				v.push_back(p->val);
				if (p->left)p = p->left;
				else p = p->right;
			}
			v.erase(v.begin() + v.size() - 1);
		}
		else {
			v.push_back(p->val);
		}
		return v;
	}
	void addRight(TreeNode* p) {
		vector<int>v2;
		if (p->right) {
			while (p) {
				v2.push_back(p->val);
				if (p->right)p = p->right;
				else p = p->left;
			}
			for (int i = v2.size() - 2; i; i--)v.push_back(v2[i]);
		}
	}
	void dfs(TreeNode* root) {
		if (!root->left && !root->right)v.push_back(root->val);
		if (root->left)dfs(root->left);
		if (root->right)dfs(root->right);
	}
};

力扣 814. 二叉树剪枝

给你二叉树的根结点 root ,此外树的每个结点的值要么是 0 ,要么是 1 。

返回移除了所有不包含 1 的子树的原二叉树。

节点 node 的子树为 node 本身加上所有 node 的后代。

示例 1:

输入:root = [1,null,0,0,1]
输出:[1,null,0,null,1]
解释:
只有红色节点满足条件“所有不包含 1 的子树”。 右图为返回的答案。

示例 2:

输入:root = [1,0,1,0,0,0,1]
输出:[1,null,1,null,1]

示例 3:

输入:root = [1,1,0,1,1,0,1,0]
输出:[1,1,0,1,1,null,1]

提示:

  • 树中节点的数目在范围 [1, 200] 内
  • Node.val 为 0 或 1

class Solution {
public:
    TreeNode* pruneTree(TreeNode* root) {
        if(dfs(root))return root;
        return nullptr;
    }
    bool dfs(TreeNode* root){
        bool a=false,b=false;
        if(root->left)a=dfs(root->left);
        if(root->right)b=dfs(root->right);
        if(!a)root->left=nullptr;
        if(!b)root->right=nullptr;
        return a||b||root->val;
    }
};

力扣 1026. 节点与其祖先之间的最大差值

给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。

(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)

示例 1:

输入:root = [8,3,10,1,6,null,14,null,null,4,7,13]
输出:7
解释: 
我们有大量的节点与其祖先的差值,其中一些如下:
|8 - 3| = 5
|3 - 7| = 4
|8 - 1| = 7
|10 - 13| = 3
在所有可能的差值中,最大值 7 由 |8 - 1| = 7 得出。

示例 2:

输入:root = [1,null,2,null,0,3]
输出:3

提示:

  • 树中的节点数在 2 到 5000 之间。
  • 0 <= Node.val <= 105
class Solution {
public:
    int maxAncestorDiff(TreeNode* root) {
        ans=0;
        dfs(root,root->val,root->val);
        return ans;
    }
    void dfs(TreeNode* root,int mins,int maxs) {
        ans=max(ans,abs(root->val-mins));
        ans=max(ans,abs(root->val-maxs));
        mins=min(mins,root->val);
        maxs=max(maxs,root->val);
        if(root->left)dfs(root->left,mins,maxs);
        if(root->right)dfs(root->right,mins,maxs);
    }
    int ans;
};

力扣 1261. 在受污染的二叉树中查找元素

给出一个满足下述规则的二叉树:

  1. root.val == 0
  2. 如果 treeNode.val == x 且 treeNode.left != null,那么 treeNode.left.val == 2 * x + 1
  3. 如果 treeNode.val == x 且 treeNode.right != null,那么 treeNode.right.val == 2 * x + 2

现在这个二叉树受到「污染」,所有的 treeNode.val 都变成了 -1

请你先还原二叉树,然后实现 FindElements 类:

  • FindElements(TreeNode* root) 用受污染的二叉树初始化对象,你需要先把它还原。
  • bool find(int target) 判断目标值 target 是否存在于还原后的二叉树中并返回结果。

示例 1:

输入:
["FindElements","find","find"]
[[[-1,null,-1]],[1],[2]]
输出:
[null,false,true]
解释:
FindElements findElements = new FindElements([-1,null,-1]); 
findElements.find(1); // return False 
findElements.find(2); // return True 

示例 2:

输入:
["FindElements","find","find","find"]
[[[-1,-1,-1,-1,-1]],[1],[3],[5]]
输出:
[null,true,true,false]
解释:
FindElements findElements = new FindElements([-1,-1,-1,-1,-1]);
findElements.find(1); // return True
findElements.find(3); // return True
findElements.find(5); // return False

示例 3:

输入:
["FindElements","find","find","find","find"]
[[[-1,null,-1,-1,null,-1]],[2],[3],[4],[5]]
输出:
[null,true,false,false,true]
解释:
FindElements findElements = new FindElements([-1,null,-1,-1,null,-1]);
findElements.find(2); // return True
findElements.find(3); // return False
findElements.find(4); // return False
findElements.find(5); // return True

提示:

  • TreeNode.val == -1
  • 二叉树的高度不超过 20
  • 节点的总数在 [1, 10^4] 之间
  • 调用 find() 的总次数在 [1, 10^4] 之间
  • 0 <= target <= 10^6
class FindElements {
public:
	FindElements(TreeNode* root) {
		dfs(root,0);
	}
	void dfs(TreeNode* root,int val) {
		s.insert(val);
		if (root->left)dfs(root->left, val * 2 + 1);
		if (root->right)dfs(root->right, val * 2 + 2);
	}
	bool find(int target) {
		return s.find(target) != s.end();
	}
	set<int>s;
};

力扣 1430. 判断给定的序列是否是二叉树从根到叶的路径

给定一个二叉树,我们称从根节点到任意叶节点的任意路径中的节点值所构成的序列为该二叉树的一个 “有效序列” 。检查一个给定的序列是否是给定二叉树的一个 “有效序列” 。

我们以整数数组 arr 的形式给出这个序列。从根节点到任意叶节点的任意路径中的节点值所构成的序列都是这个二叉树的 “有效序列” 。

示例 1:

输入:root = [0,1,0,0,1,0,null,null,1,0,0], arr = [0,1,0,1]
输出:true
解释:
路径 0 -> 1 -> 0 -> 1 是一个“有效序列”(图中的绿色节点)。
其他的“有效序列”是:
0 -> 1 -> 1 -> 0 
0 -> 0 -> 0
示例 2:

输入:root = [0,1,0,0,1,0,null,null,1,0,0], arr = [0,0,1]
输出:false 
解释:路径 0 -> 0 -> 1 不存在,所以这不是一个“序列”。
示例 3:

输入:root = [0,1,0,0,1,0,null,null,1,0,0], arr = [0,1,1]
输出:false
解释:路径 0 -> 1 -> 1 是一个序列,但不是一个“有效序列”(译者注:因为序列的终点不是叶节点)。
 

提示:

1 <= arr.length <= 5000
0 <= arr[i] <= 9
每个节点的值的取值范围是 [0 - 9]

class Solution {
public:
	bool dfs(TreeNode* root, vector<int>& arr, int loc)
	{
		if (root->val != arr[loc])return false;
		if (!root->left && !root->right && loc == arr.size() - 1)return true;
		if ((!root->left && !root->right) || loc == arr.size() - 1)return false;
		if(root->left && dfs(root->left, arr, loc + 1))return true;
		if(root->right && dfs(root->right, arr, loc + 1))return true;
		return false;
	}
	bool isValidSequence(TreeNode* root, vector<int>& arr) {
		if (!root)return false;
		return dfs(root, arr, 0);
	}
};

力扣 1973. 值等于子节点值之和的节点数量

给定一颗二叉树的根节点 root ,返回满足条件:节点的值等于该节点所有子节点的值之和 的节点的数量。

一个节点 x 的 子节点 是指从节点 x 出发,到所有叶子节点路径上的节点。没有子节点的节点的子节点和视为 0 。

示例 1:

输入: root = [10,3,4,2,1]
输出: 2
解释:
对于值为10的节点: 其子节点之和为: 3+4+2+1 = 10。
对于值为3的节点:其子节点之和为: 2+1 = 3。

示例 2:

输入: root = [2,3,null,2,null]
输出: 0
解释:
没有节点满足其值等于子节点之和。

示例 3:

输入: root = [0]
输出: 1
解释:
对于值为0的节点:因为它没有子节点,所以自己点之和为0。

提示:

  • 树中节点的数量范围: [1, 105]
  • 0 <= Node.val <= 105
class Solution {
public:
	int ans = 0;
	long long dfs(TreeNode* root)
	{
		if (!root)return 0;
		long long n = dfs(root->left) + dfs(root->right);
		if (root->val == n)ans++;
		return root->val + n;
	}
	int equalToDescendants(TreeNode* root) {
		dfs(root);
		return ans;
	}
};

力扣 1110. 删点成林

给出二叉树的根节点 root,树上每个节点都有一个不同的值。

如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。

返回森林中的每棵树。你可以按任意顺序组织答案。

示例 1:

输入:root = [1,2,3,4,5,6,7], to_delete = [3,5]
输出:[[1,2,null,4],[6],[7]]

示例 2:

输入:root = [1,2,4,null,3], to_delete = [3]
输出:[[1,2,4]]

提示:

  • 树中的节点数最大为 1000
  • 每个节点都有一个介于 1 到 1000 之间的值,且各不相同。
  • to_delete.length <= 1000
  • to_delete 包含一些从 1 到 1000、各不相同的值。
class Solution {
public:
    vector<TreeNode*> delNodes(TreeNode* root, vector<int>& to_delete) {
        dfs(root, root);
        map<TreeNode*,int>ans;
        ans[root]++;
        for(auto x:to_delete){
            auto p=m[x],pfa=mfa[x];
            if(!p)continue;
            if(p->left)ans[p->left]++;
            if(p->right)ans[p->right]++;
            if(pfa->left==p)pfa->left=nullptr;
            if(pfa->right==p)pfa->right=nullptr;
        }
        for(auto x:to_delete){
            auto p=m[x];
            ans[p]=0;
        }
        vector<TreeNode*>ans2;
        for(auto p:ans)if(p.second)ans2.push_back(p.first);
        return ans2;
    }
    void dfs(TreeNode* root, TreeNode* fa)
    {
        if(!root)return;
        m[root->val]=root,mfa[root->val]=fa;
        dfs(root->left,root);
        dfs(root->right,root);
    }
    map<int,TreeNode*>m,mfa;
};

力扣 1120. 子树的最大平均值

 给你一棵二叉树的根节点 root,找出这棵树的 每一棵 子树的 平均值 中的 最大 值。

子树是树中的任意节点和它的所有后代构成的集合。

树的平均值是树中节点值的总和除以节点数。

示例:

输入:[5,6,1]
输出:6.00000
解释: 
以 value = 5 的节点作为子树的根节点,得到的平均值为 (5 + 6 + 1) / 3 = 4。
以 value = 6 的节点作为子树的根节点,得到的平均值为 6 / 1 = 6。
以 value = 1 的节点作为子树的根节点,得到的平均值为 1 / 1 = 1。
所以答案取最大值 6。
 

提示:

树中的节点数介于 1 到 5000之间。
每个节点的值介于 0 到 100000 之间。
如果结果与标准答案的误差不超过 10^-5,那么该结果将被视为正确答案。

class Solution {
public:
	double ans = -1234567;
	void dfs(TreeNode* root, int& s, int& num)
	{
		if (!root) {
			s = 0, num = 0;
			return;
		}
		int s1, num1, s2, num2;
		dfs(root->left, s1, num1);
		dfs(root->right, s2, num2);
		s = s1 + s2 + root->val, num = num1 + num2 + 1;
		ans = max(ans, s * 1.0 / num);
	}
	double maximumAverageSubtree(TreeNode* root) {
		int s, num;
		dfs(root, s, num);
		return ans;
	}
};

力扣 1379. 找出克隆二叉树中的相同节点

给你两棵二叉树,原始树 original 和克隆树 cloned,以及一个位于原始树 original 中的目标节点 target

其中,克隆树 cloned 是原始树 original 的一个 副本 

请找出在树 cloned 中,与 target 相同 的节点,并返回对该节点的引用(在 C/C++ 等有指针的语言中返回 节点指针,其他语言返回节点本身)。

注意:你 不能 对两棵二叉树,以及 target 节点进行更改。只能 返回对克隆树 cloned 中已有的节点的引用。

示例 1:

输入: tree = [7,4,3,null,null,6,19], target = 3
输出: 3
解释: 上图画出了树 original 和 cloned。target 节点在树 original 中,用绿色标记。答案是树 cloned 中的黄颜色的节点(其他示例类似)。

示例 2:

输入: tree = [7], target =  7
输出: 7

示例 3:

输入: tree = [8,null,6,null,5,null,4,null,3,null,2,null,1], target = 4
输出: 4

提示:

  • 树中节点的数量范围为 [1, 104] 。
  • 同一棵树中,没有值相同的节点。
  • target 节点是树 original 中的一个节点,并且不会是 null 。

进阶:如果树中允许出现值相同的节点,将如何解答?

class Solution {
public:
	TreeNode* getTargetCopy(TreeNode* original, TreeNode* cloned, TreeNode* target) {
		return dfs(original, cloned, target);
	}
	TreeNode* dfs(TreeNode* original, TreeNode* cloned, TreeNode* target) {
		if (original == target)return cloned;
		if (original->left) {
			auto p = dfs(original->left, cloned->left, target);
			if (p)return p;
		}
		if (original->right) {
			auto p = dfs(original->right, cloned->right, target);
			if (p)return p;
		}
		return nullptr;
	}
};

力扣 1448. 统计二叉树中好节点的数目

给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。

「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。

示例 1:

输入:root = [3,1,4,3,null,1,5]
输出:4
解释:图中蓝色节点为好节点。
根节点 (3) 永远是个好节点。
节点 4 -> (3,4) 是路径中的最大值。
节点 5 -> (3,4,5) 是路径中的最大值。
节点 3 -> (3,1,3) 是路径中的最大值。

示例 2:

输入:root = [3,3,null,4,2]
输出:3
解释:节点 2 -> (3, 3, 2) 不是好节点,因为 "3" 比它大。

示例 3:

输入:root = [1]
输出:1
解释:根节点是好节点。

提示:

  • 二叉树中节点数目范围是 [1, 10^5] 。
  • 每个节点权值的范围是 [-10^4, 10^4] 。
class Solution {
public:
    int goodNodes(TreeNode* root,int m) {
        int ans=(root->val>=m);
        m=max(m,root->val);
        if(root->left)ans+=goodNodes(root->left,m);
        if(root->right)ans+=goodNodes(root->right,m);
        return ans;
    }
    int goodNodes(TreeNode* root) {
        if(!root)return 0;
        return goodNodes(root,root->val);
    }
};

力扣 1469. 寻找所有的独生节点

二叉树中,如果一个节点是其父节点的唯一子节点,则称这样的节点为 “独生节点” 。二叉树的根节点不会是独生节点,因为它没有父节点。

给定一棵二叉树的根节点 root ,返回树中 所有的独生节点的值所构成的数组 。数组的顺序 不限 

示例 1:

输入:root = [1,2,3,null,4]
输出:[4]
解释:浅蓝色的节点是唯一的独生节点。
节点 1 是根节点,不是独生的。
节点 2 和 3 有共同的父节点,所以它们都不是独生的。

示例 2:

输入:root = [7,1,4,6,null,5,3,null,null,null,null,null,2]
输出:[6,2]
输出:浅蓝色的节点是独生节点。
请谨记,顺序是不限的。 [2,6] 也是一种可接受的答案。

示例 3:

输入:root = [11,99,88,77,null,null,66,55,null,null,44,33,null,null,22]
输出:[77,55,33,66,44,22]
解释:节点 99 和 88 有共同的父节点,节点 11 是根节点。
其他所有节点都是独生节点。

示例 4:

输入:root = [197]
输出:[]

示例 5:

输入:root = [31,null,78,null,28]
输出:[78,28]

提示:

  •  tree 中节点个数的取值范围是 [1, 1000]
  • 每个节点的值的取值范围是 [1, 10^6]
class Solution {
public:
	vector<int> getLonelyNodes(TreeNode* root) {
		auto ans= vector<int>{};
		if (!root)return ans;
		if(!root->left && !root->right)return ans;
		if (!root->left) {
			ans = getLonelyNodes(root->right);
			ans.push_back(root->right->val);
		}
		else if (!root->right) {
			ans = getLonelyNodes(root->left);
			ans.push_back(root->left->val);
		}
		else {
			ans = getLonelyNodes(root->left);
			auto ans2= getLonelyNodes(root->right);
			ans = FvecJoin(ans, ans2);
		}
		return ans;
	}
};

力扣 1612. 检查两棵二叉表达式树是否等价

二叉表达式树是一种表达算术表达式的二叉树。二叉表达式树中的每一个节点都有零个或两个子节点。 叶节点(有 0 个子节点的节点)表示操作数,非叶节点(有 2 个子节点的节点)表示运算符。在本题中,我们只考虑 '+' 运算符(即加法)。

给定两棵二叉表达式树的根节点 root1 和 root2 。如果两棵二叉表达式树等价,返回 true ,否则返回 false 。

当两棵二叉搜索树中的变量取任意值,分别求得的值都相等时,我们称这两棵二叉表达式树是等价的。

示例 1:

输入: root1 = [x], root2 = [x]
输出: true
示例 2:

输入:root1 = [+,a,+,null,null,b,c], root2 = [+,+,a,b,c]
输出:true
解释:a + (b + c) == (b + c) + a
示例 3:

输入: root1 = [+,a,+,null,null,b,c], root2 = [+,+,a,b,d]
输出: false
解释: a + (b + c) != (b + d) + a
 

提示:

两棵树中的节点个数相等,且节点个数为范围 [1, 4999] 内的奇数。
Node.val 是 '+' 或小写英文字母。
给定的树保证是有效的二叉表达式树。
 

进阶:当你的答案需同时支持 '-' 运算符(减法)时,你该如何修改你的答案

template<typename T>
vector<T> vecAdd(const vector<T>& v1, const vector<T>& v2)
{
	vector<T>v(max(v1.size(), v2.size()));
	for (int i = 0; i < v.size(); i++)v[i] = 0;
	for (int i = 0; i < v1.size(); i++)v[i] += v1[i];
	for (int i = 0; i < v2.size(); i++)v[i] += v2[i];
	return v;
}

class Solution {
public:
	vector<int> dfs(Node* r)
	{
		vector<int> v(26);
		if (!r)return v;
		if (r->val >= 'a' && r->val <= 'z')v[r->val - 'a'] = 1;
		vector<int>v1 = dfs(r->left);
		vector<int>v2 = dfs(r->right);
		v = vecAdd(v, v1);
		v = vecAdd(v, v2);
		return v;
	}
	bool checkEquivalence(Node* r1, Node* r2) {
		vector<int> v1 = dfs(r1);
		vector<int> v2 = dfs(r2);
		for (int i = 0; i < 26; i++)if (v1[i] != v2[i])return false;
		return true;
	}
};

力扣 437. 路径总和 III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

示例 1:

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。

示例 2:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:3

提示:

  • 二叉树的节点个数的范围是 [0,1000]
  • -109 <= Node.val <= 109 
  • -1000 <= targetSum <= 1000 
class Solution {
public:
    int pathSum(TreeNode* root, int targetSum) {
        if(!root)return 0;
        return dfs(root,targetSum)+pathSum(root->left,targetSum)+pathSum(root->right,targetSum);
    }
    int dfs(TreeNode* root, long long targetSum) {
        targetSum-=root->val;
        int s=0;
        if(targetSum==0)s=1;
        if(root->left)s+=dfs(root->left,targetSum);
        if(root->right)s+=dfs(root->right,targetSum);
        return s;
    }
};

力扣 993. 二叉树的堂兄弟节点

在二叉树中,根节点位于深度 0 处,每个深度为 k 的节点的子节点位于深度 k+1 处。

如果二叉树的两个节点深度相同,但 父节点不同 ,则它们是一对堂兄弟节点

我们给出了具有唯一值的二叉树的根节点 root ,以及树中两个不同节点的值 x 和 y 。

只有与值 x 和 y 对应的节点是堂兄弟节点时,才返回 true 。否则,返回 false

示例 1:

输入:root = [1,2,3,4], x = 4, y = 3
输出:false

示例 2:

输入:root = [1,2,3,null,4,null,5], x = 5, y = 4
输出:true

示例 3:

输入:root = [1,2,3,null,4], x = 2, y = 3
输出:false

提示:

  • 二叉树的节点数介于 2 到 100 之间。
  • 每个节点的值都是唯一的、范围为 1 到 100 的整数。
class Solution {
public:
	bool isCousins(TreeNode* root, int x, int y) {
		dfs(root);
		return deep[x] == deep[y] && fa[x] != fa[y];
	}
	void dfs(TreeNode* root) {
		if (root->left) {
			fa[root->left->val] = root->val;
			deep[root->left->val] = deep[root->val] + 1;
			dfs(root->left);
		}
		if (root->right) {
			fa[root->right->val] = root->val;
			deep[root->right->val] = deep[root->val] + 1;
			dfs(root->right);
		}
	}
	map<int, int>fa;
	map<int, int>deep;
};

力扣 314. 二叉树的垂直遍历

给你一个二叉树的根结点,返回其结点按 垂直方向(从上到下,逐列)遍历的结果。

如果两个结点在同一行和列,那么顺序则为 从左到右

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[9],[3,15],[20],[7]]

示例 2:

输入:root = [3,9,8,4,0,1,7]
输出:[[4],[9],[3,0,1],[8],[7]]

示例 3:

输入:root = [3,9,8,4,0,1,7,null,null,null,2,5]
输出:[[4],[9,5],[3,0,1],[8,2],[7]]

提示:

  • 树中结点的数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100
class Solution {
public:
	vector<vector<int>> verticalOrder(TreeNode* root) {
        vector<vector<int>>ans;
        if(!root)return ans;
		m.clear();
		dfs(root, 0, 0);		
		vector<int>v;
		for (auto& mi : m) {
			v.clear();
			for (auto s : mi.second)for (auto x : s.second)v.push_back(x);
			ans.push_back(v);
		}
		return ans;
	}
	void dfs(TreeNode* root, int r, int c) {
		m[c][r].push_back(root->val);
		if (root->left)dfs(root->left, r + 1, c - 1);
		if (root->right)dfs(root->right, r + 1, c + 1);
	}
	map<int, map<int, vector<int>>>m;
};

力扣 987. 二叉树的垂序遍历

给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。

对位于 (row, col) 的每个结点而言,其左右子结点分别位于 (row + 1, col - 1) 和 (row + 1, col + 1) 。树的根结点位于 (0, 0) 。

二叉树的 垂序遍历 从最左边的列开始直到最右边的列结束,按列索引每一列上的所有结点,形成一个按出现位置从上到下排序的有序列表。如果同行同列上有多个结点,则按结点的值从小到大进行排序。

返回二叉树的 垂序遍历 序列。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[9],[3,15],[20],[7]]
解释:
列 -1 :只有结点 9 在此列中。
列  0 :只有结点 3 和 15 在此列中,按从上到下顺序。
列  1 :只有结点 20 在此列中。
列  2 :只有结点 7 在此列中。

示例 2:

输入:root = [1,2,3,4,5,6,7]
输出:[[4],[2],[1,5,6],[3],[7]]
解释:
列 -2 :只有结点 4 在此列中。
列 -1 :只有结点 2 在此列中。
列  0 :结点 1 、5 和 6 都在此列中。
          1 在上面,所以它出现在前面。
          5 和 6 位置都是 (2, 0) ,所以按值从小到大排序,5 在 6 的前面。
列  1 :只有结点 3 在此列中。
列  2 :只有结点 7 在此列中。

示例 3:

输入:root = [1,2,3,4,6,5,7]
输出:[[4],[2],[1,5,6],[3],[7]]
解释:
这个示例实际上与示例 2 完全相同,只是结点 5 和 6 在树中的位置发生了交换。
因为 5 和 6 的位置仍然相同,所以答案保持不变,仍然按值从小到大排序。

提示:

  • 树中结点数目总数在范围 [1, 1000] 内
  • 0 <= Node.val <= 1000
class Solution {
public:
    vector<vector<int>> verticalTraversal(TreeNode* root) {
        m.clear();
        dfs(root,0,0);
        vector<vector<int>>ans;
        vector<int>v;
        for(auto &mi:m){
            v.clear();
            for(auto s:mi.second)for(auto x:s.second)v.push_back(x);
            ans.push_back(v);
        }
        return ans;
    }
    void dfs(TreeNode* root,int r,int c){
        m[c][r].insert(root->val);
        if(root->left)dfs(root->left,r+1,c-1);
        if(root->right)dfs(root->right,r+1,c+1);
    }
    map<int,map<int,multiset<int>>>m;
};

力扣 2673. 使二叉树所有路径值相等的最小代价

给你一个整数 n 表示一棵 满二叉树 里面节点的数目,节点编号从 1 到 n 。根节点编号为 1 ,树中每个非叶子节点 i 都有两个孩子,分别是左孩子 2 * i 和右孩子 2 * i + 1 。

树中每个节点都有一个值,用下标从 0 开始、长度为 n 的整数数组 cost 表示,其中 cost[i] 是第 i + 1 个节点的值。每次操作,你可以将树中 任意 节点的值 增加 1 。你可以执行操作 任意 次。

你的目标是让根到每一个 叶子结点 的路径值相等。请你返回 最少 需要执行增加操作多少次。

注意:

  • 满二叉树 指的是一棵树,它满足树中除了叶子节点外每个节点都恰好有 2 个子节点,且所有叶子节点距离根节点距离相同。
  • 路径值 指的是路径上所有节点的值之和。

示例 1:

输入:n = 7, cost = [1,5,2,2,3,3,1]
输出:6
解释:我们执行以下的增加操作:
- 将节点 4 的值增加一次。
- 将节点 3 的值增加三次。
- 将节点 7 的值增加两次。
从根到叶子的每一条路径值都为 9 。
总共增加次数为 1 + 3 + 2 = 6 。
这是最小的答案。

示例 2:

输入:n = 3, cost = [5,3,3]
输出:0
解释:两条路径已经有相等的路径值,所以不需要执行任何增加操作。

提示:

  • 3 <= n <= 105
  • n + 1 是 2 的幂
  • cost.length == n
  • 1 <= cost[i] <= 104
class Solution {
public:
	int minIncrements(int n, vector<int>& cost) {
		s = 0;
		dfs(n, cost, 0);
		return s;
	}
	int s;
	int dfs(int n, vector<int>& cost, int id)
	{
		if (id >= n / 2)return cost[id];
		int x = dfs(n, cost, id * 2 + 1), y = dfs(n, cost, id * 2 + 2);
		s += abs(x - y);
		return cost[id] + max(x, y);
	}
};

力扣 2581. 统计可能的树根数目

Alice 有一棵 n 个节点的树,节点编号为 0 到 n - 1 。树用一个长度为 n - 1 的二维整数数组 edges 表示,其中 edges[i] = [ai, bi] ,表示树中节点 ai 和 bi 之间有一条边。

Alice 想要 Bob 找到这棵树的根。她允许 Bob 对这棵树进行若干次 猜测 。每一次猜测,Bob 做如下事情:

  • 选择两个 不相等 的整数 u 和 v ,且树中必须存在边 [u, v] 。
  • Bob 猜测树中 u 是 v 的 父节点 。

Bob 的猜测用二维整数数组 guesses 表示,其中 guesses[j] = [uj, vj] 表示 Bob 猜 uj 是 vj 的父节点。

Alice 非常懒,她不想逐个回答 Bob 的猜测,只告诉 Bob 这些猜测里面 至少 有 k 个猜测的结果为 true 。

给你二维整数数组 edges ,Bob 的所有猜测和整数 k ,请你返回可能成为树根的 节点数目 。如果没有这样的树,则返回 0

示例 1:

输入:edges = [[0,1],[1,2],[1,3],[4,2]], guesses = [[1,3],[0,1],[1,0],[2,4]], k = 3
输出:3
解释:
根为节点 0 ,正确的猜测为 [1,3], [0,1], [2,4]
根为节点 1 ,正确的猜测为 [1,3], [1,0], [2,4]
根为节点 2 ,正确的猜测为 [1,3], [1,0], [2,4]
根为节点 3 ,正确的猜测为 [1,0], [2,4]
根为节点 4 ,正确的猜测为 [1,3], [1,0]
节点 0 ,1 或 2 为根时,可以得到 3 个正确的猜测。

示例 2:

输入:edges = [[0,1],[1,2],[2,3],[3,4]], guesses = [[1,0],[3,4],[2,1],[3,2]], k = 1
输出:5
解释:
根为节点 0 ,正确的猜测为 [3,4]
根为节点 1 ,正确的猜测为 [1,0], [3,4]
根为节点 2 ,正确的猜测为 [1,0], [2,1], [3,4]
根为节点 3 ,正确的猜测为 [1,0], [2,1], [3,2], [3,4]
根为节点 4 ,正确的猜测为 [1,0], [2,1], [3,2]
任何节点为根,都至少有 1 个正确的猜测。

提示:

  • edges.length == n - 1
  • 2 <= n <= 105
  • 1 <= guesses.length <= 105
  • 0 <= ai, bi, uj, vj <= n - 1
  • ai != bi
  • uj != vj
  • edges 表示一棵有效的树。
  • guesses[j] 是树中的一条边。
  • guesses 是唯一的。
  • 0 <= k <= guesses.length
class Solution {
public:
	int rootCount(vector<vector<int>>& edges, vector<vector<int>>& guesses, int k) {
		auto m = UndirectedGraphData<>(edges).adjaList;
		MultiTreeNode* root = EdgesToMultiTree(m);
		mg.clear();
		for (auto gi : guesses)mg[gi[0]].insert(gi[1]);
		s = 0, ans = 0;
		dfs(root);
		this->k = k;
		dfs(root, s);
		return ans;
	}
	void dfs(MultiTreeNode* root) {
		for (auto p : root->son) {
			dfs(p);
			if (mg[root->val].find(p->val) != mg[root->val].end())s++;
		}
	}
	void dfs(MultiTreeNode* root, int s)
	{
		if (s >= k)ans++;
		for (auto p : root->son) {
			int d = 0;
			if (mg[root->val].find(p->val) != mg[root->val].end())d--;
			if (mg[p->val].find(root->val) != mg[p->val].end())d++;
			dfs(p, s + d);
		}
	}
private:
	map<int, set<int>>mg;
	int s;
	int k;
	int ans;
};

力扣 LCP 26. 导航装置

小扣参加的秋日市集景区共有 �N 个景点,景点编号为 11~�N。景点内设有 �−1N−1 条双向道路,使所有景点形成了一个二叉树结构,根结点记为 root,景点编号即为节点值。

由于秋日市集景区的结构特殊,游客很容易迷路,主办方决定在景区的若干个景点设置导航装置,按照所在景点编号升序排列后定义装置编号为 1 ~ M。导航装置向游客发送数据,数据内容为列表 [游客与装置 1 的相对距离,游客与装置 2 的相对距离,...,游客与装置 M 的相对距离]。由于游客根据导航装置发送的信息来确认位置,因此主办方需保证游客在每个景点接收的数据信息皆不相同。请返回主办方最少需要设置多少个导航装置。

示例 1:

输入:root = [1,2,null,3,4]

输出:2

解释:在景点 1、3 或景点 1、4 或景点 3、4 设置导航装置。

image.png

示例 2:

输入:root = [1,2,3,4]

输出:1

解释:在景点 3、4 设置导航装置皆可。

image.png

提示:

  • 2 <= N <= 50000
  • 二叉树的非空节点值为 1~N 的一个排列。
class Solution {
public:
	int navigation(TreeNode* root) {
		int s;
		if (isLine(root->left))s= dfs(root->right, 0);
		else if (isLine(root->right))s= dfs(root->left, 0);
		else s = dfs(root, 1);
		return s ? s : 1;
	}
	bool isLine(TreeNode* root) {
		if (!root)return true;
		if (root->left && root->right)return false;
		if (root->left)return isLine(root->left);
		if (root->right)return isLine(root->right);
		return true;
	}
	int dfs(TreeNode* root,int k) {
		if (!root)return 0;
		if (root->left && root->right) {
			int a= dfs(root->left,1);
			int b= dfs(root->right,1);
			if (k == 0)return max(a, 1) + max(b, 1);
			if (a == 0 && b == 0)return 1;
			return a + b;
		}
		if (root->left)return dfs(root->left,k);
		if (root->right)return dfs(root->right, k);
		return 0;
	}
};

力扣 面试题 04.06. 后继者

设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。

如果指定节点没有对应的“下一个”节点,则返回null

示例 1:

输入: root = [2,1,3], p = 1

  2
 / \
1   3

输出: 2

示例 2:

输入: root = [5,3,6,2,4,null,null,1], p = 6

      5
     / \
    3   6
   / \
  2   4
 /   
1

输出: null

class Solution {
public:
    bool flag=false;
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        if(!root)return nullptr;
        if(root==p)return flag=true, firstNode(root->right);
        auto ans=inorderSuccessor(root->left,p);
        if(!ans)return flag?root:inorderSuccessor(root->right,p);
        return ans;
    }
    TreeNode* firstNode(TreeNode* root){
        if(!root)return nullptr;
        while(root->left)root=root->left;
        return root;
    }
};

力扣 331. 验证二叉树的前序序列化

序列化二叉树的一种方法是使用 前序遍历 。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #

例如,上面的二叉树可以被序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#",其中 # 代表一个空节点。

给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。

保证 每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 '#' 。

你可以认为输入格式总是有效的

  • 例如它永远不会包含两个连续的逗号,比如 "1,,3" 。

注意:不允许重建树。

示例 1:

输入: preorder = "9,3,4,#,#,1,#,#,2,#,6,#,#"
输出: true

示例 2:

输入: preorder = "1,#"
输出: false

示例 3:

输入: preorder = "9,#,#,1"
输出: false

提示:

  • 1 <= preorder.length <= 104
  • preorder 由以逗号 “,” 分隔的 [0,100] 范围内的整数和 “#” 组成

class Solution {
public:
	bool isValidSerialization(string preorder) {
		auto v = StringSplit(preorder, ',');
        int s=0;
		for(int i=0;i<v.size()-1;i++){
            if (v[i] == "#") {
                if(--s<0)return false;
            }else{
                s++;
            }
        }
        return s==0 && v[v.size()-1]=="#";
	}
};

力扣 572. 另一棵树的子树

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

示例 1:

输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true

示例 2:

输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false

提示:

  • root 树上的节点数量范围是 [1, 2000]
  • subRoot 树上的节点数量范围是 [1, 1000]
  • -104 <= root.val <= 104
  • -104 <= subRoot.val <= 104
class Solution {
public:
    bool isSubtree(TreeNode* root, TreeNode* subRoot) {
        if(root && isSubtree(root->left, subRoot))return true;
        if(root && isSubtree(root->right, subRoot))return true;
        return IsSameTree(root, subRoot);
    }
};

3,模板LevelOrderTraversal实战

力扣 102. 二叉树的层序遍历

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

示例:
二叉树:[3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回其层序遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        return LevelOrderTraversal(root);
    }
};

力扣 103. 二叉树的锯齿形层序遍历

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000] 内
  • -100 <= Node.val <= 100
class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        auto v=LevelOrderTraversal(root);
        for(int i=1;i<v.size();i+=2)reverse(v[i].begin(),v[i].end());
        return v;
    }
};

力扣 107. 二叉树的层序遍历 II

给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回其自底向上的层序遍历为:

[
  [15,7],
  [9,20],
  [3]
]

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        auto v=LevelOrderTraversal(root);
        reverse(v.begin(), v.end());
        return v;
    }
};

力扣 2583. 二叉树中的第 K 大层和

给你一棵二叉树的根节点 root 和一个正整数 k 。

树中的 层和 是指 同一层 上节点值的总和。

返回树中第 k 大的层和(不一定不同)。如果树少于 k 层,则返回 -1 。

注意,如果两个节点与根节点的距离相同,则认为它们在同一层。

示例 1:

输入:root = [5,8,9,2,1,3,7,4,6], k = 2
输出:13
解释:树中每一层的层和分别是:
- Level 1: 5
- Level 2: 8 + 9 = 17
- Level 3: 2 + 1 + 3 + 7 = 13
- Level 4: 4 + 6 = 10
第 2 大的层和等于 13 。

示例 2:

输入:root = [1,2,null,3], k = 1
输出:3
解释:最大的层和是 3 。

提示:

  • 树中的节点数为 n
  • 2 <= n <= 105
  • 1 <= Node.val <= 106
  • 1 <= k <= n
class Solution {
public:
    long long kthLargestLevelSum(TreeNode* root, int k) {
        auto v=LevelOrderTraversal(root);
        vector<long long>ss;
        for(auto vi:v){
            long long s=0;
            for(auto x:vi)s+=x;
            ss.push_back(s);
        }
        if(k>ss.size())return -1;
        sort(ss.begin(),ss.end());
        return ss[ss.size()-k];
    }
};

力扣 面试题 04.03. 特定深度节点链表

给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组。

示例:

输入:[1,2,3,4,5,null,7,8]

        1
       /  \ 
      2    3
     / \    \ 
    4   5    7
   /
  8

输出:[[1],[2,3],[4,5,7],[8]]
class Solution {
public:
    vector<ListNode*> listOfDepth(TreeNode* tree) {
        vector<vector<int>> v=LevelOrderTraversal(tree);
        vector<ListNode*>ans;
        for(auto &vi:v)ans.push_back(InitLinkList(vi));
        return ans;
    }
};

力扣 2641. 二叉树的堂兄弟节点 II

给你一棵二叉树的根 root ,请你将每个节点的值替换成该节点的所有 堂兄弟节点值的和 

如果两个节点在树中有相同的深度且它们的父节点不同,那么它们互为 堂兄弟 。

请你返回修改值之后,树的根 root 

注意,一个节点的深度指的是从树根节点到这个节点经过的边数。

示例 1:

输入:root = [5,4,9,1,10,null,7]
输出:[0,0,0,7,7,null,11]
解释:上图展示了初始的二叉树和修改每个节点的值之后的二叉树。
- 值为 5 的节点没有堂兄弟,所以值修改为 0 。
- 值为 4 的节点没有堂兄弟,所以值修改为 0 。
- 值为 9 的节点没有堂兄弟,所以值修改为 0 。
- 值为 1 的节点有一个堂兄弟,值为 7 ,所以值修改为 7 。
- 值为 10 的节点有一个堂兄弟,值为 7 ,所以值修改为 7 。
- 值为 7 的节点有两个堂兄弟,值分别为 1 和 10 ,所以值修改为 11 。

示例 2:

输入:root = [3,1,2]
输出:[0,0,0]
解释:上图展示了初始的二叉树和修改每个节点的值之后的二叉树。
- 值为 3 的节点没有堂兄弟,所以值修改为 0 。
- 值为 1 的节点没有堂兄弟,所以值修改为 0 。
- 值为 2 的节点没有堂兄弟,所以值修改为 0 。

提示:

  • 树中节点数目的范围是 [1, 105] 。
  • 1 <= Node.val <= 104
class Solution {
public:
	TreeNode* replaceValueInTree(TreeNode* root) {
		auto v = LevelOrderTraversal(root);
		vector<int>ans;
		for (int i = 0; i < v.size(); i++) {
			ans.push_back(GetVecSum(v[i]));
		}
		root->val = 0;
		bfs(root, 0, ans);
		return root;
	}
	void bfs(TreeNode* root, int deep, vector<int>&v) {
		if (!root->left && !root->right)return;
		int x = v[++deep];
		if (root->left)x -= root->left->val;
		if (root->right)x -= root->right->val;
		if (root->left)root->left->val = x, bfs(root->left, deep, v);
		if (root->right)root->right->val = x, bfs(root->right, deep, v);
	}
};

力扣 513. 找树左下角的值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

输入: root = [2,1,3]
输出: 1

示例 2:

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

提示:

  • 二叉树的节点个数的范围是 [1,104]
  • -231 <= Node.val <= 231 - 1 

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        auto v = LevelOrderTraversal(root);
        return v[v.size()-1][0];
    }
};

力扣 1161. 最大层内元素和

给你一个二叉树的根节点 root。设根节点位于二叉树的第 1 层,而根节点的子节点位于第 2 层,依此类推。

请返回层内元素之和 最大 的那几层(可能只有一层)的层号,并返回其中 最小 的那个。

示例 1:

输入:root = [1,7,0,7,-8,null,null]
输出:2
解释:
第 1 层各元素之和为 1,
第 2 层各元素之和为 7 + 0 = 7,
第 3 层各元素之和为 7 + -8 = -1,
所以我们返回第 2 层的层号,它的层内元素之和最大。
示例 2:

输入:root = [989,null,10250,98693,-89388,null,null,null,-32127]
输出:2
 

提示:

树中的节点数在 [1, 104]范围内
-105 <= Node.val <= 105

class Solution {
public:
	int maxLevelSum(TreeNode* root) {
		vector<vector<int>>v = LevelOrderTraversal(root);
		int m = INT_MIN;
		int ans = 0;
		for (int i = 0; i < v.size(); i++) {
			int s = 0;
			for (auto vi : v[i])s += vi;
			if (m < s)m = s, ans = i + 1;
		}
		return ans;
	}
};

剑指 Offer 32 - I. 从上到下打印二叉树

从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

例如:
给定二叉树: [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回:

[3,9,20,15,7]
 

提示:

节点总数 <= 1000

class Solution {
public:
	vector<int> decorateRecord(TreeNode* root) {
		return FoldJoin(LevelOrderTraversal(root));
	}
};

4,其他BFS遍历OJ实战

力扣 297. 二叉树的序列化与反序列化

rust实战

力扣 1602. 找到二叉树中最近的右侧节点

给定一棵二叉树的根节点 root 和树中的一个节点 u ,返回与 u 所在层中距离最近的右侧节点,当 u 是所在层中最右侧的节点,返回 null 。

示例 1:

输入:root = [1,2,3,null,4,5,6], u = 4
输出:5
解释:节点 4 所在层中,最近的右侧节点是节点 5。
示例 2:

输入:root = [3,null,4,2], u = 2
输出:null
解释:2 的右侧没有节点。
示例 3:

输入:root = [1], u = 1
输出:null
示例 4:

输入:root = [3,4,2,null,null,null,1], u = 4
输出:2
 

提示:

树中节点个数的范围是 [1, 105] 。
1 <= Node.val <= 105
树中所有节点的值是唯一的。
u 是以 root 为根的二叉树的一个节点。

思路:

如果只需要输出右侧节点的节点值,可以先调用层序遍历的模板,再逐行搜索,但是这样运行效率稍微慢一点,所以用模板代码改一改更好。

而现在是需要输出右侧节点的指针,那更应该基于模板代码改一改,直接调用有点麻烦。


class Solution {
public:
    TreeNode* tar;
    int tarDeep;
    void bfs(vector<vector<TreeNode*>>&v, int deep){
        vector<TreeNode*>v1=*(v.end()-1);
        vector<TreeNode*>v2;
        for(int i=0;i<v1.size();i++){
            if(v1[i]==tar)tarDeep=deep;
            if(v1[i]->left)v2.push_back(v1[i]->left);
            if(v1[i]->right)v2.push_back(v1[i]->right);
        }
        if(v2.empty())return;
        v.push_back(v2);
        bfs(v, deep+1);
    }
    vector<vector<TreeNode*>> levelOrder(TreeNode* root) {
        vector<vector<TreeNode*>>v;
        vector<TreeNode*>tmp;
        vector<int>tmpans;
        if(!root)return v;
        tmp.push_back(root);
        v.push_back(tmp);
        bfs(v, 0);
        return v;
    }
    TreeNode* findNearestRightNode(TreeNode* root, TreeNode* u) {
        tar = u;
        auto v = levelOrder(root);
        for(int i=0;i<v[tarDeep].size();i++){
            if(v[tarDeep][i]!=u)continue;
            if(i==v[tarDeep].size()-1)return NULL;
            return v[tarDeep][i+1];
        }
        return NULL;
    }
};

力扣 117. 填充每个节点的下一个右侧节点指针 II

给定一个二叉树:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL 。

初始状态下,所有 next 指针都被设置为 NULL 。

示例 1:

输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。

示例 2:

输入:root = []
输出:[]

提示:

  • 树中的节点数在范围 [0, 6000] 内
  • -100 <= Node.val <= 100

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序的隐式栈空间不计入额外空间复杂度。

思路:

把层序遍历的模板代码改一改,先把层序遍历的指针结果拿到,然后按行加next指针即可。

#define TreeNode Node
void bfs(vector<vector<TreeNode*>>&v){
    vector<TreeNode*>v1=*(v.end()-1);
    vector<TreeNode*>v2;
    for(int i=0;i<v1.size();i++){
        if(v1[i]->left)v2.push_back(v1[i]->left);
        if(v1[i]->right)v2.push_back(v1[i]->right);
    }
    if(v2.empty())return;
    v.push_back(v2);
    bfs(v);
}
vector<vector<TreeNode*>> levelOrder(TreeNode* root) {
    vector<vector<TreeNode*>>v;
    vector<TreeNode*>tmp;
    if(!root)return v;
    tmp.push_back(root);
    v.push_back(tmp);
    bfs(v);
    return v;
}

class Solution {
public:
    Node* connect(Node* root) {
        auto v = levelOrder(root);
        for(auto &vi:v){
            for(int i=1;i<vi.size();i++)vi[i-1]->next=vi[i];
        }
        return root;
    }
};

力扣 2368. 受限条件下可到达节点的数目

现有一棵由 n 个节点组成的无向树,节点编号从 0 到 n - 1 ,共有 n - 1 条边。

给你一个二维整数数组 edges ,长度为 n - 1 ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条边。另给你一个整数数组 restricted 表示 受限 节点。

在不访问受限节点的前提下,返回你可以从节点 0 到达的 最多 节点数目

注意,节点 0  会标记为受限节点。

示例 1:

输入:n = 7, edges = [[0,1],[1,2],[3,1],[4,0],[0,5],[5,6]], restricted = [4,5]
输出:4
解释:上图所示正是这棵树。
在不访问受限节点的前提下,只有节点 [0,1,2,3] 可以从节点 0 到达。

示例 2:

输入:n = 7, edges = [[0,1],[0,2],[0,5],[0,4],[3,2],[6,5]], restricted = [4,2,1]
输出:3
解释:上图所示正是这棵树。
在不访问受限节点的前提下,只有节点 [0,5,6] 可以从节点 0 到达。

提示:

  • 2 <= n <= 105
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • ai != bi
  • edges 表示一棵有效的树
  • 1 <= restricted.length < n
  • 1 <= restricted[i] < n
  • restricted 中的所有值 互不相同
class Solution {
public:
	int reachableNodes(int n, vector<vector<int>>& edges, vector<int>& restricted) {
		map<int, vector<int>> adjaList = UndirectedGraphData<>(edges).adjaList;
		map<int, int>mr;
		for (auto r : restricted)mr[r] = 1;
		int s = 0;
		map<int, int>visit;
		queue<int>q;
		q.push(0);
		while (!q.empty()) {
			int t = q.front();
			q.pop();
			visit[t] = 1;
			s++;
			for (auto x : adjaList[t]) {
				if (visit[x] == 0 && mr[x] == 0)q.push(x);
			}
		}
		return s;
	}
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python中的二叉树遍历可以使用广度优先搜索(BFS)和深度优先搜索(DFS)两种方法。 BFS(广度优先搜索)是一种逐层遍历二叉树的方法。从根节点开始,按照层级顺序依次访问每个节点,先访问左子节点,再访问右子节点。具体实现可以使用队列来辅助实现。以下是BFS的实现方式: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def bfs(root): if not root: return [] queue = [root] result = [] while queue: node = queue.pop(0) result.append(node.val) if node.left: queue.append(node.left) if node.right: queue.append(node.right) return result ``` DFS深度优先搜索)是一种先访问根节点,然后递归地访问左子树和右子树的方法。DFS有三种常见的遍历方式:前序遍历、中序遍历和后序遍历。以下是DFS的实现方式: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def dfs_preorder(root): if not root: return [] result = [] result.append(root.val) result += dfs_preorder(root.left) result += dfs_preorder(root.right) return result def dfs_inorder(root): if not root: return [] result = [] result += dfs_inorder(root.left) result.append(root.val) result += dfs_inorder(root.right) return result def dfs_postorder(root): if not root: return [] result = [] result += dfs_postorder(root.left) result += dfs_postorder(root.right) result.append(root.val) return result ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值