1. 程式人生 > 其它 >JAVA資料結構leetcode——樹樹樹樹樹樹樹樹樹樹樹樹樹樹樹樹

JAVA資料結構leetcode——樹樹樹樹樹樹樹樹樹樹樹樹樹樹樹樹

技術標籤:leetcode刷題java演算法leetcode資料結構樹結構

文章目錄

1遞迴

核心程式碼,104樹的高度深度⭐⭐⭐

//dfs
public int maxDepth(TreeNode root) {
    if (root == null) return 0;
    return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}

111最小路徑——最淺深度

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;
}

110判斷平衡樹——深度

高度差小於等於一,遍歷節點的深度,判斷差值

private boolean result = true
; public boolean isBalanced(TreeNode root) { maxDepth(root); return result; } public int maxDepth(TreeNode root) { if (root == null) return 0; int l = maxDepth(root.left); int r = maxDepth(root.right); if (Math.abs(l - r) > 1) result = false; return 1 + Math.max(l, r); }

543樹的路徑和——某點的左右子樹的深度和

private int max = 0;

public int diameterOfBinaryTree(TreeNode root) {
    depth(root);
    return max;
}

private int depth(TreeNode root) {
    if (root == null) return 0;
    int leftDepth = depth(root.left);
    int rightDepth = depth(root.right);
    max = Math.max(max, leftDepth + rightDepth);
    return Math.max(leftDepth, rightDepth) + 1;
}

226翻轉樹左右子樹——自上而下遞迴

public TreeNode invertTree(TreeNode root) {
    if (root == null) return null;
    TreeNode left = root.left;  // 後面的操作會改變 left 指標,因此先儲存下來
    root.left = invertTree(root.right);
    root.right = invertTree(left);
    return root;
}

617合併兩個樹——自上而下構建新結點遞迴合併

public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
    if (t1 == null && t2 == null) return null;
    if (t1 == null) return t2;
    if (t2 == null) return t1;
    TreeNode root = new TreeNode(t1.val + t2.val);
    root.left = mergeTrees(t1.left, t2.left);
    root.right = mergeTrees(t1.right, t2.right);
    return root;
}

112 判斷路徑和==某個數?——從上往下,減去結點值遞迴往下找

base case
1.未找到:訪問到了空結點
2.找到了:.當前結點值正好等於餘數,且是葉子結點。

public boolean hasPathSum(TreeNode root, int sum) {
    if (root == null) return false;
    if (root.left == null && root.right == null && root.val == sum) return true;
    return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}

437統計路徑和==某個數?(不一定以 root 開頭)——遍歷所有結點的時候都認為是從根結點開始,然後把路徑數加到一起

public int pathSum(TreeNode root, int sum) {
    if (root == null) return 0;
    int ret = pathSumStartWithRoot(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
    return ret;
}

private int pathSumStartWithRoot(TreeNode root, int sum) {
    if (root == null) return 0;
    int ret = 0;
    if (root.val == sum) ret++;
    ret += pathSumStartWithRoot(root.left, sum - root.val) + pathSumStartWithRoot(root.right, sum - root.val);
    return ret;
}

687 相同節點值的最大路徑長度——路徑上的值判斷

class Solution {
    private int  maxdis =0;
    //dfs從底向上尋找
    public int longestUnivaluePath(TreeNode root) {
        longdistance(root);
        return maxdis;
    }
//    定義一個函式,從低向上判斷相同結點相連的左右結點之和(不包括根節點所以不用減掉一)
//
    private int longdistance(TreeNode root){
        if(root == null) return 0;
        int l = longdistance( root.left);
        int r  = longdistance(root.right);
        int ldep = root.left!=null&&root.left.val==root.val?l+1:0;
        int rdep =root.right!=null&&root.right.val==root.val?r+1:0;
        //記錄最大深度
        maxdis=Math.max(maxdis,rdep+ldep);
        //返回當前節點的深度,左右兩邊的最大長度
        return Math.max(ldep,rdep);
    }

}

337打家劫舍——1.動態規劃. 2.間隔遍歷

class Solution {
    public int rob(TreeNode root) {
        //間接dfs 累積之和的最大值
        if(root ==null) return 0;
//        記錄當前結點加上左右樹累積的最大值
        int val =root.val;
//        間接左子樹累積最大值
        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);
//        當前結點不累計。記錄從下個結點才開始累積的最大值
        int val2=rob(root.left)+rob(root.right);
        //看看哪個最大,返回當前結點累積的最大值
        return Math.max(val,val2);
    }
}

671第二小的結點——找左右子樹中最小的

