1、深度优先遍历
144.二叉树的前序遍历
1、题目
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]
2、解题
递归
public class Solution {
public void traversal(TreeNode root, IList<int> result)
{
if(root == null)
{
return;
}
result.Add(root.val);
traversal(root.left, result);
traversal(root.right, result);
}
public IList<int> PreorderTraversal(TreeNode root) {
List<int> Result = new List<int>{};
traversal(root, Result);
return Result;
}
}
迭代
public class Solution {
public void traversal(TreeNode root, IList<int> result)
{
if(root == null)
{
return;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.Push(root);
while(stack.Count > 0)
{
TreeNode node = stack.Pop();
result.Add(node.val);
if(node.right != null)
{
stack.Push(node.right);
}
if(node.left != null)
{
stack.Push(node.left);
}
}
}
public IList<int> PreorderTraversal(TreeNode root) {
List<int> Result = new List<int>{};
traversal(root, Result);
return Result;
}
}
3、总结
错误:数据类型要统一,栈存储结点,列表存储结点数据,函数返回值记得检查
94.二叉树的中序遍历
1、题目
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]
2、解题
递归
public class Solution {
public void traversal(TreeNode root, List<int> result)
{
if(root == null)
{
return;
}
traversal(root.left, result);
result.Add(root.val);
traversal(root.right, result);
}
public IList<int> InorderTraversal(TreeNode root) {
List<int> Result = new List<int>{};
traversal(root, Result);
return Result;
}
}
迭代
public class Solution {
public IList<int> InorderTraversal(TreeNode root) {
List<int> result = new List<int>{};
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode current = root;
while(current != null || stack.Count > 0)
{
//左子树结点一次入栈
while(current != null)
{
stack.Push(current);
current = current.left;
}
//出栈并访问节点
current = stack.Pop();
result.Add(current.val);
//处理右子树
current = current.right;
}
return result;
}
}
3、总结
迭代比较难理解,重点看一看
145.二叉树的后序遍历
1、题目
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。
示例 1:
输入:root = [1,null,2,3]
输出:[3,2,1]
2、解题
递归
public class Solution {
public void traversal(TreeNode root, List<int> result)
{
if(root == null)
{
return;
}
traversal(root.left, result);
traversal(root.right, result);
result.Add(root.val);
}
public IList<int> PostorderTraversal(TreeNode root) {
List<int> result = new List<int>{};
traversal(root, result);
return result;
}
}
迭代
3、总结
2、广度优先遍历
102.二叉树的层序遍历
1、题目
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
2、解题
public class Solution {
public IList<IList<int>> LevelOrder(TreeNode root) {
IList<IList<int>> result = new List<IList<int>>();
if(root == null) return result;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
while(queue.Count > 0)
{
int levelSize = queue.Count;
IList<int> currentLevel = new List<int>();
for(int i = 0; i < levelSize; i ++)
{
TreeNode node = queue.Dequeue();
currentLevel.Add(node.val);
if(node.left != null)
{
queue.Enqueue(node.left);
}
if(node.right != null)
{
queue.Enqueue(node.right);
}
}
result.Add(currentLevel);
}
return result;
}
}
3、总结
利用队列先进先出模拟遍历顺序;
用levelSize = queue.Count储存每次出队元素数量;
107.二叉树的层次遍历II
1、题目
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]
2、解题
public class Solution {
public IList<IList<int>> LevelOrderBottom(TreeNode root) {
IList<IList<int>> result = new List<IList<int>>();
if(root == null) return result;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
while(queue.Count > 0)
{
int levelSize = queue.Count;
IList<int> currentLevel = new List<int>();
for(int i = 0; i < levelSize; i ++)
{
TreeNode node = queue.Dequeue();
currentLevel.Add(node.val);
if(node.left != null)
{
queue.Enqueue(node.left);
}
if(node.right != null)
{
queue.Enqueue(node.right);
}
}
result.Insert(0,currentLevel);
}
return result;
}
}
3、总结
最后使用Reverse()方法不判对
可以使用Insert(idx,val)方法直接将子列表插入头部
199.二叉树的右视图
1、题目
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例 1:
输入: [1,2,3,null,5,null,4]
输出: [1,3,4]
提示:
二叉树的节点个数的范围是 [0,100]
-100 <= Node.val <= 100
2、解题
public class Solution {
public IList<int> RightSideView(TreeNode root) {
IList<int> result = new List<int>();
if(root == null) return result;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
while(queue.Count > 0)
{
int levelSize = queue.Count;
for(int i = 0; i < levelSize; i ++)
{
TreeNode node = queue.Dequeue();
if(i == levelSize - 1)
{
result.Add(node.val);
}
if(node.left != null)
{
queue.Enqueue(node.left);
}
if(node.right != null)
{
queue.Enqueue(node.right);
}
}
}
return result;
}
}
3、总结
出结点时增加判断到层尾时加入result
637.二叉树的层平均值
1、题目
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[3.00000,14.50000,11.00000]
解释:第0层的平均值为 3,第1层的平均值为 14.5,第2层的平均值为11 。因此返回 [3, 14.5, 11] 。
2、解题
IList<double> result = new List<double>();
if (root == null) return result;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
while (queue.Count > 0) {
int levelSize = queue.Count;
double levelSum = 0;
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.Dequeue();
levelSum += node.val;
if (node.left != null) {
queue.Enqueue(node.left);
}
if (node.right != null) {
queue.Enqueue(node.right);
}
}
double levelAverage = levelSum / levelSize;
result.Add(levelAverage);
}
return result;
3、总结
每一层加入求和 & 求平均操作
429.N叉树的层序遍历
1、题目
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。
树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
示例 1:
输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]
2、解题
public class Solution {
public IList<IList<int>> LevelOrder(Node root) {
IList<IList<int>> result = new List<IList<int>>();
if(root == null) return result;
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(root);
while(queue.Count > 0)
{
int levelSize = queue.Count;
IList<int> currentLevel = new List<int>();
for(int i = 0; i < levelSize; i ++)
{
Node node = queue.Dequeue();
currentLevel.Add(node.val);
foreach(Node child in node.children)
{
queue.Enqueue(child);
}
}
result.Add(currentLevel);
}
return result;
}
}
3、总结
结点左右子结点入队操作改为遍历结点孩子列表并入队操作
515.在每个树行中找最大值
1、题目
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
示例1:
输入: root = [1,3,2,5,3,null,9]
输出: [1,3,9]
提示:
二叉树的节点个数的范围是 [0,104]
- <= Node.val <= - 1
2、解题
public class Solution {
public IList<int> LargestValues(TreeNode root) {
IList<int> result = new List<int>();
if(root == null) return result;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
while(queue.Count > 0)
{
int levelSize = queue.Count;
int max = queue.Peek().val;
for(int i = 0; i < levelSize; i ++)
{
TreeNode node = queue.Dequeue();
if(node.val > max)
{
max= node.val;
}
if(node.left != null)
{
queue.Enqueue(node.left);
}
if(node.right != null)
{
queue.Enqueue(node.right);
}
}
result.Add(max);
}
return result;
}
}
3、总结
若智啊!!!max初始值设队友元素数据值!!!
116.填充每个节点的下一个右侧节点指针
1、题目
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例 1:
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。
2、解题
public class Solution {
public Node Connect(Node root) {
if(root == null) return root;
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(root);
while(queue.Count > 0)
{
int levelSize = queue.Count;
Node prev = null;
for(int i = 0; i < levelSize; i ++)
{
Node cur = queue.Dequeue();
if(cur.left != null)
{
queue.Enqueue(cur.left);
}
if(cur.right != null)
{
queue.Enqueue(cur.right);
}
if(prev != null)
{
prev.next = cur;
}
if(i == levelSize - 1)
{
cur.next = null;
}
prev = cur;
}
}
return root;
}
}
3、总结
错误:
遍历到每层最后一个元素时,依然要执行prev.next = cur
其实对于每次遍历执行的逻辑一样,只是要单独赋值最后一个结点,也就是遍历完成后的prev
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if (root != NULL) que.push(root);
while (!que.empty()) {
int size = que.size();
// vector<int> vec;
Node* nodePre;
Node* node;
for (int i = 0; i < size; i++) {
if (i == 0) {
nodePre = que.front(); // 取出一层的头结点
que.pop();
node = nodePre;
} else {
node = que.front();
que.pop();
nodePre->next = node; // 本层前一个节点next指向本节点
nodePre = nodePre->next;
}
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
nodePre->next = NULL; // 本层最后一个节点指向NULL
}
return root;
}
};
117.填充每个节点的下一个右侧节点指针II
1、题目
给定一个二叉树:
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 指针连接),'#' 表示每层的末尾。
提示:
树中的节点数在范围 [0, 6000] 内
-100 <= Node.val <= 100
进阶:
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序的隐式栈空间不计入额外空间复杂度。
2、解题
public class Solution {
public Node Connect(Node root) {
if(root == null) return root;
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(root);
while(queue.Count > 0)
{
int levelSize = queue.Count;
Node prev = null;
Node cur = queue.Peek();
for(int i = 0; i < levelSize; i ++)
{
cur = queue.Dequeue();
if(prev != null)
{
prev.next = cur;
}
if(cur.left != null)
{
queue.Enqueue(cur.left);
}
if(cur.right != null)
{
queue.Enqueue(cur.right);
}
prev = cur;
}
prev.next = null;
}
return root;
}
}
3、总结
104.二叉树的最大深度
1、题目
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:3
提示:
树中节点的数量在 [0, 104] 区间内。
-100 <= Node.val <= 100
2、解题
遍历
public class Solution {
public int MaxDepth(TreeNode root) {
if(root == null) return 0;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
int depth = 0;
while(queue.Count > 0)
{
depth ++;
int levelSize = queue.Count;
for(int i = 0; i < levelSize; i ++)
{
TreeNode node = queue.Dequeue();
if(node.left != null)
{
queue.Enqueue(node.left);
}
if(node.right != null)
{
queue.Enqueue(node.right);
}
}
}
return depth;
}
}
递归(后序)
public class Solution {
public int MaxDepth(TreeNode root)
{
int result = 0;
return recursion(result , root);
}
public int recursion(int depth, TreeNode root)
{
if(root == null) return depth;
depth ++;
return Max(recursion(depth, root.left),recursion(depth, root.right));
}
public int Max(int a, int b)
{
if(a >= b) return a;
return b;
}
}
递归(前序)
public class Solution {
private int result;
// Function to traverse the binary tree in preorder and update the maximum depth
private void GetDepth(TreeNode node, int depth) {
// Update the result with the maximum depth encountered
result = depth > result ? depth : result; // 中
// Check if the current node is a leaf node, if so, return
if (node.left == null && node.right == null) return;
// Traverse the left subtree
if (node.left != null) {
depth++; // Increase depth by 1
GetDepth(node.left, depth);
depth--; // Backtrack, decrease depth by 1
}
// Traverse the right subtree
if (node.right != null) {
depth++; // Increase depth by 1
GetDepth(node.right, depth);
depth--; // Backtrack, decrease depth by 1
}
}
// Function to find the maximum depth of the binary tree
public int MaxDepth(TreeNode root) {
result = 0; // Initialize result to store maximum depth
if (root == null) return result; // If the tree is empty, return 0
GetDepth(root, 1); // Start preorder traversal from the root node with depth 1
return result; // Return the maximum depth of the tree
}
}
3、总结
层序遍历 or 递归 + 计数器depth
111.二叉树的最小深度
1、题目
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:2
提示:
树中节点数的范围在 [0, 105] 内
-1000 <= Node.val <= 1000
2、解题
层序遍历
public class Solution {
public int MinDepth(TreeNode root) {
if(root == null) return 0;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
int depth = 0;
while(queue.Count > 0)
{
depth ++;
int levelSize = queue.Count;
for(int i = 0; i < levelSize; i ++)
{
TreeNode node = queue.Dequeue();
if(node.left == null && node.right == null)
{
return depth;
}
if(node.left != null)
{
queue.Enqueue(node.left);
}
if(node.right != null)
{
queue.Enqueue(node.right);
}
}
}
return depth;
}
}
递归(后序遍历)
public class Solution {
public int MinDepth(TreeNode root)
{
if(root == null) return 0;
int leftDepth = MinDepth(root.left);
int rightDepth = MinDepth(root.right);
if(root.left == null && root.right != null)
{
return rightDepth + 1;
}
if(root.left != null && root.right == null)
{
return leftDepth + 1;
}
return (leftDepth < rightDepth ? leftDepth : rightDepth) + 1;
}
}
3、总结
层序遍历 + 计数器和结点是否为叶子结点判断
递归:
错误代码:
public class Solution {
public int MinDepth(TreeNode root)
{
int result = 0;
return getMin(result, root);
}
public int getMin(int depth, TreeNode root)
{
if(root == null) return depth;
depth = getMin(depth,root.left) < getMin(depth,root.right) ? getMin(depth,root.left) : getMin(depth,root.right);
depth ++;
return depth;
}
}
没有考虑左右子树中有一个为空的情况
3、操作
226.翻转二叉树
1、题目
2、解题
递归:
public class Solution {
public void reverse(TreeNode node)
{
if(node == null) return;
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
reverse(node.left);
reverse(node.right);
}
public TreeNode InvertTree(TreeNode root) {
reverse(root);
return root;
}
}
遍历:
public class Solution {
public TreeNode InvertTree(TreeNode root) {
if(root == null) return root;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
while(queue.Count > 0)
{
int levelSize = queue.Count;
for(int i = 0; i < levelSize; i ++)
{
TreeNode node = queue.Dequeue();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if(node.left != null)
{
queue.Enqueue(node.left);
}
if(node.right != null)
{
queue.Enqueue(node.right);
}
}
}
return root;
}
}
3、总结
617.合并二叉树
1、题目
2、解题
3、总结
106.从中序与后序遍历序列构造二叉树
1、题目
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]
提示:
1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder 和 postorder 都由 不同 的值组成
postorder 中每一个值都在 inorder 中
inorder 保证是树的中序遍历
postorder 保证是树的后序遍历
2、解题
3、总结
105.从前序与中序遍历序列构造二叉树
1、题目
2、解题
3、总结
4、查找
101. 对称二叉树
1、题目
2、解题
3、总结
104.二叉树的最大深度
见上
559.n叉树的最大深度
1、题目
给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。
示例 1:
输入:root = [1,null,3,2,4,null,5,6]
输出:3
2、解题
public class Solution {
public int MaxDepth(Node root) {
int result = 0;
return GetDepth(result, root);
}
public int GetDepth(int depth, Node root)
{
if(root == null) return depth;
List<int> childrendepth = new List<int>();
for(int i = 0; i < root.children.Count; i ++)
{
childrendepth.Add(GetDepth(depth,root.children[i]));
}
depth = GetMax(childrendepth);
depth ++;
return depth;
}
public int GetMax(List<int> list)
{
int max = 0;
for(int i = 0; i < list.Count; i ++)
{
if(i == 0)
{
max = list[0];
}
max = list[i] > max ? list[i] : max;
}
return max;
}
}
3、总结
左右子树深度换成所有子树深度
111.二叉树的最小深度
见上
222.完全二叉树的节点个数
1、题目
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例 1:
输入:root = [1,2,3,4,5,6]
输出:6
提示:
树中节点的数目范围是[0, 5 * 104]
0 <= Node.val <= 5 * 104
题目数据保证输入的树是 完全二叉树
2、解题
层序+遍历
public class Solution {
#region 层序
public int CountNodes(TreeNode root) {
if(root == null) return 0;
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
int count = 0;
while(queue.Count > 0)
{
int levelSize = queue.Count;
for(int i = 0; i < levelSize; i ++)
{
count ++;
TreeNode node = queue.Dequeue();
if(node.left != null)
{
queue.Enqueue(node.left);
}
if(node.right != null)
{
queue.Enqueue(node.right);
}
}
}
return count;
}
#endregion
#region 递归
public int CountNodes(TreeNode root)
{
if(root == null) return 0;
return CountNodes(root.left) + CountNodes(root.right) + 1;
}
#endregion
}
3、总结
普通二叉树 & 完全二叉树的写法:
完全二叉树:
404.左叶子之和
1、题目
给定二叉树的根节点 root ,返回所有左叶子之和。
示例 1:
输入: root = [3,9,20,null,null,15,7]
输出: 24
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
2、解题
public class Solution {
public int SumOfLeftLeaves(TreeNode root) {
if(root == null) return 0;
int add = 0;
if(root.left != null && root.left.left == null && root.left.right == null)
{
add += root.left.val;
}
int sumLeft = SumOfLeftLeaves(root.left);
int sumRight = SumOfLeftLeaves(root.right);
return add + sumLeft + sumRight;
}
}
3、总结
误区:认为父结点和子结点共享左叶子结点,若只。。。
单层递归的逻辑:
判断当前结点的左子结点是否为左叶子结点
寻找当前节点的子结点的左叶子结点
513.找树左下角的值
1、题目
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
示例 1:
输入: root = [2,1,3]
输出: 1
示例 2:
输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7
提示:
二叉树的节点个数的范围是 [1,104]
- <= Node.val <= - 1
2、解题
public class Solution {
int MaxDepth = -1;
int mostleftValue;
public int FindBottomLeftValue(TreeNode root) {
traversal(root, 0);
return mostleftValue;
}
public void traversal(TreeNode root, int depth)
{
if(root == null) return;
if(root.left == null && root.right == null)
{
if(depth > MaxDepth)
{
MaxDepth = depth;
mostleftValue = root.val;
}
}
traversal(root.left, depth + 1);
traversal(root.right, depth + 1);
}
}
3、总结
key:
优先遍历左结点
257. 二叉树的所有路径
1、题目
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
2、解题
public class Solution {
public IList<string> BinaryTreePaths(TreeNode root) {
List<string> result = new List<string>();
if (root != null)
Traverse(root, "", result);
return result;
}
private void Traverse(TreeNode node, string path, List<string> result) {
// Append current node's value to the current path
path += node.val.ToString();
// Check if the current node is a leaf node (no children)
if (node.left == null && node.right == null) {
// If it is a leaf node, add the current path to the result
result.Add(path);
return;
}
// If the current node has a left child, traverse left
if (node.left != null)
Traverse(node.left, path + "->", result);
// If the current node has a right child, traverse right
if (node.right != null)
Traverse(node.right, path + "->", result);
}
}
112. 路径总和
1、题目
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
示例 2:
输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。
2、解题
public class Solution {
List<int> sums;
int sum = 0;
public bool HasPathSum(TreeNode root, int targetSum) {
sums = new List<int>();
traversal(root, sum, sums);
for(int i = 0; i < sums.Count; i ++)
{
if(sums[i] == targetSum)
{
return true;
}
}
return false;
}
public void traversal(TreeNode root, int sum, List<int> sums)
{
if(root == null) return;
sum += root.val;
if(root.left == null && root.right == null)
{
sums.Add(sum);
}
traversal(root.left, sum, sums);
traversal(root.right, sum, sums);
}
}
3、总结
优解
public bool HasPathSum(TreeNode root, int targetSum)
{
if(root == null) return false;
return traversal(root, targetSum);
}
private bool traversal(TreeNode root, int count)
{
if(root.left == null && root.right == null && count == root.val)
{
return true;
}
if(root.left != null)
{
if(traversal(root.left, count - root.val)) return true;
}
if(root.right != null)
{
if(traversal(root.right, count - root.val)) return true;
}
return false;
}
精简:
public bool HasPathSum(TreeNode root, int targetSum)
{
if(root == null) return false;
if(root.left == null && root.right == null && targetSum == root.val)
{
return true;
}
return HasPathSum(root.left, targetSum - root.val) || HasPathSum(root.right, targetSum - root.val);
}
113.路径总和ii
1、题目
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
示例 2:
输入:root = [1,2,3], targetSum = 5
输出:[]
2、解题
public class Solution {
public IList<IList<int>> PathSum(TreeNode root, int targetSum) {
IList<int> path = new List<int>();
IList<IList<int>> results = new List<IList<int>>();
int sum = 0;
traversal(root, targetSum, path, results);
return results;
}
public void traversal(TreeNode root, int targetSum, IList<int> path, IList<IList<int>> results)
{
if(root == null) return;
path.Add(root.val);
if(root.left == null && root.right == null && root.val == targetSum)
{
results.Add(new List<int>(path));
}
traversal(root.left, targetSum - root.val, path, results);
traversal(root.right, targetSum - root.val, path, results);
path.RemoveAt(path.Count - 1);
}
}
3、总结
654.最大二叉树
1、题目
2、解题
3、总结
236. 二叉树的最近公共祖先
1、题目
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
示例 2:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
提示:
树中节点数目在范围 [2, 105] 内。
-109 <= Node.val <= 109
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。
2、解题
public class Solution {
public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
if(root == p || root == q) return root;
TreeNode left = LowestCommonAncestor(root.left, p, q);
TreeNode right = LowestCommonAncestor(root.right, p, q);
if(left != null && right != null)
{
return root;
}
else if(left == null && right != null)
{
return right;
}
else if(left != null && right == null)
{
return left;
}
else
{
return null;
}
}
}
3、总结
5、二叉搜索树
110.平衡二叉树
1、题目
给定一个二叉树,判断它是否是平衡二叉树
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:true
2、解题
public class Solution {
public bool IsBalanced(TreeNode root) {
int result = GetHeight(root);
if(result == -1) return false;
return true;
}
//后序遍历(求高度)
public int GetHeight(TreeNode node)
{
int result;
//终止条件1:结点为空
if(node == null) return 0;
int leftHeight = GetHeight(node.left); //左
int rightHeight = GetHeight(node.right); //右
//终止条件2:左右子树中存在不平衡子树
if(leftHeight == -1 || rightHeight == -1) return -1;
//左右子树均平衡时,判断左右结点高度差
if(leftHeight - rightHeight > 1 || leftHeight - rightHeight < -1) result = -1;
else result = 1 + (leftHeight > rightHeight ? leftHeight : rightHeight);
return result;
}
}
3、总结
平衡二叉树定义 —> 后序遍历求高度
700.二叉搜索树中的搜索
1、题目
给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
示例 1:
输入:root = [4,2,7,1,3], val = 2
输出:[2,1,3]
2、解题
public class Solution {
public TreeNode SearchBST(TreeNode root, int val) {
if(root == null) return null;
if(val == root.val)
{
return root;
}
else if(val < root.val)
{
return SearchBST(root.left, val);
}
else
{
return SearchBST(root.right, val);
}
}
}
3、总结
98.验证二叉搜索树
1、题目
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true
2、解题
public class Solution {
public bool IsValidBST(TreeNode root) {
List<int> result = new List<int>();
Traversal(root, result);
for(int i = 0; i < result.Count; i ++)
{
if(i > 0)
{
if(result[i] < result[i - 1] || result[i] == result[i - 1]) return false;
}
}
return true;
}
public void Traversal(TreeNode root, List<int> result)
{
if(root == null) return;
Traversal(root.left, result);
result.Add(root.val);
Traversal(root.right, result);
}
}
3、总结
二叉搜索树的通用思路:
拉成有序数列再操作
530.二叉搜索树的最小绝对差
1、题目
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
示例 1:
输入:root = [4,2,6,1,3]
输出:1
2、解题
public class Solution {
int result = int.MaxValue;
TreeNode pre;
public int GetMinimumDifference(TreeNode root) {
if(root == null) return 0;
Traversal(root);
return result;
}
public void Traversal(TreeNode cur)
{
if(cur == null) return;
Traversal(cur.left);
if(pre != null)
{
result = result < Math.Abs(cur.val - pre.val) ? result : Math.Abs(cur.val - pre.val);
}
pre = cur;
Traversal(cur.right);
}
}
3、总结
误区:
public class Solution {
public int GetMinimumDifference(TreeNode root) {
int result = int.MaxValue;
TreeNode pre = null;
result = Traversal(root, pre, result);
return result;
}
public int Traversal(TreeNode cur, TreeNode pre, int result)
{
if(cur == null) return result;
}
}
可以在Solution Class里面定义全局变量,整体的看solution,解决方案不是这个类的某几个方法(如GetMinimumDifference(TreeNode root))而是整个类。
思路:
递归 + 双指针;
递归实现遍历,遍历中运用双指针思想
501.二叉搜索树中的众数
1、题目
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
结点左子树中所含节点的值 小于等于 当前节点的值
结点右子树中所含节点的值 大于等于 当前节点的值
左子树和右子树都是二叉搜索树
示例 1:
输入:root = [1,null,2,2]
输出:[2]
2、解题
public class Solution {
TreeNode pre;
int MaxCount;
int count;
List<int> result;
public int[] FindMode(TreeNode root) {
this.result = new List<int>();
pre = null;
MaxCount = 0;
count = 0;
Traverse(root);
int[] Result = new int[result.Count];
for(int i = 0; i < result.Count; i ++)
{
Result[i] = result[i];
}
return Result;
}
public void Traverse(TreeNode cur)
{
if(cur == null) return;
Traverse(cur.left);
if(pre == null)
{
count = 1;
}
else if(cur.val == pre.val)
{
count ++;
}
else
{
count = 1;
}
if(count == MaxCount)
{
result.Add(cur.val);
}
if(count > MaxCount)
{
result.Clear();
result.Add(cur.val);
MaxCount = count;
}
pre = cur;
Traverse(cur.right);
}
}
3、总结
需要标识的变量:
最大次数,出现次数,结果列表
结点操作逻辑:
更新次数:
第一个结点count = 1;
后续结点值与pre一样count++;
后续结点值与pre不同count = 1;
根据次数更新结果列表和最大次数:
等于最大次数,加入列表;
大于最大次数,更新列表,更新最大次数
235. 二叉搜索树的最近公共祖先
1、题目
2、解题
3、总结
701.二叉搜索树中的插入操作
1、题目
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
示例 1:
输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
解释:另一个满足题目要求可以通过的树是:
2、解题
public class Solution {
public TreeNode InsertIntoBST(TreeNode root, int val) {
if(root == null)
{
return new TreeNode(val);
}
if(val < root.val)
{
root.left = InsertIntoBST(root.left, val);
}
if(val > root.val)
{
root.right = InsertIntoBST(root.right, val);
}
return root;
}
}
3、总结
依托答辩:
public class Solution {
TreeNode pre = null;
public TreeNode InsertIntoBST(TreeNode root, int val) {
Traversal(root, pre, val);
return root;
}
public void Traversal(TreeNode cur, TreeNode pre, int val)
{
if(cur == null)
{
if(pre == null)
{
cur = new TreeNode(val);
return;
}
else
{
if(val < pre.val)
{
pre.left = new TreeNode(val);
return;
}
if(val > pre.val)
{
pre.right = new TreeNode(val);
return;
}
}
}
pre = cur;
if(val < cur.val)
{
Traversal(cur.left, pre, val);
}
if(val > cur.val)
{
Traversal(cur.right, pre, val);
}
}
}
450.删除二叉搜索树中的节点
1、题目
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
示例 1:
输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
解释:另一个满足题目要求可以通过的树是:
提示:
树中的节点数将在 [0, 104]的范围内。
-108 <= Node.val <= 108
所有值 Node.val 是 独一无二 的。
-108 <= val <= 108
保证 val 在原始BST中不存在。
2、解题
public class Solution {
public TreeNode DeleteNode(TreeNode root, int key) {
if(root == null) return root;
if(root.val == key)
{
if(root.left == null)
{
root = root.right;
}
else if(root.right == null)
{
root = root.left;
}
else
{
TreeNode minNode = FindMin(root.right);
int value = minNode.val;
minNode.val = root.val;
root.val = value;
root.right = DeleteNode(root.right, key);
}
}
else if(key < root.val)
{
root.left = DeleteNode(root.left, key);
}
else
{
root.right = DeleteNode(root.right, key);
}
return root;
}
public TreeNode FindMin(TreeNode root)
{
while(root.left != null)
{
root = root.left;
}
return root;
}
}
3、总结
错误:
将两结点值互换错写成赋值
else
{
TreeNode minNode = FindMin(root.right);
int value = minNode.val;
minNode.val = root.val;
root.val = value;
root.right = DeleteNode(root.right, key);
}
669. 修剪二叉搜索树
1、题目
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
示例 1:
输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]
示例 2:
输入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3
输出:[3,2,null,1]
提示:
树中节点数在范围 [1, ] 内
0 <= Node.val <=
树中每个节点的值都是 唯一 的
题目数据保证输入是一棵有效的二叉搜索树
0 <= low <= high <=
2、解题
public class Solution {
public TreeNode TrimBST(TreeNode root, int low, int high) {
if(root == null) return null;
if(root.val < low)
{
return TrimBST(root.right, low, high);
}
if(root.val > high)
{
return TrimBST(root.left, low, high);
}
root.left = TrimBST(root.left, low, high);
root.right = TrimBST(root.right, low, high);
return root;
}
}
3、总结
因为是修剪,删除的是根节点和整个左/右子树,反而比删除操作简单。
108.将有序数组转换为二叉搜索树
1、题目
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵平衡二叉搜索树。
示例 1:
输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 按 严格递增 顺序排列