Leetcode100之树

78 阅读4分钟

一.树的遍历 前中后序遍历,层次遍历 前中后序遍历如下:与其说对应代码如下,这种代码被称作前中后序遍历 理解:遵循“根左右”,“左根右”,“左右跟的顺序”

class Solution {
        public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> list = new ArrayList<>();
            return zhongxubianli(root, list);
        }

        private List<Integer> zhongxubianli(TreeNode root, List<Integer> list) {
        if (root == null) return list;
        // 前序遍历
        // list.add(root.val);
        zhongxubianli(root.left, list);
        // 中序遍历
        // list.add(root.val);
        zhongxubianli(root.right, list);
        // 后序遍历
        // list.add(root.val);
        return list;
        }
}

层次遍历,即BFS宽度优先,一层一层遍历,通过队列存储该层节点,遍历处理再将其子节点依次添加进来处理即可 // 队列的创建与接口

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
    	if (root == null) return new LinkedList<List<Integer>>();
    	List<List<Integer>> list = new LinkedList<List<Integer>>();
    	Queue<TreeNode> queue = new LinkedList<>();
    	queue.offer(root);
    	while(!queue.isEmpty()) {
    		int n = queue.size();
    		List<Integer> res = new LinkedList<>();
    		for (int i = 0; i < n; i++) {
    			TreeNode node = queue.poll();
    			res.add(node.val);
    			if (node.left!=null) {
    				queue.offer(node.left);
    			}
    			if (node.right!=null) {
    				queue.offer(node.right);
    			}
				
			}
    		list.add(res);
    	}
    	return list;
    }
}

二叉搜索树,即左边比根小,右边比根大,中序遍历后是有序链表

// no.96 利用卡特兰数
class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n+1];
        dp[0]=1;
        dp[1]=1;
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                dp[i] += dp[j - 1] * dp[i - j];
            }
        }
        return dp[n];
    }
}
// no.98
class Solution {
    public boolean isValidBST(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        isValidBST1(root, list);
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i)<= list.get(i-1)) return false;
        }
        return true;
    }

    private void isValidBST1(TreeNode root, List<Integer>  num) {
        if (root == null) return;
        isValidBST1(root.left, num);
        num.add(root.val);
        isValidBST1(root.right, num);
    }
}

no.105 前序遍历+中序遍历构造二叉树

// 要点:节约时间,先用map将中序数组值与下标存储起来
// 要点2:线序遍历首节点便是根节点,找到在中序中的位置,左边的范围为左子树,右边为右子树,继续遍历。这个范围同时也是确定前序数组中的下一个根,需要4个辅助参数记录前中数组的范围
class Solution {
                                                                                                                                                                                                   	Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
    	if(preorder == null || preorder.length == 0) return null;
    	for (int i = 0; i < inorder.length; i++) {
    		map.put(inorder[i], i);
		}
    	return buildTree1(preorder, inorder, 0, preorder.length-1, 0 ,inorder.length-1);

    }
	private TreeNode buildTree1(int[] preorder, int[] inorder, int l_p, int r_p, int l_i, int r_i) {
		if (l_p>r_p) return null;
		
		TreeNode node = new TreeNode(preorder[l_p]);
		int newRootIndex = map.get(preorder[l_p]);
		int step = newRootIndex-l_i;
		
		node.left = buildTree1(preorder, inorder, l_p+1, l_p+step, l_i ,newRootIndex-1);
		node.right = buildTree1(preorder, inorder, l_p+step+1, r_p, newRootIndex+1 ,r_i);
		return node;
	}
}

no.114 二叉树展开为列表:先通过前序遍历记录至列表,再遍历列表赋值 no.124 二叉树的最大路径和

// 如果当前节点是最大路径的根结点,那么就等于root.val加上左右两边的值(前提是两边大于0)	
// 如果当前不是最大路径的根节点,就需要返回左右两边最大的那个值
// 技巧:即在回溯过程也要计算结果,两边不耽误(许多题中都需要这么使用)
// 技巧:在递归函数中定义值,就相当于是该值是以当前为根节点得出的结果
	class Solution {
		int res = Integer.MIN_VALUE;
	    public int maxPathSum(TreeNode root) {
	    	if (root == null) return 0;
	    	maxPathSum1(root);
	    	return res;
	    }
	    public int maxPathSum1(TreeNode root) {
	    	if (root == null) return 0;
	    	
	    	int left = Math.max(maxPathSum1(root.left), 0)+root.val;
	    	int right =  Math.max(maxPathSum1(root.right), 0)+ root.val;
	    	res = Math.max(res, left + right - root.val);
	    	return Math.max(left, right);
	    }
	}

no236. 二叉树的最近公共祖先 // 借助后序遍历,因为顺序是左右根 // 找到p或q就返回,直到回溯到p,q都找到, 那就是结果 // 可能是p或q就是祖先,代码已给出

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    	if (root == null || root == p || root == q) return root;
    	TreeNode node1 = lowestCommonAncestor(root.left, p, q);
    	TreeNode node2 = lowestCommonAncestor(root.right, p, q);
        // 借助后序遍历,回溯到同时找到pq时便是返回结果
    	if (node2==null) return node1; // 特例,p或q是对方的祖先
    	if (node1 == null) return node2; // 特例,p或q是对方的祖先
    	if (node1 != null && node2 != null) return root;
		return null;
    }
}

