代码随想录算法训练营第二十一天| 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先

[LeetCode] 530. 二叉搜索树的最小绝对差

[LeetCode] 530. 二叉搜索树的最小绝对差 文章解释

[LeetCode] 530. 二叉搜索树的最小绝对差 视频解释

题目:

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值

差值是一个正数,其数值等于两值之差的绝对值。

示例 1:

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

示例 2:

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

提示:

  • 树中节点的数目范围是 [2, 104]
  • 0 <= Node.val <= 10^5

[LeetCode] 530. 二叉搜索树的最小绝对差

自己看到题目的第一想法

    先看了一眼文章解释, 被一句话点醒了~

看完代码随想录之后的想法

    看到的第一句话就是: 二叉搜索树右子树节点的值都是大于等于左子树节点以及跟节点的值的. 这样的话就会立刻想起来, 一棵二叉搜索树的中序遍历序列是一个非递减的有序序列. 这样只需要在遍历的过程中,记录当前节点和前一个节点的差, 记录下最小的值即可.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
// 递归法: 
//   核心思想: 对于一个二叉搜索树, 中序遍历后, 遍历的序列是递增的.
class Solution {
    private TreeNode preNode;
    private int minmumDifference = -1;
    public int getMinimumDifference(TreeNode root) {
        if (root == null) {
            return -1;
        }
        int leftTreeMinimumDifference = getMinimumDifference(root.left);
        if (preNode != null && (minmumDifference == -1 || root.val - preNode.val < minmumDifference)) {
            minmumDifference = root.val - preNode.val;
        }
        preNode = root;
        int rightTreeMinimumDifference = getMinimumDifference(root.right);
        if (leftTreeMinimumDifference >= 0) {
            minmumDifference = Math.min(leftTreeMinimumDifference, minmumDifference);
        }
        if (rightTreeMinimumDifference >= 0) {
            minmumDifference = Math.min(rightTreeMinimumDifference, minmumDifference);
        }
        return minmumDifference;
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
// 迭代法: 这里用了前中序遍历统一风格法.
//   核心思想: 对于一个二叉搜索树, 中序遍历后, 遍历的序列是递增的.
class Solution {
    public int getMinimumDifference(TreeNode root) {
        Stack<TreeNode> nodes = new Stack<>();
        TreeNode node = null;
        int lastNodeVal = -1;
        int minmumDifference = -1;
        nodes.push(root);
        while (!nodes.isEmpty()) {
            node = nodes.pop();
            if (node != null) {
                if (node.right != null) {
                    nodes.push(node.right);
                }
                nodes.push(node);
                nodes.push(null);
                if (node.left != null) {
                    nodes.push(node.left);
                }
            } else {
                node = nodes.pop();
                if (lastNodeVal != -1 && (minmumDifference == -1 || node.val - lastNodeVal < minmumDifference)) {
                    minmumDifference = node.val - lastNodeVal;
                }
                lastNodeVal = node.val;
            }
        }
        return minmumDifference;
    }
}

自己实现过程中遇到哪些困难

    无

[LeetCode] 501. 二叉搜索树中的众数

[LeetCode] 501. 二叉搜索树中的众数 文章解释

[LeetCode] 501. 二叉搜索树中的众数 视频解释

题目:

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。

如果树中有不止一个众数,可以按 任意顺序 返回。

假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值
  • 结点右子树中所含节点的值 大于等于 当前节点的值
  • 左子树和右子树都是二叉搜索树

示例 1:

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

示例 2:

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

提示:

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

进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)

[LeetCode] 501. 二叉搜索树中的众数

自己看到题目的第一想法

    1. 看到二叉搜索树, 第一反应就是中序是递增或者递减的.

    2. 既然遍历后的序列是递增的, 那么只要拿 number 记录上一个数字, 拿 count 记录当前数字出现的次数, 同时用 maxCount 表示当前出现次数最大的数字出现的次数. 如果 count 比 maxCount 大则要清空结果列表 List<Integer> 后添加, 如果 count == maxCount, 则直接添加到结果列表 List<Integer> 中.

