由遍歷結果反求樹
分析:遞迴分治,第一層需要找到相應的遍歷結果,對陣列來說,問題轉化為找下標問題
對前序、中序遍歷結果來說
前序:[root,[左],[右]]
中序:[[左],root,[右]]
因此,中序中root的下標可求,為inorderPos
對每一層來說,左子樹的長度為leftLen = inorderPos,右子樹的長度為rightLen = inorder.length - 1 - leftLen,
左子樹前序為preorder[1 至 leftLen],中序為inorder[0 至 leftLen - 1],可以使用System.arraycopy(preorder, 1, leftPre, 0, leftLen),
System.arraycopy(inorder, 0, leftInorder, 0, leftLen);
右子樹前序為preorder[leftLen + 1 至 preorder.length - 1],中序為inorder[leftLen + 1 至 inorder.lenth - 1],可以使用System.arraycopy(preorder, leftLen + 1, rightPre, 0, rightLen),System.arraycopy(inorder, leftLen + 1, rightInorder, 0, rightLen);
對中序、後序來說,
中序:[[左],root,[右]]
後序:[[左],[右],root]
Leetcode 105 由前序、中序構建樹
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length == 0 || inorder.length == 0 || preorder.length != inorder.length){
return null;
}
int len = preorder.length;
TreeNode root = new TreeNode(preorder[0]);
int inorderPos = 0;
for(; inorderPos < inorder.length; inorderPos++){
if(inorder[inorderPos] == root.val){
break;
}
}
int leftLen = inorderPos;
int rightLen = len - inorderPos - 1;
int[] leftPre = new int[leftLen];
int[] leftInorder = new int[leftLen];
int[] rightPre = new int[rightLen];
int[] rightInorder = new int[rightLen];
for(int i = 0; i < leftLen; i++){
leftPre[i] = preorder[i + 1];
leftInorder[i] = inorder[i];
}
for(int i = 0; i < rightLen; i++){
rightPre[i] = preorder[leftLen + 1 + i];
rightInorder[i] = inorder[leftLen + 1 + i];
}
TreeNode left = buildTree(leftPre, leftInorder);
TreeNode right = buildTree(rightPre, rightInorder);
root.left = left;
root.right = right;
return root;
}
Leetcode 106 由中序、後序構建樹
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(inorder.length == 0 || postorder.length == 0){
return null;
}
int len = postorder.length;
TreeNode root = new TreeNode(postorder[len - 1]);
int inorderPos = 0;
for(; inorderPos < len; inorderPos++){
if(inorder[inorderPos] == root.val){
break;
}
}
int leftLength = inorderPos;
int rightLength = len - inorderPos - 1;
int[] leftInorder = new int[leftLength];
int[] leftPost = new int[leftLength];
int[] rightInorder = new int[rightLength];
int[] rightPost = new int[rightLength];
for(int i = 0; i < leftLength; i++){
leftInorder[i] = inorder[i];
leftPost[i] = postorder[i];
}
for(int i = 0; i < rightLength; i++){
rightInorder[i] = inorder[inorderPos + 1 + i];
rightPost[i] = postorder[leftLength + i];
}
TreeNode left = buildTree(leftInorder, leftPost);
TreeNode right = buildTree(rightInorder, rightPost);
root.left = left;
root.right = right;
return root;
}
leetcode 124
思路:
分治:對於每一個結點來說,需要計算,當前值+左結點+右結點 與 最大值的比較,同時,左結點與右結點的值通過遞迴得到,因此,遞迴的返回值應是一條路徑的和
public class Solution{
int maxNum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root){
if(root == null){
return 0;
}
count(root);
return maxNum;
}
public int count(TreeNode root){
int lval = Integer.MIN_VALUE, rval = Integer.MIN_VALUE;
int val = root.val;
if(root.left != null){
lval = count(root.left);
}
if(root.right != null){
rval = count(root.right);
}
val = val + Math.max(lval, 0) + Math.max(rval, 0);
if(val > maxNum){
maxNum = val;
}
return root.val + Math.max(Math.max(lval, rval), 0);
}
}
最小深度與最大深度
leetcode 111 最小深度
遞迴法:
思路:
退出條件
-
root == null,直接返回0,但是!如果root.left或root.right其中一個為null,不能退出遞迴,兩種解決方法
方法一:使用新的遞迴函式規避public int minDepth(TreeNode root){ if(root == null){ return 0; } return getMin(root); } public int getMin(TreeNode root){ //規避左右子樹某一個為null if(root == null){ return Integer.MAX_VALUE;//排除此條路徑 } if(root.left == null && root.right == null){ return 1; } int left = Integer.MAX_VALUE; int right = Integer.MAX_VALUE; if(root.left != null){ left = getMin(root.left); } if(root.right != null){ right = getMin(root.right); } return Math.min(left, right) + 1; }
方法二:給當前方法打補丁
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
if(root.left == null){
return minDepth(root.right) + 1;
}else if(root.right == null){
return minDepth(root.left) + 1;
}else{
return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
}
}
root.left == null && root.right == null 說明為葉子結點,返回1
當前層數加 左右子樹的最小深度
迭代法
思路:層級遍歷,一旦在當前層發現葉子結點,返回層數
public int minDepth(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
int depth = 0;
int curLevelNodes = 1;
int nextLevelNodes = 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode cur = queue.poll();
curLevelNodes--;
if(cur.left == null && cur.right == null){
return depth + 1;
}
if(cur.left != null){
queue.add(cur.left);
nextLevelNodes++;
}
if(cur.right != null){
queue.add(cur.right);
nextLevelNodes++;
}
if(curLevelNodes == 0){
depth++;
curLevelNodes = nextLevelNodes;
nextLevelNodes = 0;
}
}
return depth;
}
leetcode 104 最大深度
遞迴法
思路:遞迴時邏輯是一貫的
public int getMaxDepth(TreeNode root){
if(root == null){
return 0;
}
return Math.max(getMaxDepth(root.left), getMaxDepth(root.right)) + 1;
}
迭代法
思路:層級遍歷求最大深度
public int maxDepth(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
int depth = 0;
int curLevelNodes = 1;
int nextLevelNodes = 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode cur = queue.poll();
curLevelNodes--;
if(cur.left != null){
queue.add(cur.left);
nextLevelNodes++;
}
if(cur.right != null){
queue.add(cur.right);
nextLevelNodes++;
}
if(curLevelNodes == 0){
depth++;
curLevelNodes = nextLevelNodes;
nextLevelNodes = 0;
}
}
return depth;
}
leetcode 110 樹是否平衡
樹平衡要求對所有結點來說,其左右子樹的深度差不超過1
public boolean isBalanced(TreeNode root){
if(root == null){
return true;
}
int leftDepth = getMaxDepth(root.left);
int rightDepth = getMaxDepth(root.right);
if(Math.abs(leftDepth - rightDepth) > 1){
return false;
}
return isBalanced(root.left) && isBalanced(root.right);
}
leetcode 100 判斷兩棵樹是否相同
分析:樹的相同,首先結構相同,其次結點值相同
兩種判斷結構是否相同的寫法,邏輯一樣
方法一
public boolean isSame(TreeNode r1, TreeNode r2){
if(r1 == null && r2 == null){
return true;
}
if(r1 == null || r2 == null){
return false;
}
//else 結構相同
}
方法二
public boolean isSame(TreeNode r1, TreeNode r2){
if(r1 == null){
return r2 == null;
}
if(r2 == null){
return false;
}
//else 結構相同
}
完整邏輯
public boolean isSame(TreeNode r1, TreeNode r2){
if(r1 == null){
return r2 == null;
}
if(r2 == null){
return false;
}
return r1.val == r2.val && isSame(r1.left, r2.left) && isSame(r1.right, r2.right);
}
leetcode 101 判斷對稱
左右子樹,結構相同,對稱位置值相同
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return help(root.left, root.right);
}
public boolean help(TreeNode p, TreeNode q){
if(p == null){
return q == null;
}
if(q == null){
return false;
}
return p.val == q.val && help(p.left, q.right) && help(p.right, q.left);
}
leetcode 98 判斷二叉搜尋樹
迭代法
思路:中序遍歷 前一個結點值小於後面的結點值
public boolean isValidBST(TreeNode root){
if(root == null){
return true;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode preCur = null;
while(true){
while(cur != null){
stack.push(cur);
cur = cur.left;
}
if(stack.isEmpty()){
break;
}
cur = stack.pop();
if(preCur != null){
if(preCur.val >= cur.val){
return false;
}
}
preCur = cur;
cur = cur.right;
}
return true;
}
遞迴法
思路:同樣是中序遍歷
思考 pre結點在哪賦值,賦值前如何處理
TreeNode pre = null;
public boolean isValidBST(TreeNode root) {
if(root == null){
return true;
}
if(root.left == null && root.right == null){
return true;
}
return help(root);
}
public boolean help(TreeNode root){
if(root == null){
return true;
}
boolean left = help(root.left);
if(pre != null && pre.val >= root.val){
return false;
}
pre = root;
boolean right = help(root.right);
return left && right;
}
連結串列與樹
leetcode 114 二叉樹轉連結串列
思路:斷開每一個結點,從用一個指標遞迴地向下指,每次都只更新右結點,遞迴順序為先左子樹,後右子樹
TreeNode pointer = new TreeNode(-1);
public void flatten(TreeNode root){
if(root == null){
return;
}
TreeNode left = root.left;
TreeNode right = root.right;
root.left = null;
root.right = null;
pointer.right = root;
pointer = root;
flatten(root.left);
flatten(root.right);
}
連結串列轉二叉樹
O(nlogn)解法
public TreeNode sortedListToBST(ListNode head){
if(head == null){
return null;
}
if(head.next == null){
return new TreeNode(head.val);
}
int length = 0;
ListNode cur = head;
while(cur != null){
cur = cur.next;
length++;
}
return help(head, length);
}
public TreeNode help(ListNode head, int length){
if(length == 0){
return null;
}
ListNode now = head;
for(int i = 0; i < (length - 1) >> 1; i++){
now = now.next;
}
TreeNode root = new TreeNode(now.val);
TreeNode left = help(head, (length - 1) >> 1);
TreeNode right = help(now.next, length >> 1);
root.left = left;
root.right = right;
return root;
}
O(n)解法
將連結串列先轉成陣列
leetcode 108 陣列轉平衡二叉樹
public TreeNode sortedArrayToBST(int[] nums) {
if(nums.length == 0){
return null;
}
if(nums.length == 1){
return new TreeNode(nums[0]);
}
int length = nums.length;
int now = nums[(length - 1) >> 1];
TreeNode root = new TreeNode(now);
int leftLen = (length - 1) >> 1;
int rightLen = length >> 1;
int[] leftArr = new int[leftLen];
int[] rightArr = new int[rightLen];
System.arraycopy(nums, 0, leftArr, 0, leftLen);
System.arraycopy(nums, leftLen + 1, rightArr, 0, rightLen);
root.left = sortedArrayToBST(leftArr);
root.right = sortedArrayToBST(rightArr);
return root;
}