no297. 二叉树的序列化与反序列化 // 前序遍历或层次遍历,因为反序列化要从根节点开始。然后再反过来计算即可,要注意对于需要把null值也序列化,不然反推不回来

public class Codec {

    public String serialize(TreeNode root) {
    	StringBuilder sb = new StringBuilder();
    	BFS(root, sb);
        return sb.toString();
    }
    private void BFS(TreeNode root, StringBuilder sb) {
		if (root == null)
			return;
		Queue<TreeNode> queue = new LinkedList<>();
		queue.offer(root);
		while (!queue.isEmpty()) {
			for (int i = 0; i < queue.size(); i++) {
				TreeNode node = queue.poll();
				if (node == null) sb.append("null,");
				else {
					sb.append(node.val + ",");
					queue.offer(node.left);
					queue.offer(node.right);
				}
			}
		}
	}
    
    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
    	if (data == null || data.isEmpty())
			return null;
    	String[] str = data.split(","); 
    	
        int index = 0;
        TreeNode node = new TreeNode(Integer.parseInt(str[index]));
        Queue<TreeNode> queue = new LinkedList<>();
    	queue.offer(node);
        while (true) {
        	for (int i = 0; i < queue.size(); i++) {
        		TreeNode root = queue.poll();
        		if (++index >= str.length-1) return node;
        		if (!"null".equals(str[index])) {
        			TreeNode left = new TreeNode(Integer.parseInt(str[index]));
        			root.left = left;
        			queue.offer(left);
        		}
        		if (++index >= str.length-1) return node;
        		if (!"null".equals(str[index])) {
        			TreeNode right = new TreeNode(Integer.parseInt(str[index]));
        			root.right = right;
        			queue.offer(right);
        		}
        		
			}	
        }
    }
}

no337. 打家劫舍3 首先并不是135层或者246层加起来就可以,可能存在孙子节点+叔叔节点才是最大值 那么对于一个节点来说,存在选,不选两种状态并取其中的最大值 如果选,那么当前最大值就等于当前值+孙子节点的最大值(注意:这不代表孙子节点是一定选择的) 如果不选,就等于儿子节点的最大值(同样的,也不代表儿子节点就是被选了的) 此外呢,注意到有些节点是可能重复计算的,可以用map记录下

class Solution {
	Map<TreeNode, Integer> map = new HashMap<>();
    public int rob(TreeNode root) {
    	if (root ==null) return 0;
    	if (map.get(root) != null) return map.get(root);
    	int money = root.val;
    	if (root.left != null) {
    		money += rob(root.left.left)+rob(root.left.right);
    	}
    	if (root.right != null) {
    		money+=rob(root.right.left)+rob(root.right.right);
    	}
    	int max = Math.max(money, rob(root.left)+rob(root.right));
    	map.put(root, max);
    	return max;

    }
}

no437. 路径总和(前缀和) // 向下递归通过map记录前缀和(节点以上的和) // 向上回溯清除当前前缀和 // 当前前缀和前去目标target如果有值加起来就行

class Solution {
    Map<Integer, Integer> currentSumMap = new HashMap<>();
    public int pathSum (TreeNode root, int targetSum) {
        currentSumMap.put(0, 1);
        return pathSum1(root, targetSum, 0);
    }
    public int pathSum1(TreeNode root, int targetSum, int currentSum) {
        if (root == null) return 0;
        int res = 0;
        currentSum+= root.val;
        res+=currentSumMap.getOrDefault((currentSum-targetSum), 0);
        currentSumMap.put(currentSum, currentSumMap.getOrDefault(currentSum, 0) +1);
        res+=pathSum1(root.left, targetSum, currentSum);
        res+=pathSum1(root.right, targetSum, currentSum);
        currentSumMap.put(currentSum, currentSumMap.getOrDefault(currentSum, 0) -1);
        return res;
    }
}

no538. 把二叉搜索树转换为累加树 // 中序遍历变形 -> 右中左。依次相加即可

class Solution {
    public TreeNode convertBST(TreeNode root) {
        convertBST1(root);
        return root;
    }
    int res = 0;
    public int convertBST1(TreeNode root) {
        if (root == null) return 0;
        convertBST1(root.right);
        res+=root.val;
        root.val = res;
        convertBST1(root.left);
        return res;
    }
}

no543. 二叉树的直径 // 根节点并非最大,可能是子节点 // 需要回溯记录最大值

class Solution {
    int ans = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        if (root == null) return 0;
        TreeNode node = new TreeNode(0);
        diameterOfBinaryTree1(root, node);
        return node.val;
    }
    public int diameterOfBinaryTree1(TreeNode root, TreeNode ans) {
        if (root == null) return 0;
        int left = diameterOfBinaryTree1(root.left, ans);
        int right = diameterOfBinaryTree1(root.right, ans);
        ans.val = Math.max(left+right, ans.val);
        return Math.max(left,right) + 1;
    }
}

个人总结

  1. 理解前中序层次遍历,对于给出的树,需要借助遍历去计算,至于何种遍历,根据题意是否有计算顺序,没有可默认前序
  2. 对于最大最小种类等题,根节点可能不符合题意,这种需要同时处理两种情况(可能:需要一个额外值记录[不受递归影响])
    1. 递归过程中定义变量求出当前值并记录比较(当作符合题意根节点
    2. 正常返回当前节点值给父节点(非符合题意根节点