    3. 通过中序遍历数组.

看完代码随想录之后的想法

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    private List<Integer> result = new ArrayList<>();
    private int maxCount = 0;

    private int number = 0;
    private int count = 0;
    
    public int[] findMode(TreeNode root) {
        findModeRecursion(root);
        int[] resultArray = new int[result.size()];
        for (int i = 0; i < result.size(); ++i) {
            resultArray[i] = result.get(i);
        }
        return resultArray;
    }

    public void findModeRecursion(TreeNode root) {
        
        if (root.left != null) {
            // 最早的时候这里不小心调用 findMode... 导致耗时很长
            findModeRecursion(root.left);
        }

        if (root.val != number) {
            number = root.val;
            count = 1;
        } else {
            count++;
        }

        if (maxCount < count) {
            result.clear();
            result.add(number);
            maxCount = count;
        } else if (maxCount == count) {
            result.add(number);
        }

        if (root.right != null) {
            // 最早的时候这里不小心调用 findMode... 导致耗时很长
            findModeRecursion(root.right);
        }
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
// 迭代法
class Solution {
    
    private List<Integer> result = new ArrayList<>();
    private int maxCount = 0;

    private int number = 0;
    private int count = 0;
    
    public int[] findMode(TreeNode root) {
        if (root == null) {
            return new int[0];
        }
        Stack<TreeNode> nodes = new Stack<>();
        TreeNode node = root;
        while (node != null || !nodes.isEmpty()) {
            if (node != null) {
                nodes.push(node);
                node = node.left;
            } else {
                node = nodes.pop();
                if (node.val != number) {
                    count = 1;
                } else {
                    count++;
                }
                if (maxCount == count) {
                    result.add(node.val);
                } else if (maxCount < count) {
                    maxCount = count;
                    result.clear();
                    result.add(node.val);
                }
                number = node.val;
                node = node.right;
            }
        }
        int[] resultArray = new int[result.size()];
        for (int i = 0; i < result.size(); ++i) {
            resultArray[i] = result.get(i);
        }
        return resultArray;
    }
}

 

自己实现过程中遇到哪些困难

    刚开始使用双指针的时候, 是拿当前节点和上一个节点做比较, 如果发现不想等, 才将节点添加到 List<Integer>. 一开始以为只有这样才能知道当前字符出现的次数, 以及该频率的节点是否可以添加到结果中.

    这样做有一个问题就是, 如果所有数字都是相等的, 因为没有触发节点不想等的逻辑了.

    后来发现不行, 于是就只能在根据当前节点的值更新完 count 后, 去判断当前节点是否要添加到结果集合中. 后来发现, 如果当前 maxCount = n, 而 当前节点的 count = n, 把当前节点添加到结果集后, 尽管下一个节点和当前节点的值是一样的, 这样说明 maxCount 要更新了, 只要清空结果集重新添加一次就可以. 完美解决...

    当然, 递归方法1中, 出现了一次愚蠢的错误, 递归调用时, 调错了函数, 导致时间很长. 和随想录还有 leetcode 官方题解对了好几遍, 修改成几乎完全一样还是不对... 后来才发现调用错了函数... 所以日常没有 test case 的时候... 这样的粗心大意可是很危险的.

[LeetCode] 236. 二叉树的最近公共祖先

[LeetCode] 236. 二叉树的最近公共祖先 文章解释

[LeetCode] 236. 二叉树的最近公共祖先 视频解释

题目:

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 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 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

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

提示:

  • 树中节点数目在范围 [2, 105] 内。
  • -10^9 <= Node.val <= 10^9
  • 所有 Node.val 互不相同
  • p != q
  • pq 均存在于给定的二叉树中。

[LeetCode] 236. 二叉树的最近公共祖先

自己看到题目的第一想法

第一次:

    需要回溯! 回溯的模板我熟悉!

    首先向左侧遍历, 当找到p或者q对应的节点后, 继续往下遍历.

    于是我迷失在了节点的相互关系中.

第二次:

    递归吧, 想不明白的时候递归总是能解救迷茫的算法er.

    首先递归求左子树是否存在p或q, 再递归求右子树是否存在p或q.

    如果左子树和右子树都返回非空, 则当前节点就是公共节点.

    (提交后报错, 虽然左子树和右子树没有同时非空, 但这时候前节点是p或q中的一个, 导致返回了错误的结果)

    那再加一个条件, 如果 左子树和右子树 不同时为空, 再加一个当前节点的判断, 如果匹配p或者q中的一个节点, 则返回当前节点.

    (提交后报错, 因为这时候, 递归函数返回的节点, 可能只代表p或者q中的一个, 也可能代表p和q两者, 因此当 leftNode 和 rightNode 并非同时非空时, 不知道如何返回了)

    那就用一个数字来计数吧, 这样就知道p和q有没有都匹配上了, 但是因为我们最终的出口只有一个节点, 因此需要在主函数中接住这个节点. 同时判断匹配的数量是否为 2. 是则返回该节点, 否则返回 Null.

    至此解决问题.

第三次:

    在第二次中解决问题时, 遗漏了一个问题. 那就是题目保证了p和q是一定存在的. 这会大大的简化解题的逻辑.

看完代码随想录之后的想法

    因为 p 和 q 一定存在于二叉树中, 因此整体分两种情况:

        1. p 和 q 分别位于某个节点的左右子树中

        2. p 和 q 中有一个是公共节点

    因此, 当我们匹配到 p 或者 q 中的一个节点时, 就可以直接返回, 以该节点为根节点的子树可以不用考虑了. 因为如果该节点是公共子节点的孩子节点的话, 那另外一个就在父节点的另外一个子树中, 可以被遍历到. 或者不在该节点的父节点的另一个子树中, 而是在祖先结点的另一个子树中, 因此也可以被遍历到. 如果在其他地方找不到另外一个待匹配的节点的话, 那自然说明该节点的子节点中, 一定右另外一个可以匹配的 p 或者 q 节点.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
// 自己的解法: 后序遍历, 不需要p和q同时存在(未验证)
class Solution {

    private TreeNode result = null;
    private int matchCount = 0;

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode node = findNode(root, p, q);
        if (node != null && matchCount == 2) {
            return node;
        }
        return null;
    }
    private TreeNode findNode(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        
        TreeNode leftNode = null;
        TreeNode rightNode = null;

        leftNode = findNode(root.left, p, q);
        rightNode = findNode(root.right, p, q);
        if (leftNode != null && rightNode != null) {
            return root;
        } else {
            if (root.val == q.val || root.val == p.val) {
                matchCount++;
                return root;
            }

            if (leftNode != null) {
                return leftNode;
            } else if (rightNode != null) {
                return rightNode;
            }
        }
        
        return null;
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
// 后序遍历: 假设 p 和 q 一定存在
class Solution {

    private TreeNode result = null;
    private int matchCount = 0;

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        TreeNode leftNode = lowestCommonAncestor(root.left, p, q);
        TreeNode rightNode = lowestCommonAncestor(root.right, p, q);
        if (leftNode != null && rightNode != null) {
            return root;
        }
        if (root.val == p.val || root.val == q.val) {
            return root;
        } else if (leftNode != null) {
            return leftNode;
        } else {
            return rightNode;
        }
    }
}

自己实现过程中遇到哪些困难

    当处理了 p 和 q 分别位于公共节点的左右子树时, p 和 q 同时位于公共节点同一侧子树的情况就无法满足. 虽然看似就那么几个判断条件, 但真正实现起来似乎怎么也绕不出来. 不过最后还是做出来了... 就是耗费的时间也真的很长. 在这一题上稍微有点沮丧.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值