DS | 二叉树_练习

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]
-2^{31} <= Node.val <= 2^{31}  - 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]
-2^{31} <= Node.val <= 2^{31}  - 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, 10^{4}] 内
0 <= Node.val <= 10^{4}
树中每个节点的值都是 唯一 的
题目数据保证输入是一棵有效的二叉搜索树
0 <= low <= high <= 10^{4}

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 按 严格递增 顺序排列

2、解题
3、总结
538.把二叉搜索树转换为累加树
1、题目
2、解题
3、总结
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值