JAVA資料結構leetcode——樹樹樹樹樹樹樹樹樹樹樹樹樹樹樹樹
阿新 • • 發佈:2020-12-25
技術標籤: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);
}
}
}