public int findSecondMinimumValue(TreeNode root) {
    if (root == null) return -1;
    if (root.left == null && root.right == null) return -1;
    int leftVal = root.left.val;
    int rightVal = root.right.val;
    if (leftVal == root.val) leftVal = findSecondMinimumValue(root.left);
    if (rightVal == root.val) rightVal = findSecondMinimumValue(root.right);
    if (leftVal != -1 && rightVal != -1) return Math.min(leftVal, rightVal);
    if (leftVal != -1) return leftVal;
    return rightVal;
}

核心程式碼,同樹判斷

private boolean isSubtreeWithRoot(TreeNode s, TreeNode t) {
    if (t == null && s == null) return true;
    if (t == null || s == null) return false;
    if (t.val != s.val) return false;
    return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right);
}

變形1:572子樹判斷

572

分兩部分遍歷節點和判斷同樹

public boolean isSubtree(TreeNode s, TreeNode t) {
    if (s == null) return false;
    //遍歷結點
    return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t);
}

private boolean isSubtreeWithRoot(TreeNode s, TreeNode t) {
    if (t == null && s == null) return true;
    if (t == null || s == null) return false;
    if (t.val != s.val) return false;
    return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right);
}

變形2:101 樹的對稱

101

對比兩個結點是否對車,同樹判斷的左右子樹互換位置判斷是否相同。

public boolean isSymmetric(TreeNode root) {
    if (root == null) return true;
    return isSymmetric(root.left, root.right);
}

private boolean isSymmetric(TreeNode t1, TreeNode t2) {
    if (t1 == null && t2 == null) return true;
    if (t1 == null || t2 == null) return false;
    if (t1.val != t2.val) return false;
    return isSymmetric(t1.left, t2.right) && isSymmetric(t1.right, t2.left);
}

5.3 從中序與後序遍歷序列構造二叉樹

106

從後序找根結點,遞迴劃分中序的左右子樹。
不錯的題解

public class ti_106 {
    HashMap<Integer,Integer> memo= new HashMap<>();
    int[] post;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        for(int i=0;i<inorder.length;i++){//中序遍歷存放位置索引
            memo.put(inorder[i],i);
        }
        post = postorder;
        TreeNode root = buildTree(0,inorder.length-1,0,post.length-1);
        return root;
    }
    //遞迴建樹
    public TreeNode buildTree(int is,int ie,int ps,int pe){
        if(is>ie || ps>pe){                   //葉子結點
            return null;
        }
        int node =post[pe];
        int ri = memo.get(node);
        TreeNode root =new TreeNode(node);
        root.left = buildTree(is,ri-1,ps,ps+ri-is-1);//ri-is-1為左子樹長度
        root.right = buildTree(ri+1,ie,ps+ri-is,pe-1);//pe-1去掉最後的根結點
        return root;//返回當前結點給上一級的left或者right
    }
}

2 前中後序遍歷

2.1 遞迴實現

前序遍歷

public void dfs(TreeNode root) {
    visit(root.val);
    dfs(root.left);
    dfs(root.right);
}

中序遍歷

public void dfs(TreeNode root) {
    dfs(root.left);
    visit(root.val);
    dfs(root.right);
}

後序遍歷

public void dfs(TreeNode root) {
    dfs(root.left);
    dfs(root.right);
    visit(root.val);
}

2.2 非遞迴實現

前序遍歷

構建棧,根左右,先彈出根結點,然後將右結點先放,最後彈出。
彈出記得儲存。

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> ret = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode node = stack.pop();
        if (node == null) continue;
        ret.add(node.val);
        stack.push(node.right);  // 先右後左,保證左子樹先遍歷
        stack.push(node.left);
    }
    return ret;
}

中序遍歷

左子結點存到底,彈出記得存入右子結點。
彈出時記得儲存。

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> ret = new ArrayList<>();
    if (root == null) return ret;
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        while (cur != null) {
            stack.push(cur);
            cur = cur.left;
        }
        TreeNode node = stack.pop();
        ret.add(node.val);
        cur = node.right;
    }
    return ret;
}

後序遍歷

通過前序遍歷變形得到,後序遍歷為左右根,反過來是根右左。

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> ret = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode node = stack.pop();
        if (node == null) continue;
        ret.add(node.val);
        stack.push(node.left);
        stack.push(node.right);
    }
    Collections.reverse(ret);
    return ret;
}

3 層序遍歷

dfs實現,佇列實現,先入先出,從左到右的層序遍歷。

void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode node = queue.poll(); // Java 的 pop 寫作 poll()
        if (node.left != null) {
            queue.add(node.left);
        }
        if (node.right != null) {
            queue.add(node.right);
        }
    }
}