二叉树的总结
目录
二叉树的遍历
递归遍历
前序
中序
后序
迭代遍历
前序
中序
后序
统一迭代遍历
层序遍历
求二叉树的属性
判断二叉树是否对称:101. 对称二叉树 - 力扣(LeetCode)
求二叉树最大深度:104. 二叉树的最大深度 - 力扣(LeetCode)
求二叉树最小深度:111. 二叉树的最小深度 - 力扣(LeetCode)
求有多少个节点:222. 完全二叉树的节点个数 - 力扣(LeetCode)
判断二叉树是否平衡:110. 平衡二叉树 - 力扣(LeetCode)
找二叉树的所有路径:257. 二叉树的所有路径 - 力扣(LeetCode)
求二叉树路径总和:112. 路径总和 - 力扣(LeetCode)
求二叉树所有左叶子的和:404. 左叶子之和 - 力扣(LeetCode)
求二叉树左下角的值:513. 找树左下角的值 - 力扣(LeetCode)
二叉树的修改和构造
翻转二叉树:226. 翻转二叉树 - 力扣(LeetCode)
构造二叉树
从中序与后序遍历序列构造二叉树:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
从前序和中序遍历构造二叉树:105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
构造最大二叉树:654. 最大二叉树 - 力扣(LeetCode)
合并二叉树:617. 合并二叉树 - 力扣(LeetCode)
求二叉搜索树的属性
二叉搜索树中的搜索:700. 二叉搜索树中的搜索 - 力扣(LeetCode)
验证二叉搜索树:98. 验证二叉搜索树 - 力扣(LeetCode)
求二叉搜索树的最小绝对差:530. 二叉搜索树的最小绝对差 - 力扣(LeetCode)
求二叉搜索树的众数:501. 二叉搜索树中的众数 - 力扣(LeetCode)
把二叉搜索树转成累加树:538. 把二叉搜索树转换为累加树 - 力扣(LeetCode)
二叉树的公共祖先问题
二叉树的公共祖先:236. 二叉树的最近公共祖先 - 力扣(LeetCode)
二叉搜索树的公共祖先:235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)
二叉搜索树的修改与改造
二叉搜索树中的插入操作:701. 二叉搜索树中的插入操作 - 力扣(LeetCode)
二叉搜索树中的删除操作:450. 删除二叉搜索树中的节点 - 力扣(LeetCode)
修剪二叉搜索树:669. 修剪二叉搜索树 - 力扣(LeetCode)
构造二叉搜索树:108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
二叉树的遍历
递归遍历
前序
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> list=new ArrayList<>();if(root==null)return list;list.add(root.val);list.addAll(preorderTraversal(root.left));list.addAll(preorderTraversal(root.right));return list;}
}
中序
class Solution {public List<Integer> inorderTraversal(TreeNode root) {//递归法List<Integer> list=new ArrayList<>();if(root==null)return list;list.addAll(inorderTraversal(root.left));list.add(root.val);list.addAll(inorderTraversal(root.right));return list;}
}
后序
class Solution {public List<Integer> postorderTraversal(TreeNode root) {//递归法List<Integer> list=new ArrayList<>();if(root==null)return list;list.addAll(postorderTraversal(root.left));list.addAll(postorderTraversal(root.right));list.add(root.val);return list;}
}
迭代遍历
前序
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();if (root == null)return list;Stack<TreeNode> st = new Stack<>();st.push(root);while (!st.isEmpty()) {TreeNode node = st.pop();list.add(node.val);if (node.right != null) {st.push(node.right);}if (node.left != null) {st.push(node.left);}}return list;}
}
中序
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> list=new ArrayList<>();if(root==null)return list;Stack<TreeNode> st=new Stack<>();TreeNode cur=root;while(cur!=null||!st.isEmpty()){if(cur!=null){st.push(cur);cur=cur.left;}else{cur=st.pop();list.add(cur.val);cur=cur.right;}}return list;}
}
后序
class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();if (root == null)return list;Stack<TreeNode> st = new Stack<>();st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();list.add(node.val);if(node.left!=null){st.push(node.left);}if(node.right!=null){st.push(node.right);}}Collections.reverse(list);return list;}
}
统一迭代遍历
上述前中后序的迭代遍历写法不一,这里给出统一的写法:
当栈不为空时,首先找到队列的头节点,如果该节点不为空,将该节点弹出(避免重复操作),再将右左中节点依次(中序就是右中左,后序就是中右左)添加到栈中,因为中节点被访问过所以在其后加入空节点作为标记,只有之后遇到这个空节点时,才能将下一个节点放进结果集。
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> list=new ArrayList<>();if(root==null)return list;Stack<TreeNode> st=new Stack<>();st.push(root);while(!st.isEmpty()){TreeNode node=st.peek();if(node!=null){st.pop();if(node.right!=null)st.push(node.right);if(node.left!=null)st.push(node.left);st.push(node);st.push(null);}else{st.pop();node=st.pop();list.add(node.val);}}return list;}
}
前中后序在统一迭代写法的区别就在这几行:
if(node.right!=null)st.push(node.right);
if(node.left!=null)st.push(node.left);
st.push(node);
st.push(null);
前序遍历的写法就是上述这样,按右左中的顺序依次放入栈,中序遍历的写法就是按右中左的顺序,后序遍历就是按中右左的顺序。
层序遍历
不再是深度优先遍历,而是广度优先。
class Solution {public List<List<Integer>> reslist=new ArrayList<List<Integer>>();public List<List<Integer>> levelOrder(TreeNode root) {check(root);return reslist;}public void check(TreeNode node){if(node==null)return;Queue<TreeNode> q=new LinkedList<>();q.offer(node);while(!q.isEmpty()){List<Integer> list=new ArrayList<>();int len=q.size();while(len>0){TreeNode tmp=q.poll();list.add(tmp.val);if(tmp.left!=null)q.offer(tmp.left);if(tmp.right!=null)q.offer(tmp.right);len--;}reslist.add(list);}}
}
求二叉树的属性
判断二叉树是否对称:101. 对称二叉树 - 力扣(LeetCode)
递归:比较根节点的左子树和右子树是不是相互翻转的
class Solution {public boolean isSymmetric(TreeNode root) {return compare(root.left,root.right);}public boolean compare(TreeNode left,TreeNode right){if(left==null&&right==null)return true;else if(left==null&&right!=null)return false;else if(left!=null&&right==null)return false;else if(left.val!=right.val)return false;boolean compareOutside=compare(left.left,right.right);boolean compareInside=compare(left.right,right.left);return compareOutside&compareInside;}}
迭代: 使用队列或栈将两个节点顺序放入容器中进行比较
class Solution {public boolean isSymmetric(TreeNode root){return check(root);}public boolean check(TreeNode node){Queue<TreeNode> q=new LinkedList<>();q.offer(node.left);q.offer(node.right);while(!q.isEmpty()){TreeNode left=q.poll();TreeNode right=q.poll();if(left==null&&right==null)continue;else if(left==null||right==null||left.val!=right.val)return false;q.offer(left.left);q.offer(right.right);q.offer(left.right);q.offer(right.left);}return true;}
}
求二叉树最大深度:104. 二叉树的最大深度 - 力扣(LeetCode)
递归:求根节点的最大高度就是最大深度
class Solution {public int maxDepth(TreeNode root) {if(root==null)return 0;int leftDepth=maxDepth(root.left);int rightDepth=maxDepth(root.right);return Math.max(leftDepth,rightDepth)+1;}
}
迭代:层序遍历
class Solution {public int maxDepth(TreeNode root) {int depth=0;if(root==null)return 0;Queue<TreeNode> q=new LinkedList<>();q.offer(root);while(!q.isEmpty()){int len=q.size();while(len>0){TreeNode tmp=q.poll();if(tmp.left!=null)q.offer(tmp.left);if(tmp.right!=null)q.offer(tmp.right);len--;}depth++;}return depth;}
}
求二叉树最小深度:111. 二叉树的最小深度 - 力扣(LeetCode)
递归:求根节点的最小高度就是最小深度
class Solution {public int minDepth(TreeNode root) {if(root==null)return 0;int leftDepth=minDepth(root.left);int rightDepth=minDepth(root.right);if(leftDepth==0)return rightDepth+1;if(rightDepth==0)return leftDepth+1;return Math.min(leftDepth,rightDepth)+1;}
}
迭代:层序遍历
class Solution {public int minDepth(TreeNode root) {int depth=0;if(root==null)return 0;Queue<TreeNode> q=new LinkedList<>();q.offer(root);while(!q.isEmpty()){int len=q.size();while(len>0){TreeNode tmp=q.poll();if(tmp.left!=null)q.offer(tmp.left);if(tmp.right!=null)q.offer(tmp.right);if(tmp.left==null&&tmp.right==null)return depth+1;len--;}depth++;}return depth;}
}
求有多少个节点:222. 完全二叉树的节点个数 - 力扣(LeetCode)
递归:
class Solution {public int countNodes(TreeNode root) {if(root==null)return 0;int left=countNodes(root.left);int right=countNodes(root.right);int count=0;if(root!=null)count++;return left+right+count;}
}
迭代:
class Solution {public int countNodes(TreeNode root) {if(root==null)return 0;Queue<TreeNode> q=new LinkedList<>();List<Integer> list=new ArrayList<>();if(root!=null)q.offer(root);while(!q.isEmpty()){int len=q.size();while(len>0){TreeNode tmp=q.poll();if(tmp.left!=null)q.offer(tmp.left);if(tmp.right!=null)q.offer(tmp.right);list.add(tmp.val);len--;}}return list.size();}
}
判断二叉树是否平衡:110. 平衡二叉树 - 力扣(LeetCode)
递归:递归过程判断高度差
class Solution {public boolean isBalanced(TreeNode root) {if(root==null)return true;if(Height(root)==-1)return false;return true;}public int Height(TreeNode node){if(node==null)return 0;int leftHeight=Height(node.left);if(leftHeight==-1)return -1;int rightHeight=Height(node.right);if(rightHeight==-1)return -1;if(Math.abs(leftHeight-rightHeight)>1)return -1;return Math.max(leftHeight,rightHeight)+1;}}
}
迭代:效率很低,不推荐
class Solution {public boolean isBalanced(TreeNode root){if(root==null)return true;Stack<TreeNode> st=new Stack<>();if(root!=null)st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();if(node!=null){st.push(node);st.push(null);if(node.right!=null)st.push(node.right);if(node.left!=null)st.push(node.left);}else{node=st.pop();if(Math.abs(Height(node.left)-Height(node.right))>1)return false;}}return true;}public int Height(TreeNode node){if(node==null)return 0;Queue<TreeNode> q=new LinkedList<>();q.offer(node);int res=0;while(!q.isEmpty()){int len=q.size();while(len>0){TreeNode tmp=q.poll();if(tmp.left!=null)q.offer(tmp.left);if(tmp.right!=null)q.offer(tmp.right);len--;}res++;}return res;}
}
找二叉树的所有路径:257. 二叉树的所有路径 - 力扣(LeetCode)
递归:涉及回溯处理根节点到叶子的所有路径
class Solution {public List<String> binaryTreePaths(TreeNode root){List<String> list=new ArrayList<>();if(root==null)return null;List<Integer> path=new ArrayList<>();findPaths(root,path,list);return list;}public void findPaths(TreeNode node,List<Integer> path,List<String> res){path.add(node.val);if(node.left==null&&node.right==null){//叶子节点StringBuffer sb=new StringBuffer();for(int i=0;i<path.size()-1;i++){sb.append(path.get(i)+"->");}sb.append(path.get(path.size()-1));res.add(sb.toString());return;}if(node.left!=null){findPaths(node.left,path,res);path.remove(path.size()-1);//回溯}if(node.right!=null){findPaths(node.right,path,res);path.remove(path.size()-1);//回溯}}
}
迭代法:把节点和路径同时放入栈中
class Solution {public List<String> binaryTreePaths(TreeNode root) {List<String> list=new ArrayList<>();Stack<Object> st=new Stack<>();if(root!=null){//节点和路径同时入栈st.push(root);st.push(root.val+"");}while(!st.isEmpty()){//节点和路径同时出栈String path=(String)st.pop();TreeNode node=(TreeNode)st.pop();//找到叶子节点if(node.left==null&&node.right==null){list.add(path);}//右子节点不为空if(node.right!=null){st.push(node.right);st.push(path+"->"+node.right.val);}//左子节点不为空if(node.left!=null){st.push(node.left);st.push(path+"->"+node.left.val);}}return list;}
}
求二叉树路径总和:112. 路径总和 - 力扣(LeetCode)
递归:和上述找二叉树的所有路径思路一样,只需稍作修改即可
class Solution {public boolean hasPathSum(TreeNode root, int targetSum) {if(root==null)return false;return findPaths(root,0,targetSum);}public boolean findPaths(TreeNode node,int pathSum,int targetSum){pathSum+=node.val;if(node.left==null&&node.right==null&&pathSum==targetSum)return true;boolean left=false;boolean right=false;if(node.left!=null){left=findPaths(node.left,pathSum,targetSum);}if(node.right!=null){right=findPaths(node.right,pathSum,targetSum);}return left||right;}
}
迭代:
class Solution {public boolean hasPathSum(TreeNode root, int targetSum) {if(root==null)return false;Stack<Object> st=new Stack<>();st.push(root);st.push(root.val);while(!st.isEmpty()){int pathSum=(Integer)st.pop();System.out.println(pathSum);TreeNode tmp=(TreeNode)st.pop();if(tmp.left!=null){st.push(tmp.left);st.push(pathSum+tmp.left.val);}if(tmp.right!=null){st.push(tmp.right);st.push(pathSum+tmp.right.val);}if(tmp.left==null&&tmp.right==null&&pathSum==targetSum)return true;}return false;}
}
求二叉树所有左叶子的和:404. 左叶子之和 - 力扣(LeetCode)
递归:后序,必须有三个约束条件才能判断是否是左叶子
class Solution {public int sumOfLeftLeaves(TreeNode root) {if(root==null)return 0;int left=sumOfLeftLeaves(root.left);int right=sumOfLeftLeaves(root.right);int mid=0;if(root.left!=null&&root.left.left==null&&root.left.right==null){mid=root.left.val;}return mid+left+right;}
}
迭代:层序遍历法
class Solution {public int sumOfLeftLeaves(TreeNode root) {if(root==null)return 0;Queue<TreeNode> q=new LinkedList<>();int res=0;if(root!=null)q.offer(root);while(!q.isEmpty()){int len=q.size();while(len>0){TreeNode tmp=q.poll();if(tmp.left!=null&&tmp.left.left==null&&tmp.left.right==null)res+=tmp.left.val;if(tmp.left!=null)q.offer(tmp.left);if(tmp.right!=null)q.offer(tmp.right);len--;}}return res;}
}
求二叉树左下角的值:513. 找树左下角的值 - 力扣(LeetCode)
递归:优先左孩子搜索,同时找深度最大的叶子节点
class Solution {int maxDepth = 0;int res = 0;public int findBottomLeftValue(TreeNode root) {//递归traversal(root,1);return res;}public void traversal(TreeNode node, int depth) {if (node.left == null && node.right == null) {if (depth > maxDepth) {maxDepth = depth;res=node.val;}return;}if(node.left!=null){depth++;traversal(node.left,depth);depth--;}if(node.right!=null){depth++;traversal(node.right,depth);depth--;}}
}
迭代:层序遍历找最后一行左边
class Solution {public int findBottomLeftValue(TreeNode root) {Deque<TreeNode> q=new LinkedList<>();List<List<Integer>> res=new ArrayList<List<Integer>>();q.offer(root);while(!q.isEmpty()){List<Integer> list=new ArrayList<>();int len=q.size();while(len>0){TreeNode tmp=q.poll();if(tmp.left!=null)q.offer(tmp.left);if(tmp.right!=null)q.offer(tmp.right);list.add(tmp.val);len--;}res.add(list);}return res.get(res.size()-1).get(0);}
}
二叉树的修改和构造
翻转二叉树:226. 翻转二叉树 - 力扣(LeetCode)
递归:前序,交换左右孩子
class Solution {public TreeNode invertTree(TreeNode root) {if(root==null)return null;swapChildren(root);invertTree(root.left);invertTree(root.right);return root;}public void swapChildren(TreeNode node){TreeNode tmp=node.right;node.right=node.left;node.left=tmp;}}
迭代:层序遍历
class Solution {public TreeNode invertTree(TreeNode root){check(root);return root;}public void check(TreeNode node){if(node==null)return;Queue<TreeNode> q=new LinkedList<>();q.offer(node);while(!q.isEmpty()){int len=q.size();while(len>0){TreeNode tmp=q.poll();swapChildren(tmp);if(tmp.left!=null)q.offer(tmp.left);if(tmp.right!=null)q.offer(tmp.right);len--;}}}public void swapChildren(TreeNode node){TreeNode tmp=node.right;node.right=node.left;node.left=tmp;}}
构造二叉树
重点在于找分割点,分左右区间构造。
从中序与后序遍历序列构造二叉树:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
class Solution {public TreeNode buildTree(int[] inorder, int[] postorder) {if(inorder.length==0||postorder.length==0)return null;return build(inorder,0,inorder.length,postorder,0,postorder.length);}//左闭右开public TreeNode build(int[] inorder,int instart,int inend,int[] postorder,int poststart,int postend){if(poststart==postend)return null;int rootval=postorder[postend-1];TreeNode root=new TreeNode(rootval);int index=0;for(index=instart;index<inend;index++){if(inorder[index]==rootval)break;}int leftinstart=instart;int leftinend=index;int rightinstart=index+1;int rightinend=inend;int leftpoststart=poststart;int leftpostend=poststart+index-instart;int rightpoststart=poststart+index-instart;int rightpostend=postend-1;root.left=build(inorder,leftinstart,leftinend,postorder,leftpoststart,leftpostend);root.right=build(inorder,rightinstart,rightinend,postorder,rightpoststart,rightpostend);return root;}
}
从前序和中序遍历构造二叉树:105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
class Solution {public TreeNode buildTree(int[] preorder, int[] inorder) {if(preorder.length==0||inorder.length==0)return null;return build(preorder,0,preorder.length,inorder,0,inorder.length);}//左闭右开public TreeNode build(int[] preorder,int prestart,int prend,int[] inorder,int instart,int inend){if(prestart==prend)return null;int rootval=preorder[prestart];TreeNode root=new TreeNode(rootval);int index=0;for(index=instart;index<inend;index++){if(inorder[index]==rootval)break;}int leftinstart=instart;int leftinend=index;int rightinstart=index+1;int rightinend=inend;int leftprestart=prestart+1;int leftprend=prestart+index-instart+1;int rightprestart=leftprend;int rightprend=prend;root.left=build(preorder,leftprestart,leftprend,inorder,leftinstart,leftinend);root.right=build(preorder,rightprestart,rightprend,inorder,rightinstart,rightinend);return root;}
}
构造最大二叉树:654. 最大二叉树 - 力扣(LeetCode)
依旧是找分割点,分割左右区间。
class Solution {public TreeNode constructMaximumBinaryTree(int[] nums) {if(nums.length==0)return null;return build(nums,0,nums.length);}public int findMaxIndex(int[] arr,int start,int end){int Max=arr[start];int index=start;for(int i=start+1;i<end;i++){if(arr[i]>Max){Max=arr[i];index=i;}}//System.out.println(Max);return index;}//左闭右开public TreeNode build(int[] arr,int start,int end){if(start==end)return null;int leftstart,leftend,rightstart,rightend;int index=findMaxIndex(arr,start,end);TreeNode root=new TreeNode(arr[index]);leftstart=start;leftend=index;rightstart=index+1;rightend=end;TreeNode left=build(arr,leftstart,leftend);TreeNode right=build(arr,rightstart,rightend);root.left=left;root.right=right;return root;}
}
合并二叉树:617. 合并二叉树 - 力扣(LeetCode)
递归:前序,同时操作两个树的节点,注意合并的规则
class Solution {public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {if(root1==null&&root2==null)return null;if(root1==null)return root2;if(root2==null)return root1;int val=(root1==null?0:root1.val)+(root2==null?0:root2.val);TreeNode root=new TreeNode(val);root.left=mergeTrees(root1.left,root2.left);root.right=mergeTrees(root1.right,root2.right);return root; }
}
迭代:使用队列,类似层序遍历
class Solution {public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {if(root1==null&&root2==null)return null;if(root1==null)return root2;if(root2==null)return root1;Deque<TreeNode> q=new LinkedList<>();q.push(root2);q.push(root1);while(!q.isEmpty()){TreeNode tmp1=q.pop();TreeNode tmp2=q.pop();tmp1.val+=tmp2.val;if(tmp1.left!=null&&tmp2.left!=null){q.push(tmp2.left);q.push(tmp1.left);}if(tmp1.right!=null&&tmp2.right!=null){q.push(tmp2.right);q.push(tmp1.right);}if(tmp1.left==null&&tmp2.left!=null)tmp1.left=tmp2.left;if(tmp1.right==null&&tmp2.right!=null)tmp1.right=tmp2.right; }return root1;}
}
求二叉搜索树的属性
二叉搜索树中的搜索:700. 二叉搜索树中的搜索 - 力扣(LeetCode)
递归:
class Solution {public TreeNode searchBST(TreeNode root, int val) {if(root==null)return null;if(root.val==val)return root;if(root.val>val)return searchBST(root.left,val);return searchBST(root.right,val);}
}
迭代:
class Solution {public TreeNode searchBST(TreeNode root, int val) {if(root==null||root.val==val)return root;Stack<TreeNode> st=new Stack<>();st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();if(node.val>val){if(node.left!=null)st.push(node.left);else return null;}else if(node.val<val){if(node.right!=null)st.push(node.right);else return null;}else return node;}return null;}
}
验证二叉搜索树:98. 验证二叉搜索树 - 力扣(LeetCode)
递归:中序,相当于判断一个序列是不是递增的
class Solution {public boolean isValidBST(TreeNode root) {List<Integer> list=inorder(root);for(int i=0;i<list.size()-1;i++){if(list.get(i)>=list.get(i+1))return false;}return true;}public List<Integer> inorder(TreeNode node){List<Integer> list=new ArrayList<>();if(node==null)return list;list.addAll(inorder(node.left));list.add(node.val);list.addAll(inorder(node.right));return list;}}
}
迭代:模拟中序,逻辑相同
class Solution {public boolean isValidBST(TreeNode root){Stack<TreeNode> st=new Stack<>();List<Integer> list=new ArrayList<>();if(root!=null)st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();if(node!=null){if(node.right!=null)st.push(node.right);st.push(node);st.push(null);if(node.left!=null)st.push(node.left);}else{node=st.pop();list.add(node.val);}}for(int i=0;i<list.size()-1;i++){if(list.get(i)>=list.get(i+1))return false;}return true;}
}
求二叉搜索树的最小绝对差:530. 二叉搜索树的最小绝对差 - 力扣(LeetCode)
递归:中序,引入一个pre节点,保留前序节点
class Solution {int res=Integer.MAX_VALUE;TreeNode pre=null;public int getMinimumDifference(TreeNode root) {if(root==null)return 0;getMinimumDifference(root.left);if(pre!=null)res=Math.min(res,root.val-pre.val);pre=root;getMinimumDifference(root.right);return res;}
}
迭代:模拟中序,逻辑相同
class Solution {public int getMinimumDifference(TreeNode root) {int res=Integer.MAX_VALUE;TreeNode pre=null;Stack<TreeNode> st=new Stack<>();st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();if(node!=null){if(node.right!=null)st.push(node.right);st.push(node);st.push(null);if(node.left!=null)st.push(node.left);}else{TreeNode tmp=st.pop();if(pre!=null)res=Math.min(res,tmp.val-pre.val);pre=tmp;}}return res;}
}
求二叉搜索树的众数:501. 二叉搜索树中的众数 - 力扣(LeetCode)
和上一题思路一样,都需要一个pre节点来保留前序节点
递归:中序,清空结果集的技巧,遍历一遍即可求众数集合
class Solution {List<Integer> list=new ArrayList<>();int count=0;int maxCount=0;TreeNode pre=null;public int[] findMode(TreeNode root) {if(root==null)return null;findMode(root.left);if(pre==null)count=1;else if(pre!=null&&pre.val==root.val)count++;else count=1;pre=root;if(count==maxCount)list.add(root.val);else if(count>maxCount){maxCount=count;list.clear();list.add(root.val);}findMode(root.right);int[] res=new int[list.size()];for(int i=0;i<list.size();i++){res[i]=list.get(i);}return res;}
}
迭代:
class Solution {public int[] findMode(TreeNode root) {int count=0;int maxCount=0;Stack<TreeNode> st=new Stack<>();List<Integer> list=new ArrayList<>();TreeNode pre=null;if(root!=null)st.push(root);while(!st.isEmpty()){TreeNode node=st.pop();if(node!=null){if(node.right!=null)st.push(node.right);st.push(node);st.push(null);if(node.left!=null)st.push(node.left);}else{TreeNode tmp=st.pop();if(pre==null)count=1;else if(pre!=null&&tmp.val==pre.val)count++;else count=1;pre=tmp;if(count==maxCount)list.add(tmp.val);else if(count>maxCount){maxCount=count;list.clear();list.add(tmp.val);}}}int[] res=new int[list.size()];for(int i=0;i<list.size();i++){res[i]=list.get(i);}return res;}
}
把二叉搜索树转成累加树:538. 把二叉搜索树转换为累加树 - 力扣(LeetCode)
递归:中序,依旧使用pre节点保留前序节点
class Solution {public TreeNode convertBST(TreeNode root) {if(root==null)return null;return inorder(root);}TreeNode pre=null;public TreeNode inorder(TreeNode node){if(node==null)return null;inorder(node.right);if(pre!=null)node.val+=pre.val;pre=node;inorder(node.left);return node;}
}
递归:模拟中序,逻辑相同
class Solution {public TreeNode convertBST(TreeNode root) {if(root==null)return null;Stack<TreeNode> st=new Stack<>();TreeNode pre=null;st.push(root);while(!st.isEmpty()){TreeNode tmp=st.pop();if(tmp!=null){if(tmp.left!=null)st.push(tmp.left);st.push(tmp);st.push(null);if(tmp.right!=null)st.push(tmp.right);}else{tmp=st.pop();if(pre!=null)tmp.val+=pre.val;pre=tmp;}}return root;}
}
二叉树的公共祖先问题
二叉树的公共祖先:236. 二叉树的最近公共祖先 - 力扣(LeetCode)
递归:后序,回溯,找到左子树出现目标值,右子树出现目标值的节点
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root==null||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;}
}
迭代不适合模拟回溯。
二叉搜索树的公共祖先:235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)
因为二叉搜索树的特性,所以比二叉树求最近公共祖先要简单一点。
递归:如果节点的数值在目标区间内就是最近公共祖先
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if((p.val<=root.val&&q.val>=root.val)||(p.val>=root.val&&q.val<=root.val))return root;else if(p.val<root.val&&q.val<root.val)return lowestCommonAncestor(root.left,p,q);else if(p.val>root.val&&q.val>root.val)return lowestCommonAncestor(root.right,p,q);return null;}
}
迭代:
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {while(root!=null){if((p.val<=root.val&&q.val>=root.val)||(p.val>=root.val&&q.val<=root.val))return root;else if(p.val<root.val&&q.val<root.val)root=root.left;else if(p.val>root.val&&q.val>root.val)root=root.right;}return null;}
}
二叉搜索树的修改与改造
二叉搜索树中的插入操作:701. 二叉搜索树中的插入操作 - 力扣(LeetCode)
递归:
class Solution {public TreeNode insertIntoBST(TreeNode root, int val) {TreeNode node=new TreeNode(val);if(root==null){root=node;return root;}search(root,node);return root;}public void search(TreeNode root,TreeNode node){if(root.left==null&&node.val<root.val)root.left=node;if(root.right==null&&node.val>root.val)root.right=node;if(node.val>root.val)search(root.right,node);else if(node.val<root.val)search(root.left,node);}
}
迭代:这里又用到了pre节点记录插入父节点
class Solution {public TreeNode insertIntoBST(TreeNode root, int val) {TreeNode node=new TreeNode(val);if(root==null)return node;TreeNode pre=null;TreeNode Newroot=root;while(Newroot!=null){pre=Newroot;if(Newroot.val>val)Newroot=Newroot.left;else if(Newroot.val<val)Newroot=Newroot.right;}if(pre.val>val)pre.left=node;else if(pre.val<val)pre.right=node;return root;}
}
二叉搜索树中的删除操作:450. 删除二叉搜索树中的节点 - 力扣(LeetCode)
递归:前序,想清楚删除非叶子节点的情况
class Solution {public TreeNode deleteNode(TreeNode root, int key) {if(root==null)return null;if(root.val==key){if(root.left==null)return root.right;else if(root.right==null)return root.left;else{TreeNode tmp=root.right;while(tmp.left!=null){tmp=tmp.left;}tmp.left=root.left;root=root.right;return root;}}else if(root.val<key)root.right=deleteNode(root.right,key);else root.left=deleteNode(root.left,key);return root;}
}
迭代:较复杂
class Solution {public TreeNode deleteNode(TreeNode root, int key) {if (root == null)return null;TreeNode tmp = root;TreeNode pre = null;while (tmp != null) {if (tmp.val > key) {pre = tmp;tmp = tmp.left;} else if (tmp.val < key) {pre = tmp;tmp = tmp.right;} elsebreak;}if (pre == null)return delete(tmp);if (pre.left != null && pre.left.val == key) {pre.left = delete(tmp);}if (pre.right != null && pre.right.val == key) {pre.right = delete(tmp);}return root;}public TreeNode delete(TreeNode node){if(node==null)return null;if(node.right==null)return node.left;TreeNode cur=node.right;while(cur.left!=null){cur=cur.left;}cur.left=node.left;node=node.right;return node;}
}
修剪二叉搜索树:669. 修剪二叉搜索树 - 力扣(LeetCode)
递归:
class Solution {public TreeNode trimBST(TreeNode root,int low,int high){if(root==null)return null;//如果根节点的值小于low,说明根节点左子树都不在范围内,返回修剪好的右子树即可if(root.val<low)return trimBST(root.right,low,high);//如果根节点的值大于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;}
}
迭代:
class Solution {public TreeNode trimBST(TreeNode root,int low,int high){if(root==null)return null;while(root!=null&&(root.val<low||root.val>high)){if(root.val<low)root=root.right;if(root.val>high)root=root.left;}TreeNode curr=root;while(curr!=null){while(curr.left!=null&&curr.left.val<low){curr.left=curr.left.right;}curr=curr.left;}curr=root;while(curr!=null){while(curr.right!=null&&curr.right.val>high){curr.right=curr.right.left;}curr=curr.right;}return root;}
}
构造二叉搜索树:108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
分割点就是数组中间位置,这个思路和构造二叉树、求最大二叉树一样。
class Solution {public TreeNode sortedArrayToBST(int[] nums) {return build(nums,0,nums.length);}public TreeNode build(int[] arr,int start,int end){if(start==end)return null;int n=end-start;int index=start+n/2;TreeNode root=new TreeNode(arr[index]);root.left=build(arr,start,index);root.right=build(arr,index+1,end);return root;}
}