二叉树算法设计总路线
明确一个节点要做什么事情,然后剩下的事情交给递归框架
void traverse(TreeNode root) {
// root 需要做什么
// 剩下的交给递归
traverse(root.left);
traverse(root.right);
}
先拿两个二叉树操作练手:
一、二叉树 中每个节点的值加一 (二叉树操作)
void plusOne(TreeNode root) {
if (root == null) return;
root.val += 1;
plusOne(root.left);
plusOne(root.right);
}
二、判断两颗二叉树 是否完全相同 (二叉树操作)
boolean isSameTree(TreeNode root1, TreeNode root2) {
// 如果同时为空,显然相同
if (root1 == null && root2 == null) return true;
// 如果不同时为空,则显然不相同
if (root1 == null || root2 == null) return false;
// 如果两节点值不同,显然不相同
if (root1.val != root2.val) return false;
// 当前root1和root2值相同,比较完毕,接下来判断其左右子树
return isSameTree(root1.left, root2.left) && isSameTree(root1.right, root2.right);
}
二叉搜索树介绍
定义:一个二叉树中,任意节点的值要大于等于左子树所有节点的值,且要小于等于右子树的所有节点的值。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 < 当前节点的数。
- 节点的右子树只包含 > 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
LeetCode 98. 判断BST的合法性
思路
root需要做的是要和其整个左子树和右子树作比较,值要大于等于左子树所有节点的值,且要小于等于右子树的所有节点的值
使用辅助函数,增加函数列表boolean isValidBST(TreeNode root, TreeNode min, TreeNode max); 这样相当于给子树上的所有节点添加了一个min和max边界,约束root的左子树节点值不超过root的值, 右子树节点值不小于root的值
代码实现(java)
boolean isValidBST(TreeNode root) {
return isValidBST(root, null, null);
}
boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) {
// 成功到达叶子节点,说明过程都合法,返回true
if (root == null) return true;
// 如果有min, 且当前root节点值小于min的值,说明不符合二叉搜索树,返回false
if (min != null && root.val <= min.val) return false;
// 如果有max, 且当前root节点值大于max的值,说明不符合二叉搜索树,返回false
if (max != null && root.val >= max.val) return false;
// 当前节点判断完毕,接着判断左子树和右子树
// 左子树中较小值为空,较大值则为其父节点
// 右子树中较大值为空,较小值则为其父节点
return isValidBST(root.left, min, root) && isValidBST(root.right, root, max);
}
LeetCode 700. 二叉搜索树中的搜索
思路
递归思路:
利用二叉搜索树的特性,左小右大
- 当根节点值比val值大时,去左子树找
- 当根节点值比val值小时,去右子树找
- 当根节点值等于val值时,返回给节点
迭代思路:
循环寻找值为val的节点,判断依据为:
- 当根节点值比val值大时,让根节点等于左孩子,继续找
- 当根节点值比val值小时,让根节点等于右孩子,继续找
- 结束迭代的条件为 root == null(没找到)、root.val == val(找到了)
- 最终返回root,没找到就为null,找到了则为root为根的子树
代码实现(java)
/**
* 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 TreeNode searchBST(TreeNode root, int val) {
// 递归
if(root == null || root.val == val) return root;
return root.val > val ? searchBST(root.left, val) : searchBST(root.right, val);
// 迭代
// while(root != null && root.val != val) {
// root = root.val > val ? root.left : root.right;
// }
// return root;
}
}
LeetCode 701. 二叉搜索树中的插入操作
思路
整体的思路就是:遍历二叉搜索树,找到空节点 插入元素就可以了(不考虑改变树结构)
递归思路:
利用二叉搜索树的特性,左小右大
- 当根节点值比val值大时,去左子树找
- 当根节点值比val值小时,去右子树找
- 当根节点值为空时,创建新节点插入
迭代思路:
利用二叉搜索树的特性,左小右大
- 当根节点值比val值大时,去左子树找
- 当根节点值比val值小时,去右子树找
- 最终找到要插入的叶子结点位置,然后判断val的值和该叶子结点的值的大小关系,最终插入即可
代码实现(java)
/**
* 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 TreeNode insertIntoBST(TreeNode root, int val) {
// 迭代 迭代寻找空叶子结点
if(root == null) return new TreeNode(val);
TreeNode resRoot = root; // 记录根节点
TreeNode prev = root; // 记录叶子结点
// 寻找要插入的叶子结点
while(root != null) {
prev = root;
if(root.val > val) {
root = root.left;
} else {
root = root.right;
}
}
// 判断要插在叶子结点的左孩子还是右孩子
if(val > prev.val) prev.right = new TreeNode(val);
else prev.left = new TreeNode(val);
return resRoot;
// 递归 递归寻找空叶子结点
// if(root == null) return new TreeNode(val);
// if(root.val > val) root.left = insertIntoBST(root.left, val);
// else root.right = insertIntoBST(root.right, val);
// return root;
}
}
LeetCode 653. 两数之和 IV - 输入 BST
思路
中序遍历 + 双指针
最简单的思路:题目给出的是二叉搜索树,所以中序遍历可以得到排序好的集合(数组),然后使用双指针,找和为k即可
哈希表 + 深度优先遍历
使用哈希表记录遍历到的节点,判断当前哈希表中,是否包含k - 当前节点的值
,如果存在,说明这两个值加起来就是k,返回true,如果最终遍历结束都没有找到,说明不存在,返回false
代码实现(java)
/**
* 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 {
// 深度优先搜索 + 哈希表
Set<Integer> set = new HashSet<>();
public boolean findTarget(TreeNode root, int k) {
if(root == null) return false;
if(set.contains(k - root.val)) return true;
set.add(root.val);
return findTarget(root.left, k) || findTarget(root.right, k);
}
// 中序遍历 + 双指针
// List<Integer> list = new ArrayList<>();
// public boolean findTarget(TreeNode root, int k) {
// inorder(root);
// int left = 0;
// int right = list.size() - 1;
// while(left < right) {
// int sum = list.get(left) + list.get(right);
// if(sum == k) return true;
// else if(sum < k) left++;
// else right--;
// }
// return false;
// }
// public void inorder(TreeNode node) {
// if(node == null) return;
// inorder(node.left);
// list.add(node.val);
// inorder(node.right);
// }
}
LeetCode 235. 二叉搜索树的最近公共祖先
思路
根据题意可以知道,p和q是在其公共父节点的两端的,所以我们可以进行判断
- 如果p和q都小于 root, 说明p和q都在其左子树,递归到左子树 root.left
- 如果p和q都大于 root, 说明p和q都在其右子树,递归到左子树 root.right
- 当发现前两个条件都不满足,说明p和q在root节点的左右子树上,即root就是最近公共父节点,返回即可
代码实现(java)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
TreeNode res = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// p 和 q 在某个节点的两端, 如果找到既不同时大,也不同时小的节点,就是二者的最近公共父节点
if(root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
if(root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
return root;
}
}