文章目录
- leetcode 94 二叉树的中序遍历
- leetcode 96 不同的二叉搜索树
- leetcode 98 验证二叉搜索树
- leetcode 101 对称二叉树
- Leetcode 102 二叉树的层次遍历
- leetcode 104 二叉树的最大深度
- leetcode 105 从前序和中序构建二叉树
- leetcode 114 二叉树转成链表
- 二叉查找树转成双向链表
- leetcode 124 二叉树中的最大路径和
- leetcode 226 翻转二叉树
- leetcodr 236 二叉树的最近公共祖先
- leetcode 274 二叉树的序列化和反序列化
- leetcode 337 打家劫舍三
- leetcode 437 路径总和三
- leetcode 538 把二叉搜索树转换成累加树
- leetcode 543 树的直径
- leetcode 617 合并二叉树
leetcode 94 二叉树的中序遍历
/**
* 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 {
List<Integer> res=new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
inorder(root);
return res;
}
public void inorder(TreeNode node){
if(node==null) return ;
inorder(node.left);
res.add(node.val);
inorder(node.right);
}
}
leetcode 96 不同的二叉搜索树
分析:
f(i)表示以第i个节点为根节点的不同二叉搜索树的个数。
dp(n) 表示n个节点的不同二叉搜索树的个数。
所以dp(n)=f(1)+f(2)+…+f(n);
以第i个节点为根节点,其左子树有i-1个节点,右子树有n-i个节点
所有f(i)=dp(i-1)*dp(n-i);
f(1)=dp(0)*dp(n-1);
f(2)=dp(1)*dp(n-2);
…
所以
dp(n)=dp(0)*dp(n-1)+dp(1)*dp(n-2)+…+dp(n-1)*dp(0)
class Solution {
public int numTrees(int n) {
int dp[]=new int[n+1];//dp[i] 为i个节点的不同二叉搜索树的个数
//dp[n]=f(1)+f(2)+...+f(n-1); f(i) 为以第i个节点为根节点的不同二叉搜索树的个数。
//当 第i个点为根节点时,其左子树节点个数为 i-1 个,右子树节点为 n-i,
//则f(i) = dp(i-1)*dp(n-i)
//dp(n)=dp(0)dp(n−1)+dp(1)dp(n−2)+...+dp(n−1)∗dp(0)
dp[0]=1;
dp[1]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<i;j++){
dp[i]+=dp[j]*dp[i-1-j];
}
}
return dp[n];
}
}
leetcode 98 验证二叉搜索树
二叉搜索树判断条件
1.左子树是二叉搜索树。
2.右子树是二叉搜索树。
3. 根节点大于左子树的全部值
4. 根节点小于右子树的全部值
1 ,2条件 递归即可判断。。
对于是3,4条件。
中序遍历二叉搜索树会按照升序遍历。
利用中序遍历保存上次的值。与当前值进行比较。
如果大于等于当前值 ,则不成立。
/**
* 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 {
//中序遍历 二叉搜索树 结果为升序.
double pre=-Double.MAX_VALUE;//保存上个节点的值
public boolean isValidBST(TreeNode root) {
if(root==null) return true;
if(!isValidBST(root.left)) return false;
if(pre<root.val) {
pre=root.val;
return isValidBST(root.right);
}
return false;
}
}
leetcode 101 对称二叉树
/**
* 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 {
//其实就是两次层次遍历(差别在于null也入队), 判断结果是都相等
//第一种:左子树先入队列
public boolean isSymmetric(TreeNode root) {
return helper(root,root);
}
// private boolean helper(TreeNode root1,TreeNode root2){
// Queue<TreeNode> q=new LinkedList<>();
// q.offer(root1);
// q.offer(root2);
// while(!q.isEmpty()) {
// TreeNode node1=q.poll();
// TreeNode node2=q.poll();
// if (node1==null&&node2==null) continue;
// if((node1==null||node2==null)||(node1.val!=node2.val)) return false;
// q.offer(node1.left);
// q.offer(node2.right);
// q.offer(node1.right);
// q.offer(node2.left);
// }
// return true;
// }
//两种先序遍历对比
//1. 中 左 右
//2. 中 右 左
private boolean helper(TreeNode v,TreeNode u){
if(v==null&&u==null) return true;
if(v==null||u==null||(v.val!=u.val)) return false;
return helper(v.left,u.right)&&helper(v.right,u.left);
}
}
Leetcode 102 二叉树的层次遍历
/**
* 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 List<List<Integer>> levelOrder(TreeNode root) {
if(root==null) return new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
List<List<Integer>> res=new ArrayList<>();
while(!queue.isEmpty()){
//访问
int size=queue.size();
List<Integer> list=new ArrayList<>();
while(size-->0){
//取元素
TreeNode node=queue.poll();
list.add(node.val);
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
res.add(list);
}
return res;
}
}
leetcode 104 二叉树的最大深度
/**
* 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 maxDepth(TreeNode root) {
if(root==null) return 0;
return Math.max(maxDepth(root.right),maxDepth(root.left))+1;
}
}
leetcode 105 从前序和中序构建二叉树
class Solution{
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder.length == 0) {
return null;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
int pre = 0;
int in = 0;
//先序遍历第一个值作为根节点
TreeNode curRoot = new TreeNode(preorder[pre]);
TreeNode root = curRoot;//存储整棵树根节点
stack.push(curRoot);
pre++;
//遍历前序遍历的数组
while (pre < preorder.length) {
//出现了当前节点的值和中序遍历数组的值相等,寻找是谁的右子树
if (curRoot.val == inorder[in]) {
//每次进行出栈,实现倒着遍历
while (!stack.isEmpty() && stack.peek().val == inorder[in]) {
curRoot = stack.pop();
in++;
}
//设为当前的右孩子
curRoot.right = new TreeNode(preorder[pre]);
//更新 curRoot
curRoot = curRoot.right;
} else {
//否则的话就一直作为左子树
curRoot.left = new TreeNode(preorder[pre]);
curRoot = curRoot.left;
}
stack.push(curRoot);
pre++;
}
return root;
}
class Solution {
private Map<Integer, Integer> indexMap;
public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return null;
}
int preorder_root = preorder_left;
int inorder_root = indexMap.get(preorder[preorder_root]);
TreeNode root = new TreeNode(preorder[preorder_root]);
int size_left_subtree = inorder_root - inorder_left;
root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
// 构造哈希映射,帮助我们快速定位根节点
indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < n; i++) {
indexMap.put(inorder[i], i);
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
}
leetcode 114 二叉树转成链表
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
class Solution {
TreeNode pre=null;
public void flatten1(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
TreeNode prev = null;
while (!stack.isEmpty()) {
TreeNode curr = stack.pop();
if (prev != null) {
prev.left = null;//断开连接
prev.right = curr;
}
if (curr.right != null) {
stack.push(curr.right);
}
if (curr.left != null) {
stack.push(curr.left);
}
prev = curr;
}
}
public void flatten(TreeNode root) {
if(root==null) return;
flatten(root.left);
flatten(root.right);
TreeNode temp=root.right;
root.right=root.left;
root.left = null;//记住将root的左侧置为空
while(root.right!=null){
root=root.right;
}
root.right=temp;
}
}
二叉查找树转成双向链表
private Node head,tail;
public void traversal(Node node){
if(node==null) return;
if(node.leftNode!=null) traversal(node.leftNode);
changeNode(node);
if(node.rightNode!=null) traversal(node.rightNode);
}
private void changeNode(Node node) {
//初始时,双向链表中无节点,head及tail均为null
if(head == null){
head = node;
tail = node;
}else{
//将新node的左指针指向当前tail,再将当前tail的右指针指向新node,最后将tail后移
node.leftNode = tail;
tail.rightNode = node;
tail = node;
}
}
leetcode 124 二叉树中的最大路径和
/**
* 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 {
//返回结果
int res=Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
fun(root);
return res;
}
// 返回当前节点到叶子结点路径上可能的最大值
private int fun(TreeNode root){
if(root==null) {
return 0;
}
int val=root.val;
int left=Math.max(fun(root.left),0);
int right=Math.max(fun(root.right),0);
res=Math.max(val+right+left,res);//(val+right+left将当前节点为根节点的路径的最大值。
return val+Math.max(right,left);
}
}
leetcode 226 翻转二叉树
/**
* 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 invertTree(TreeNode root) {
if(root==null) return null;
TreeNode left=invertTree(root.left);
TreeNode right=invertTree(root.right);
root.right=left;
root.left=right;
return root;
}
}
leetcodr 236 二叉树的最近公共祖先
方法1
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// LCA 问题
if (root == null) {
return root;
}
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) {
return left;
} else if (right != null) {
return right;
}
return null;
}
}
方法2
思路
我们可以用哈希表存储所有节点的父节点,然后我们就可以利用节点的父节点信息从 p 结点开始不断往上跳,并记录已经访问过的节点,再从 q 节点开始不断往上跳,如果碰到已经访问过的节点,那么这个节点就是我们要找的最近公共祖先。
算法
从根节点开始遍历整棵二叉树,用哈希表记录每个节点的父节点指针。
从 p 节点开始不断往它的祖先移动,并用数据结构记录已经访问过的祖先节点。
同样,我们再从 q 节点开始不断往它的祖先移动,如果有祖先已经被访问过,即意味着这是 p 和 q 的深度最深的公共祖先,即 LCA 节点。
class Solution {
Map<Integer, TreeNode> parent = new HashMap<Integer, TreeNode>();
Set<Integer> visited = new HashSet<Integer>();
public void dfs(TreeNode root) {
if (root.left != null) {
parent.put(root.left.val, root);
dfs(root.left);
}
if (root.right != null) {
parent.put(root.right.val, root);
dfs(root.right);
}
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
dfs(root);
while (p != null) {
visited.add(p.val);
p = parent.get(p.val);
}
while (q != null) {
if (visited.contains(q.val)) {
return q;
}
q = parent.get(q.val);
}
return null;
}
}
leetcode 274 二叉树的序列化和反序列化
方法1:BFS(层次遍历)
public String serialize(TreeNode root) {
//序列化
if(root==null) return null;
StringBuilder s=new StringBuilder();
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode node=queue.poll();
if(node!=null){
s.append(node.val+",");
queue.offer(node.left);
queue.offer(node.right);
}else{
s.append("null,");
}
}
return s.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data==null) return null;
String array[]=data.split(",");
TreeNode root=new TreeNode(Integer.parseInt(array[0]));
int i=1;
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
if(!(array[i]).equals("null")){
node.left = new TreeNode(Integer.parseInt(array[i]));
queue.offer(node.left);
}
i++;
if(!(array[i]).equals("null")){
node.right = new TreeNode(Integer.parseInt(array[i]));
queue.offer(node.right);
}
i++;
}
return root;
}
方法二:DFS(先序遍历)
public String serialize(TreeNode root){
return dfs(root);
}
private String dfs(TreeNode root){
if(root==null) return "null";
return root.val+","+dfs(root.left)+","+dfs(root.right);
}
public TreeNode deserialize(String data){
if(data==null) return null;
String[] array=data.split(",");
return dfs2(array);
}
private TreeNode dfs2(String[] array){
if(array[index].equals("null")||index>=array.length) return null;
TreeNode node =new TreeNode(Integer.parseInt(array[index]));
index++;
node.left=dfs2(array);
index++;
node.right=dfs2(array);
return node;
}
leetcode 337 打家劫舍三
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
// 我们可以用 f(o) 表示选择 o节点的情况下,以o节点为根节点的树上被选择的节点的最大权值和;g(o) 表示不选择 o 节点的情况下,以o节点为根节点的树上被选择的节点的最大权值和;l 和 r 代表 o的左右孩子。
// 当 o 被选中时,o 的左右孩子都不能被选中,故 o 被选中情况下子树上被选中点的最大权值和为 l 和 r 不被选中的最大权值和相加,即 f(o) = g(l)+g(r)。
// 当 o 不被选中时,o的左右孩子可以被选中,也可以不被选中。对于 o 的某个具体的孩子 x,它对 o 的贡献是 x 被选中和不被选中情况下权值和的较大值。故 g(o) = max { f(l) , g(l)}+max{ f(r) , g(r) }
// 至此,我们可以用哈希表来存 f 和 g 的函数值,用深度优先搜索的办法后序遍历这棵二叉树,我们就可以得到每一个节点的 f 和 g。根节点的 f 和 g 的最大值就是我们要找的答案。
class Solution {
public int rob(TreeNode root) {
/*
你是我见过最聪明的小偷
*/
/*
//递归思想(不要深入递归函数体,只需知道递归函数的功能,以及找到跳出递归的边界条件)
//思路:
//能盗取的最高金额为 抢劫该节点+抢劫该节点的左孩子的左右子树+抢劫该节点的右孩子的左右子树
//与 抢劫该节点的左子树+抢劫该节点的右子树的和 的最大值
//执行用时 1005ms 原因是出现了很多重复的计算,可使用动态规划解决
if(root == null) return 0;
int val = 0;
if(root.left != null) val += rob(root.left.left) + rob(root.left.right);
if(root.right != null) val += rob(root.right.left) + rob(root.right.right);
return Math.max(rob(root.left) + rob(root.right),val + root.val);
*/
//动态规划
//思路:
//定义一个数组res,长度为2,res[0]表示不抢该节点可获得最大值,res[1]表示抢劫该节点可获得最大值
//方法helper(r)意为:在以r为根节点的树中,返回抢劫根节点与不抢劫根节点可获得的最大值
//执行用时 2ms
int[] res = helper(root);
return Math.max(res[0],res[1]);
}
public int[] helper(TreeNode r){
if(r == null) return new int[2];//边界条件,r为null时,跳出
int[] left = helper(r.left);//对于以r.left为根的树,计算抢劫根节点(r.left)与不抢劫根节点可获得最大金额. left[0]则为不抢r.lrft可获得的最大金额,left[1]则为抢劫r.left可获得的最大金额 以下right[] 分析同理
int[] right = helper(r.right);
int[] res = new int[2];
res[0] = Math.max(left[0],left[1]) + Math.max(right[0],right[1]);//计算不抢劫当前根节点可获得的最大金额(那么其左右子树可以随便抢)
res[1] = r.val + left[0] + right[0];//计算若抢劫根节点可获得的最大金额(此时,其左右子树的根节点不能被抢)
return res;
}
}
leetcode 437 路径总和三
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
示例:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \
3 2 11
/ \
3 -2 1
返回 3。和等于 8 的路径有:
- 5 -> 3
- 5 -> 2 -> 1
- -3 -> 11
方法一
先序遍历+dfs
先序遍历每一个结点。
对每一个节点dfs,计算以它为根节点的的路径总和,并判断是否和目标值相等。
class Solution {
int num=0;
public int pathSum(TreeNode root, int sum) {
if(root==null) return 0;
dfs(root,sum);
pathSum(root.left,sum);
pathSum(root.right,sum);
return num;
}
private void dfs(TreeNode root,int sum){
if(root==null) return ;
//访问当前节点
sum-=root.val;
if(sum==0) num++;
dfs(root.left,sum);
dfs(root.right,sum);
}
}
方法二
使用前缀法;
节点10的前缀为10 (10)
节点5的前缀为10+5=15 (10——>5)
节点3的前缀是10+5+3=18;(10——>5——>3)
目标路径为8
路径18-10=8 所以 5——>3 为所求的路径之一。
题解:先序遍历求每个节点的前缀和。 res保存结果
使用map保存<前缀和key,前缀和为key的路径的数量>
对于每一个节点 ,判断前缀和为cuurentPath-target的路径数目。res=res+路径数目。
先序回溯。 遍历每一个节点。
class Solution{
Map<Integer,Integer> map=new HashMap<>();
int res=0;
public int pathSum(TreeNode root,int sum){
map.put(0,1);
dfs(root,0,sum);
return res;
}
private void dfs(TreeNode root ,int prefix_sum,int target){
if(root==null) return ;
prefix_sum+=root.val;
res+=map.getOrDefault(prefix_sum-target,0);
//更新数目
map.put(prefix_sum,map.getOrDefault(prefix_sum,0)+1);
dfs(root.left,prefix_sum,target);
dfs(root.right,prefix_sum,target);
//回溯
map.put(prefix_sum,map.getOrDefault(prefix_sum,0)-1);
}
}
leetcode 538 把二叉搜索树转换成累加树
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
提醒一下,二叉搜索树满足下列约束条件:
节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。
class Solution {
int pre=0;//前一个节点的值
public TreeNode convertBST(TreeNode root) {
dfs(root);
return root;
}
//按照右,中,左的顺序遍历节点。记录前一个节点的值。
void dfs(TreeNode root){
if(root==null) return;
//先遍历
dfs(root.right);
//处理当前节点。
root.val=root.val+pre;
pre=root.val;
dfs(root.left);
}
}
leetcode 543 树的直径
(此题与124题相似,细节处理是重点)
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例 :
给定二叉树
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
class Solution {
int maxPath=Integer.MIN_VALUE;
public int diameterOfBinaryTree(TreeNode root) {
if(root==null) return 0;
if(root.left==null&&root.right==null) return 0;
dfs(root);
return maxPath;
}
//返回当前节点到子节点路径的最大值
private int dfs(TreeNode root){
if(root.left==null&&root.right==null) return 0;
int left=root.left==null?0:dfs(root.left)+1;
int right=root.right==null?0:dfs(root.right)+1;
maxPath=Math.max(maxPath,left+right);
return Math.max(left,right);
}
}
leetcode 617 合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {
return root1;
}
TreeNode merged = new TreeNode(root1.val + root2.val);
merged.left = mergeTrees(root1.left, root2.left);
merged.right = mergeTrees(root1.right, root2.right);
return merged;
}
}