前言
一、递归
本部分题型大部分使用递归思想解决!!
1、树的高度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
2、平衡树
一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
class Solution {
public boolean isBalanced(TreeNode root) {
if(root==null) return true;
boolean isbalan=Math.abs(depth(root.left)-depth(root.right))<=1 ? true : false;
return isBalanced(root.left) && isBalanced(root.right) && isbalan;
}
public int depth(TreeNode p){
if(p==null) return 0;
return Math.max(depth(p.left),depth(p.right))+1;
}
}
3、两节点的最长路径
给定一棵二叉树,求出两节点的最长路径。这条路径可能穿过也可能不穿过根结点。
class Solution {
public int res=0; //注意这种全局变量的作用!!!
public int diameterOfBinaryTree(TreeNode root) {
depth(root);
return res;
}
public int depth(TreeNode node){
if(node==null) return 0;
int left=depth(node.left);
int right=depth(node.right);
res=Math.max(res,left+right);
return Math.max(left,right)+1;
}
}
但是使用局部变量传入时就不太对!!
class Solution {
public int diameterOfBinaryTree(TreeNode root) {
int res=0; //它作为参数传入时是不对的!!!
depth(root,res);
return res;
}
public int depth(TreeNode node,int res){
if(node==null) return 0;
int left=depth(node.left,res);
int right=depth(node.right,res);
res=Math.max(res,left+right);
return Math.max(left,right)+1;
}
}
4、翻转树
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return root;
TreeNode left=root.left;
TreeNode right=root.right;
invertTree(left);
invertTree(right);
root.right=left;
root.left=right;
return root;
}
}
5、归并两棵树
合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1==null && root2==null) return null;
else if(root1==null) return root2;
else if(root2==null) return root1;
else {
TreeNode root=new TreeNode(root1.val+ root2.val); //需要时新建树
root.left=mergeTrees(root1.left,root2.left);
root.right=mergeTrees(root1.right,root2.right);
return root;
}
}
}
6、判断路径和是否等于一个数
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root==null) return false;
targetSum-=root.val;
if(targetSum==0 && root.left==null && root.right==null) return true;
return hasPathSum(root.left,targetSum) || hasPathSum(root.right,targetSum);
}
}
7、计路径和等于一个数的路径数量
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
class Solution {
public int pathSum(TreeNode root, int targetSum) {
if (root == null) {
return 0;
}
int ret = rootSum(root, targetSum);
ret += pathSum(root.left, targetSum);
ret += pathSum(root.right, targetSum);
return ret;
}
public int rootSum(TreeNode root, int targetSum) {
int ret = 0;
if (root == null) {
return 0;
}
int val = root.val;
if (val == targetSum) ret++;
ret += rootSum(root.left, targetSum - val);
ret += rootSum(root.right, targetSum - val);
return ret;
}
}
8、子树
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。
二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root==null && subRoot==null) return true;
if(root==null) return false;
return issub(root,subRoot) || isSubtree(root.left,subRoot) || isSubtree(root.right,subRoot);
}
public boolean issub(TreeNode root1,TreeNode root2){
if(root1==null && root2==null) return true;
else if(root1==null || root2==null) return false;
if(root1.val!=root2.val) return false;
return issub(root1.left,root2.left) && issub(root1.right,root2.right);
}
}
9、对称二叉树
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
return issym(root.left,root.right); //主要是要调用两个树
}
public boolean issym(TreeNode root1,TreeNode root2){
if(root1==null && root2==null) return true;
else if(root1==null || root2==null) return false;
if(root1.val!= root2.val) return false;
return issym(root1.left,root2.right) && issym(root1.right,root2.left);
}
}
10、最小路径
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
//最小深度
class Solution {
public int minDepth(TreeNode root) {
if(root==null) return 0;
int left=minDepth(root.left);
int right=minDepth(root.right);
if(left==0 || right==0) return left+right+1; //注意这个特殊情况哈!!
return Math.min(left,right)+1;
}
}
11、统计左叶子节点的和
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if (root == null) return 0;
if (isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right);
return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
}
private boolean isLeaf(TreeNode node){
if (node == null) return false;
return node.left == null && node.right == null;
}
}
12、相同节点值的最大路径长度
给定一个二叉树的 root ,返回最长的路径的长度 ,这个路径中的每个节点具有相同值 。
这条路径可以经过也可以不经过根节点。
两个节点之间的路径长度,由它们之间的边数表示。
class Solution {
int res=0;
public int longestUnivaluePath(TreeNode root) {
dfs(root);
return res;
}
//这两个方法看起来一样,但是longestUnivaluePath时并没有用到它的返回值,而是一个全局变量res。
public int dfs(TreeNode node){
if(node==null) return 0;
int left=dfs(node.left);
int right=dfs(node.right);
int leftnum= (node.left!=null && node.left.val==node.val) ? left+1 : 0;
int rightnum= (node.right!=null && node.right.val==node.val) ? right+1 : 0;
res=Math.max(res,leftnum+rightnum); //
return Math.max(leftnum,rightnum); //如果用当前节点node,只能是它得left或right中的一个路径
}
}
13、间隔遍历
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
class Solution {
HashMap<TreeNode,Integer> hashMap=new HashMap<>(); //使用hashmap的原因是调用rob函数的时候可以直接获取,不用再计算,减少计算时间。
public int rob(TreeNode root) {
if(root==null) return 0;
if(hashMap.containsKey(root)) return hashMap.get(root);
int num1=root.val;
if(root.left!=null) num1+=rob(root.left.left)+ rob(root.left.right);
if(root.right!=null) num1+=rob(root.right.left)+rob(root.right.right);
int num2=rob(root.left)+rob(root.right);
int res=Math.max(num1,num2);
hashMap.put(root,res);
return res;
}
}
14、找出二叉树中第二小的节点
给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。
更正式地说,即 root.val = min(root.left.val, root.right.val) 总成立。给出这样的一个二叉树,你需要输出所有节点中的 第二小的值 。如果第二小的值不存在的话,输出 -1 。
//TMD有点不好理解
class Solution {
int ans;
int rootvalue;
public int findSecondMinimumValue(TreeNode root) {
ans = -1;
rootvalue = root.val;
dfs(root);
return ans;
}
public void dfs(TreeNode node) {
if (node == null) {
return;
}
if (ans != -1 && node.val >= ans) {
return;
}
if (node.val > rootvalue) {
ans = node.val;
}
dfs(node.left);
dfs(node.right);
}
}
class Solution {
public int findSecondMinimumValue(TreeNode root) {
if (root.left == null) {
return -1;
}
int leftValue = root.left.val;
int rightValue = root.right.val;
// 等于 root.val 则是最小值,递归获取该子节点的第二小值
if (leftValue == root.val) {
leftValue = findSecondMinimumValue(root.left);
}
if (rightValue == root.val) {
rightValue = findSecondMinimumValue(root.right);
}
// 为 -1 说明没有第二小值
if (leftValue == -1) {
return rightValue;
}
if (rightValue == -1) {
return leftValue;
}
// 左右子节点都有第二小值,则取最小的值
return leftValue < rightValue ? leftValue : rightValue;
}
}
二、层序遍历
1、一棵树每层节点的平均数
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> res=new ArrayList<>();
List<TreeNode> path=new LinkedList<>();
if(root==null) return res;
path.add(root);
int size;
while (!path.isEmpty()){
size=path.size();
double num=0,numsize=size;
while (size>0){
TreeNode node=path.remove(0);
num+=node.val;
if(node.left!=null) path.add(node.left);
if(node.right!=null) path.add(node.right);
--size;
}
res.add(num/numsize);
}
return res;
}
}
2、得到左下角的节点
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
//为了拿到最下面那层的最左边那个,层序遍历时从右到左;
class Solution {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue=new LinkedList<>(); //用的是队列奥
queue.add(root);
TreeNode node=null;
while (!queue.isEmpty()){
node=queue.poll();
if(node.right!=null) queue.add(node.right);
if(node.left!=null) queue.add(node.left);
}
return node.val;
}
}
三、前中后序遍历
注意这里尽量不要使用递归做法哈,学习非递归的思想!!!
1、前序遍历
前序遍历的顺序为:中左右
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list=new LinkedList<>();
if(root==null) return list;
Stack<TreeNode> stack=new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node=stack.pop();
list.add(node.val);
if(node.right!=null) stack.push(node.right);
if(node.left!=null) stack.push(node.left);
}
return list;
}
}
2、后序遍历
后序遍历的顺序为:左右中
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> list=new LinkedList<>();
if(root==null) return list;
Stack<TreeNode> stack=new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node=stack.pop();
list.addFirst(node.val); //注意这里是addFirst奥,先序遍历是:中左右;后序遍历是:左右中;他们都可以使用Stack和List来实现。
if(node.left!=null) stack.push(node.left);
if(node.right!=null) stack.push(node.right);
}
return list;
}
}
前序遍历与后续遍历代码思想很相似,主要使用了Stack和LinkedList;
3、中序遍历
中序遍历的顺序为:左中右
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if(root==null) return res;
Stack<TreeNode> stack=new Stack<>();
while (root!=null){
stack.push(root);
root=root.left;
}
while (!stack.isEmpty()){
TreeNode node=stack.pop();
res.add(node.val);
if(node.right!=null){
node=node.right;
while (node!=null){
stack.push(node);
node=node.left;
}
}
}
return res;
}
}
四、BST
1、修剪二叉树
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 。 可以证明,存在 唯一的答案 。
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if(root==null) return root;
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;
}
}
2、寻找二叉查找树的第k个元素
可以使用递归的思想:
class Solution {
public int kthSmallest(TreeNode root, int k) {
int left=count(root.left);
if(left==k-1) return root.val;
else if(left>k-1) return kthSmallest(root.left,k);
else return kthSmallest(root.right,k-left-1);
}
public int count(TreeNode root){
if(root==null) return 0;
int left= root.left!=null ? count(root.left) : 0;
int right= root.right!=null ? count(root.right) : 0;
return left+right+1;
}
}
使用中序遍历,遍历到第k个就输出;
class Solution {
public int kthSmallest(TreeNode root, int k) {
Stack<TreeNode> stack=new Stack<>();
while (root!=null){
stack.push(root);
root=root.left;
}
while (!stack.isEmpty()){
TreeNode node=stack.pop();
if(--k==0) return node.val;
if(node.right!=null){
node=node.right;
while (node!=null){
stack.push(node);
node=node.left;
}
}
}
return -1;
}
}
3、把二叉查找树每个节点的值都加上比它大的节点的值
给定二叉搜索树的根节点,该树的节点值各不相同,请将其转换为累加树,使每个节点node的新值等于原树中大于或等于node.val的值之和。
class Solution {
private int sum = 0; //定义一个全局变量sum
public TreeNode convertBST(TreeNode root) {
traver(root);
return root;
}
private void traver(TreeNode node) {
if (node == null) return;
traver(node.right);
sum += node.val;
node.val = sum;
traver(node.left);
}
}
4、二叉查找树的最近公共祖先
“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
//不可以直接判断if(p.val<root.val && q.val>root.val)就return root是不可以滴哈。先从左右递归如果都没有才是root。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(p.val<root.val && q.val<root.val) return lowestCommonAncestor(root.left,p,q);
if(p.val>root.val && q.val>root.val) return lowestCommonAncestor(root.right,p,q);
return root;
}
}
5、二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
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);
return left==null ? right : (right==null ? left : root);
}
}
6、从有序数组中构造二叉查找树
//其实是不用考虑高度平衡问题的,因为方法中每次找的都是mid位置,左右高度最多相差1,所以不用特地做高度平衡。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return toBST(nums,0,nums.length-1);
}
public TreeNode toBST(int[] nums,int begin,int end){
if(begin>end) return null;
int mid=begin+(end-begin)/2;
TreeNode node=new TreeNode(nums[mid]);
node.left=toBST(nums,begin,mid-1);
node.right=toBST(nums,mid+1,end);
return node;
}
}
7、根据有序链表构造平衡二叉树
给定一个单链表的头节点 head ,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。
首先确定中间节点,然后递归创建节点,左右建树。
class Solution {
public TreeNode sortedListToBST(ListNode head) {
if(head==null) return null;
if(head.next==null){
TreeNode node=new TreeNode(head.val);
return node;
}
ListNode premid=getmid(head); //获取premid
ListNode mid=premid.next;
premid.next=null;
TreeNode node=new TreeNode(mid.val);
node.left=sortedListToBST(head);
node.right=sortedListToBST(mid.next);
return node;
}
private ListNode getmid(ListNode head) {
ListNode slow = head, fast = head.next;
ListNode pre = head; //初始是这个而不是new ListNode(0)!!!
while (fast != null && fast.next != null) {
pre = slow;
slow = slow.next;
fast = fast.next.next;
}
return pre;
}
}
同样的原理,但是注意Java函数中的局部性:
class Solution {
public TreeNode sortedListToBST(ListNode head) {
if(head==null) return null;
if(head.next==null) return new TreeNode(head.val);
ListNode mid=getmid(head);
TreeNode node=new TreeNode(mid.val);
node.left=sortedListToBST(head);
node.right=sortedListToBST(mid.next);
return node;
}
private ListNode getmid(ListNode head) {
ListNode slow = head, fast = head.next;
ListNode pre = head; //初始是这个而不是new ListNode(0)!!!
while (fast != null && fast.next != null) {
pre = slow;
slow = slow.next;
fast = fast.next.next;
}
pre.next=null; //在这里将 pre.next=null;不会影响真正的链表;
return slow;
}
}
8、在二查找树中寻找两个节点,使它们的和为一个给定值
给定一个二叉搜索树 root 和一个目标结果 k,如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果,则返回 true。
class Solution {
Set<Integer> set = new HashSet<Integer>();
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);
}
}
9、在二叉查找树中寻找两个节点之差的最小绝对值
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。
class Solution {
int res=Integer.MAX_VALUE; //全局变量
TreeNode pre=null; //定义一个pre节点
public int getMinimumDifference(TreeNode root) {
inorder(root);
return res;
}
public void inorder(TreeNode node){
if(node==null) return;
inorder(node.left);
if(pre!=null) res=Math.min(res,Math.abs(pre.val-node.val));
pre=node;
inorder(node.right);
}
}
10、二叉搜索树中的众数
给一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有众数(即,出现频率最高的元素)。
//嘿嘿,虽然效率不是很高但是做出来了,重点是hashmap的排序
class Solution {
public int[] findMode(TreeNode root) {
if(root==null) return new int[]{};
HashMap<Integer,Integer> hashMap=new HashMap<>();
travers(root,hashMap);
List<Map.Entry<Integer,Integer>> list=new ArrayList<>(hashMap.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
return o2.getValue()-o1.getValue();
}
});
int size=list.size(),i=0;
for(i=0;i<size;++i){
if(i>0 && list.get(i).getValue()<list.get(i-1).getValue())
break;
}
int[] res=new int[i];
for(int j=0;j<i;++j)
res[j]=list.get(j).getKey();
return res;
}
public void travers(TreeNode root,HashMap<Integer,Integer> hashMap){
if(root==null) return;
travers(root.left,hashMap);
hashMap.put(root.val,hashMap.getOrDefault(root.val,0)+1);
travers(root.right,hashMap);
}
}
总结
提示:以上就是关于Leetcode树部分所有题目啦。