【數據結構與算法】二叉樹模板及例題


二叉樹節點結構

class Node<V>{ 
    V value; 
	Node left; 
	Node right; 
}

二叉樹的遍歷(遞歸)

先序遍歷

順序:根左右

    public static void preOrderRecur(Node head) {
		if (head == null) {
			return;
		}
		System.out.print(head.value + " ");
		preOrderRecur(head.left);
		preOrderRecur(head.right);
	}

中序遍歷

順序:左根右

    public static void inOrderRecur(Node head) {
		if (head == null) {
			return;
		}
		inOrderRecur(head.left);
		System.out.print(head.value + " ");
		inOrderRecur(head.right);
	}

后序遍歷

順序:左右根

    public static void posOrderRecur(Node head) {
		if (head == null) {
			return;
		}
		posOrderRecur(head.left);
		posOrderRecur(head.right);
		System.out.print(head.value + " ");
	}

二叉樹的遍歷(非遞歸)

先序遍歷

順序:根左右

先把根節點壓入棧中,每次

  • 從棧中彈出一個節點cur

  • 處理節點cur

  • 先壓入cur的右節點,再壓入cur的左節點(如果有的話)

只要棧不為空,周而復始

    public static void preOrder(Node head) {
        if (head != null) {
            Stack<Node> stack = new Stack<>();
            stack.push(head);
            while (!stack.isEmpty()) {
                head = stack.pop();
                System.out.print(head.value + " ");
                if (head.right != null)
                    stack.push(head.right);
                if (head.left != null)
                    stack.push(head.left);
            }
            System.out.println();
        }

中序遍歷

順序:左根右

  • 每棵子樹整棵樹左邊界進棧

  • 依次彈出的過程中處理節點

  • 對彈出節點右樹做同樣操作

周而復始

    public static void inOrder(Node head) {
        if (head != null) {
            Stack<Node> stack = new Stack<>();
            while (!stack.isEmpty() || head != null) {  //剛開始stack為空,head不為null
                if (head != null) {
                    stack.push(head);
                    head = head.left;
                } else {
                    head = stack.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
            System.out.println();
        }
    }

后序遍歷

順序:左右根

反過來就是根右左,准備兩個棧。

先把根節點壓入棧1中,每次

  • 從棧1中彈出一個節點cur

  • 把節點cur壓入棧2

  • 先在棧1中壓入cur的右節點,再壓入cur的左節點(如果有的話)

只要棧1不為空,周而復始

最后依次彈出棧2中的節點,其順序就是后序遍歷的順序

    public static void postOrder(Node head) {
        if (head != null) {
            Stack<Node> stack1 = new Stack<>();
            Stack<Node> stack2 = new Stack<>();
            stack1.push(head);
            while (!stack1.isEmpty()){
                head = stack1.pop();
                stack2.push(head);
                if(head.left!=null){
                    stack1.push(head.left);
                }
                if(head.right!=null){
                    stack1.push(head.right);
                }
            }
            while (!stack2.isEmpty()){
                head = stack2.pop();
                System.out.print(head.value+" ");
            }
            System.out.println();
        }
    }

二叉樹層序(寬度)遍歷

先把根節點放入隊列中,每次

  • 彈出節點cur

  • 處理節點cur

  • 先把cur的左節點放入隊列,再把cur的右節點放入隊列(如果存在的話)

周而復始,直到隊列為空

    public static void leverOrder(Node head) {
        if (head != null) {
            Queue<Node> queue = new LinkedList<>();
            queue.add(head);
            while (!queue.isEmpty()) {
                head = queue.poll();
                System.out.print(head.value + " ");
                if (head.left != null) {
                    queue.add(head.left);
                }
                if (head.right != null) {
                    queue.add(head.right);
                }
            }
            System.out.println();
        }
    }
    public static void leverOrder(Node head) {
        if (head == null) return;
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        while (!queue.isEmpty()) {
            int n = queue.size();
            for (int i = 0; i < n; i++) {  //一次處理一層節點
                head = queue.poll();
                System.out.printf(head.value + " ");
                if (head.left != null) queue.add(head.left);
                if (head.right != null) queue.add(head.right);
            }
        }
        System.out.println();
    }

二叉樹應用題

二叉樹深度

遞歸

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

層序遍歷bfs

    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int ans = 0;
        while(!queue.isEmpty()){
            ans++;       //每次處理一層,處理完ans加一
            int n = queue.size();
            for(int i = 0;i < n; i++){
                root = queue.poll();
                if(root.left!=null)  queue.add(root.left);
                if(root.right!=null) queue.add(root.right);
            }
        }
        return ans;
    }

求二叉樹最大寬度

層序遍歷的過程中使用HashMap記錄節點層數

    public static void maxWidth(Node head) {
        if (head != null) {
            Queue<Node> queue = new LinkedList<>();
            queue.add(head);
            HashMap<Node, Integer> levelMap = new HashMap<>();
            levelMap.put(head, 1);
            int curLevel = 1;    //當前層數
            int curLevelNodes = 0;    //當前層數的節點數
            int max = Integer.MIN_VALUE;
            while (!queue.isEmpty()) {
                Node cur = queue.poll();
                int curNodeLevel = levelMap.get(cur);  //獲取當前節點層數
                if (curNodeLevel == curLevel) {   //當前節點層數等於當前層數,節點數加一
                    curLevelNodes++;
                } else {
                    max = Math.max(max, curLevelNodes);  //否則max取較大值
                    curLevel++;                   //當前層數加一
                    curLevelNodes = 1;            //重置當前層數的節點數為1
                }
                if (cur.left != null) {
                    levelMap.put(cur.left, curNodeLevel + 1);
                    queue.add(cur.left);
                }
                if (cur.right != null) {
                    levelMap.put(cur.right, curNodeLevel + 1);
                    queue.add(cur.right);
                }
            }
            System.out.println(max);
        }
    }

判斷是否為搜索二叉樹

image

中序遍歷遞增就是搜索二叉樹

遞歸方式

    public static int preValue = Integer.MIN_VALUE;
    public static boolean checkBST(Node head){
        if(head!=null){
            boolean isLeftBst = checkBST(head.left);
            if(!isLeftBst){
                return false;
            }
            if(head.value<=preValue){
                return false;
            }else{
                preValue = head.value;
            }
            return checkBST(head.right);
        }
        return true;
    }

非遞歸方式

    public static boolean checkBST(Node head) {
        if (head != null) {
            Stack<Node> stack = new Stack<>();
            while (!stack.isEmpty() || head != null) {  //剛開始stack為空,head不為null
                if (head != null) {
                    stack.push(head);
                    head = head.left;
                } else {
                    head = stack.pop();
                    if(head.value<=preValue){
                        return false;
                    }else {
                        preValue = head.value;
                    }
                    head = head.right;
                }
            }
        }
        return true;
    }

樹形DP處理

  • 左子樹是搜索二叉樹

  • 右子樹是搜索二叉樹

  • 根節點大於左子樹最大值

  • 根節點小於右子樹最小值

    public static boolean isBST(Node head){
        return process(head).isBST;
    }
    public static class ReturnType {
        public boolean isBST;
        public int max;
        public int min;

        public ReturnType(boolean isBST, int max, int min) {
            this.isBST = isBST;
            this.max = max;
            this.min = min;
        }
    }
    public static ReturnType process(Node x){
        if(x==null) {
            return null;
        }
        ReturnType leftData = process(x.left);  //獲取左子樹處理信息
        ReturnType rightData = process(x.right);  //獲取右子樹處理信息
        int min = x.value;
        int max = x.value;
        if(leftData!=null){                   //獲取當前樹的最大值最小值
            min = Math.min(min,leftData.min);
            max = Math.max(max,leftData.max);
        }
        if(rightData!=null){
            min = Math.min(min,rightData.min);
            max = Math.max(max,rightData.max);
        }
        boolean isBST = true;
        //左子樹存在並且(左子樹不是BST或者左子樹最大值大於x)
        if(leftData!=null&&(!leftData.isBST||leftData.max>=x.value)){
            isBST = false;
        }
        //右子樹存在並且(右子樹不是BST或者右子樹最小值小於x)
        if(rightData!=null&&(!rightData.isBST||x.value>=rightData.min)){
            isBST = false;
        }
        return new ReturnType(isBST,max,min);
    }

判斷是否是完全二叉樹

image

  • 層序遍歷

  • 任何一個節點有右孩子沒左孩子,則不是完全二叉樹(1)

  • 在(1)的前提下,遇到第一個左右不雙全節點,那其后面必須都是葉子節點,否則不是二叉樹

    public static boolean checkCBT(Node head) {
        if (head != null) {
            boolean leaf = false;  //是否遇到過左右不雙全節點
            Node l = null;
            Node r = null;
            Queue<Node> queue = new LinkedList<>();
            queue.add(head);
            while (!queue.isEmpty()) {
                head = queue.poll();
                l = head.left;
                r = head.right;
                if ((leaf && !(l == null && r == null))  //遇到第一個左右不雙全節點,那么以后的節點都必須是葉子節點
                        ||
                        (l == null && r != null)) {  //任何一個節點有右孩子沒左孩子
                    return false;
                }
                if (l != null) {
                    queue.add(l);
                }
                if (r != null) {
                    queue.add(r);
                }
                if (l == null || r == null) {
                    leaf = true;
                }
            }
        }
        return true;
    }

判斷是否是平衡二叉樹

  • 左子樹是平衡的

  • 右子樹是平衡的

  • 左右子樹高度差的絕對值小於2

獲取左樹信息和右樹信息后,結合處理。屬於樹形DP

    public static boolean isBalancd(Node head) {
        return process(head).isBalanced;
    }

    public static class ReturnType {  //封裝了平衡狀態和高度
        public boolean isBalanced;
        public int height;

        public ReturnType(boolean isB, int hei) {
            isBalanced = isB;
            height = hei;
        }
    }

    public static ReturnType process(Node x) {
        if (x == null) {           //空樹是平衡的,高度為0
            return new ReturnType(true, 0);
        }
        ReturnType leftData = process(x.left);   //左樹
        ReturnType rightData = process(x.right); //右樹
        int height = Math.max(leftData.height, rightData.height) + 1;  //獲取左子樹和右子樹的最高高度+1
        boolean isBalanced = leftData.isBalanced && rightData.isBalanced &&  //如果左子樹平衡,右子樹平衡
                Math.abs(leftData.height - rightData.height) < 2;     //左右子樹的高度差的絕對值小於2
        return new ReturnType(isBalanced, height);    //返回新狀態
    }

判斷是否是滿二叉樹

如果一個二叉樹的層數為K,且結點總數是 (2^k) -1 ,則它就是滿二叉樹。

樹形DP問題

    public static boolean isFull(Node head) {
        if (head == null) {
            return true;
        }
        Info data = process(head);
        return data.nodes == ((1 << data.height) - 1);//是否層數為K,且結點總數是 (2^k) -1
    }

    public static class Info {  //封裝樹的高度和節點數
        public int height;
        public int nodes;

        public Info(int h, int n) {
            height = h;
            nodes = n;
        }
    }

    public static Info process(Node x) {
        if (x == null) {
            return new Info(0, 0);
        }
        Info leftData = process(x.left);    //獲取左子樹信息
        Info rightData = process(x.right);  //獲取右子樹信息
        int height = Math.max(leftData.height, rightData.height) + 1; //求新高度
        int nodes = leftData.nodes + rightData.nodes + 1;  //求總的節點數

        return new Info(height, nodes);
    }

在二叉樹中找到一個節點的后繼節點

現在有一種新的二叉樹節點類型如下:

public class Node { 
    public int value; 
	public Node left; 
	public Node right; 
	public Node parent; 
	public Node(int val) { 
	    value = val; 
	} 
}

該結構比普通二叉樹節點結構多了一個指向父節點的parent指針。
假設有一棵Node類型的節點組成的二叉樹,樹中每個節點的parent指針都正確地指向自己的父節點,頭節 點的parent指向null。 只給一個在二叉樹中的某個節點node,請實現返回node的后繼節點的函數。

在二叉樹的中序遍歷的序列中, node的下一個節點叫作node的后繼節點。

  • 一個節點有右子樹,那么它的下一個節點就是它的右子樹中的最左子節點。例如b的后繼節點是h。

  • 一個節點沒有右子樹時分兩種情況:

    • 當前節點是它父節點的左子節點,那么它的下一個節點就是它的父節點。 例如節點f的后繼節點是c,節點d的后繼節點是b。
      image
    • 當前節點是它父節點的右子節點,此時沿着指向父節點的指針一直向上遍歷,直到找到一個是它父節點的左子節點的節點,如果這個節點存在,那么這個節點的父節點就是我們要找的下一個節點。如下圖所示: f的下一個節點是a。
      image
    public static class Node {
		public int value;
		public Node left;
		public Node right;
		public Node parent;

		public Node(int data) {
			this.value = data;
		}
	}

	public static Node getSuccessorNode(Node node) {
		if (node == null) {
			return node;
		}
		if (node.right != null) {
			return getLeftMost(node.right);
		} else {
			Node parent = node.parent;
			while (parent != null && parent.left != node) {
				node = parent;
				parent = node.parent;
			}
			return parent;
		}
	}

	public static Node getLeftMost(Node node) {
		if (node == null) {
			return node;
		}
		while (node.left != null) {
			node = node.left;
		}
		return node;
	}

折紙問題

請把一段紙條豎着放在桌子上,然后從紙條的下邊向上方對折1次,壓出折痕后 展開。 此時折痕是凹下去的,即折痕突起的方向指向紙條的背面。 如果從紙條的下邊向上方連續對折2次,壓出折痕后展開,此時有三條折痕,從 上到下依次是下折痕、下折痕和上折痕。 給定一個輸入參數N,代表紙條都從下邊向上方連續對折N次。 請從上到下打印所有折痕的方向。
例如:
N=1時,打印: down
N=2時,打印: down down up

分析:
image

發現第n+1次的折痕中凹折痕一定在第n次折痕的左邊,第n+1次折痕中凸折痕一定在第n次折痕的右邊

形成一棵二叉樹

image

中序遍歷該二叉樹就可得到答案

    public static void printAllFolds(int N) {
		printProcess(1, N, true);
	}

	public static void printProcess(int i, int N, boolean down) {
		if (i > N) {
			return;
		}
		printProcess(i + 1, N, true);
		System.out.println(down ? "down " : "up ");
		printProcess(i + 1, N, false);
	}

	public static void main(String[] args) {
		int N = 1;
		printAllFolds(N);
	}

對稱二叉樹

LeetCode 101. 對稱二叉樹

給定一個二叉樹,檢查它是否是鏡像對稱的。

 

例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。

    1
   / \
  2   2
 / \ / \
3  4 4  3
 

但是下面這個 [1,2,2,null,3,null,3] 則不是鏡像對稱的:

    1
   / \
  2   2
   \   \
   3    3
 

進階:

你可以運用遞歸和迭代兩種方法解決這個問題嗎?

法一:遞歸

  • 當前節點是祖宗根節點:比較左右子樹

  • 當前是左子樹根節點和右子樹根節點:比較左子樹的左子樹和右子樹的右子樹、左子樹的右子樹和右子樹的左子樹

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     * int val;
     * TreeNode left;
     * TreeNode right;
     * TreeNode() {}
     * TreeNode(int val) { this.val = val; }
     * TreeNode(int val, TreeNode left, TreeNode right) {
     * this.val = val;
     * this.left = left;
     * this.right = right;
     * }
     * }
     */
    class Solution {
        public boolean isSymmetric(TreeNode root) {
            return judge(root.left, root.right);
        }

        public boolean judge(TreeNode left, TreeNode right) {
            if (left == null && right != null) return false;
            if (left != null && right == null) return false;
            if (left == null && right == null) return true;
            if (left.val != right.val) return false;

            return judge(left.left, right.right) && judge(left.right, right.left);
        }
    }

法二:BFS

用一個隊列模擬層序遍歷,每次放入和彈出兩個元素

image

    class Solution {
        public boolean isSymmetric(TreeNode root) {
            Queue<TreeNode> queue = new LinkedList<>();
            queue.add(root.left);
            queue.add(root.right);
            while (!queue.isEmpty()) {
                TreeNode left = queue.poll();
                TreeNode right = queue.poll();
                if (left == null && right == null) continue; //true放在最后返回
                if (left == null || right == null || left.val != right.val) return false;

                queue.add(left.left);  //注意順序
                queue.add(right.right);
                queue.add(left.right);
                queue.add(right.left);
            }
            return true;
        }
    }

LeetCode 100. 相同的樹

給你兩棵二叉樹的根節點 p 和 q ,編寫一個函數來檢驗這兩棵樹是否相同。

如果兩個樹在結構上相同,並且節點具有相同的值,則認為它們是相同的。

 

示例 1:


輸入:p = [1,2,3], q = [1,2,3]
輸出:true
示例 2:


輸入:p = [1,2], q = [1,null,2]
輸出:false
示例 3:


輸入:p = [1,2,1], q = [1,1,2]
輸出:false
 

提示:

兩棵樹上的節點數目都在范圍 [0, 100] 內
-104 <= Node.val <= 104

法一:遞歸

    class Solution {
        public boolean isSameTree(TreeNode p, TreeNode q) {
            if (p == null && q != null) return false;
            if (p != null && q == null) return false;
            if (p == null && q == null) return true;
            if (p.val != q.val) return false;

            return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
        }
    }

法二:BFS

    class Solution {
        public boolean isSameTree(TreeNode p, TreeNode q) {
            Queue<TreeNode> queue = new LinkedList<>();
            queue.add(p);
            queue.add(q);
            while (!queue.isEmpty()) {
                TreeNode left = queue.poll();
                TreeNode right = queue.poll();
                if (left == null && right == null) continue;
                if (left == null || right == null || left.val != right.val) return false;
                queue.add(left.left);
                queue.add(right.left);
                queue.add(left.right);
                queue.add(right.right);
            }
            return true;
        }
    }

LeetCode 572. 另一棵樹的子樹

image

給你兩棵二叉樹 root 和 subRoot 。檢驗 root 中是否包含和 subRoot 具有相同結構和節點值的子樹。如果存在,返回 true ;否則,返回 false 。

二叉樹 tree 的一棵子樹包括 tree 的某個節點和這個節點的所有后代節點。tree 也可以看做它自身的一棵子樹。

 

示例 1:


輸入:root = [3,4,5,1,2], subRoot = [4,1,2]
輸出:true
示例 2:


輸入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
輸出:false
 

提示:

root 樹上的節點數量范圍是 [1, 2000]
subRoot 樹上的節點數量范圍是 [1, 1000]
-104 <= root.val <= 104
-104 <= subRoot.val <= 104

法一:遞歸

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        if (t == null) return true;   // t 為 null 一定都是 true
        if (s == null) return false;  // 這里 t 一定不為 null, 只要 s 為 null,肯定是 false
        return isSubtree(s.left, t) || isSubtree(s.right, t) || isSameTree(s,t);
    }

    /**
     * 判斷兩棵樹是否相同
     */
    public boolean isSameTree(TreeNode s, TreeNode t){
        if (s == null && t == null) return true;
        if (s == null || t == null) return false;
        if (s.val != t.val) return false;
        return isSameTree(s.left, t.left) && isSameTree(s.right, t.right);
    }
}

二叉樹最小深度

LeetCode 111. 二叉樹的最小深度

image

給定一個二叉樹,找出其最小深度。

最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。

說明:葉子節點是指沒有子節點的節點。

示例 1:


輸入:root = [3,9,20,null,null,15,7]
輸出:2
示例 2:

輸入:root = [2,null,3,null,4,null,5,null,6]
輸出:5


法一:后序遍歷

class Solution {
    /**
     * 遞歸法,相比求MaxDepth要復雜點
     * 因為最小深度是從根節點到最近**葉子節點**的最短路徑上的節點數量
     */
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDepth = minDepth(root.left);
        int rightDepth = minDepth(root.right);
        if (root.left == null) {
            return rightDepth + 1;
        }
        if (root.right == null) {
            return leftDepth + 1;
        }
        // 左右結點都不為null
        return Math.min(leftDepth, rightDepth) + 1;
    }
}

法二:層序遍歷

每一次層數加一,如果某節點左右子樹為空,則說明找到最小深度,返回result

class Solution {
   /**
     * 迭代法,層序遍歷
     */
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        Deque<TreeNode> deque = new LinkedList<>();
        deque.offer(root);
        int depth = 0;
        while (!deque.isEmpty()) {
            int size = deque.size();
            depth++;
            for (int i = 0; i < size; i++) {
                TreeNode poll = deque.poll();
                if (poll.left == null && poll.right == null) {
                    // 是葉子結點,直接返回depth,因為從上往下遍歷,所以該值就是最小值
                    return depth;
                }
                if (poll.left != null) {
                    deque.offer(poll.left);
                }
                if (poll.right != null) {
                    deque.offer(poll.right);
                }
            }
        }
        return depth;
    }
}

完全二叉樹的節點個數

LeetCode 222. 完全二叉樹的節點個數

image

給你一棵 完全二叉樹 的根節點 root ,求出該樹的節點個數。

完全二叉樹 的定義如下:在完全二叉樹中,除了最底層節點可能沒填滿外,其余每層節點數都達到最大值,並且最下面一層的節點都集中在該層最左邊的若干位置。若最底層為第 h 層,則該層包含 1~ 2h 個節點。

 

示例 1:


輸入:root = [1,2,3,4,5,6]
輸出:6
示例 2:

輸入:root = []
輸出:0
示例 3:

輸入:root = [1]
輸出:1
 

提示:

樹中節點的數目范圍是[0, 5 * 104]
0 <= Node.val <= 5 * 104
題目數據保證輸入的樹是 完全二叉樹

法一:遞歸

class Solution {
    // 通用遞歸解法
    public int countNodes(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

法二:二叉樹性質

class Solution {
    /**
     * 針對完全二叉樹的解法
     *
     * 滿二叉樹的結點數為:2^depth - 1
     */
    public int countNodes(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftDepth = getDepth(root.left);
        int rightDepth = getDepth(root.right);
        if (leftDepth == rightDepth) {// 左子樹是滿二叉樹
            // 2^leftDepth其實是 (2^leftDepth - 1) + 1 ,左子樹 + 根結點
            return (1 << leftDepth) + countNodes(root.right);
        } else {// 右子樹是滿二叉樹
            return (1 << rightDepth) + countNodes(root.left);
        }
    }

    private int getDepth(TreeNode root) {
        int depth = 0;
        while (root != null) {
            root = root.left;
            depth++;
        }
        return depth;
    }
}

二叉樹的序列化和反序列化

LeetCode 297. 二叉樹的序列化與反序列化

image

序列化是將一個數據結構或者對象轉換為連續的比特位的操作,進而可以將轉換后的數據存儲在一個文件或者內存中,同時也可以通過網絡傳輸到另一個計算機環境,采取相反方式重構得到原數據。

請設計一個算法來實現二叉樹的序列化與反序列化。這里不限定你的序列 / 反序列化算法執行邏輯,你只需要保證一個二叉樹可以被序列化為一個字符串並且將這個字符串反序列化為原始的樹結構。

提示: 輸入輸出格式與 LeetCode 目前使用的方式一致,詳情請參閱 LeetCode 序列化二叉樹的格式。你並非必須采取這種方式,你也可以采用其他的方法解決這個問題。

 

示例 1:


輸入:root = [1,2,3,null,null,4,5]
輸出:[1,2,3,null,null,4,5]
示例 2:

輸入:root = []
輸出:[]
示例 3:

輸入:root = [1]
輸出:[1]
示例 4:

輸入:root = [1,2]
輸出:[1,2]
 

提示:

樹中結點數在范圍 [0, 104] 內
-1000 <= Node.val <= 1000

法一:中序遍歷

public class Codec {
    int INF = -2000;
    TreeNode emptyNode = new TreeNode(INF);
    public String serialize(TreeNode root) {
        if (root == null) return "";

        StringBuilder sb = new StringBuilder();
        Deque<TreeNode> d = new ArrayDeque<>();
        d.addLast(root);
        while (!d.isEmpty()) {
            TreeNode poll = d.pollFirst();
            sb.append(poll.val + "_");
            if (!poll.equals(emptyNode)) {
                d.addLast(poll.left != null ? poll.left : emptyNode);
                d.addLast(poll.right != null ? poll.right : emptyNode);
            }
        }
        return sb.toString();
    }

    public TreeNode deserialize(String data) {
        if (data.equals("")) return null;

        String[] ss = data.split("_");
        int n = ss.length;
        TreeNode root = new TreeNode(Integer.parseInt(ss[0]));
        Deque<TreeNode> d = new ArrayDeque<>();
        d.addLast(root);
        for (int i = 1; i < n - 1; i += 2) {
            TreeNode poll = d.pollFirst();
            int a = Integer.parseInt(ss[i]), b = Integer.parseInt(ss[i + 1]);
            if (a != INF) {
                poll.left = new TreeNode(a);
                d.addLast(poll.left);
            }
            if (b != INF) {
                poll.right = new TreeNode(b);
                d.addLast(poll.right);
            }
        }
        return root;
    }
}

二叉搜索樹節點最小距離

LeetCode 783. 二叉搜索樹節點最小距離

image

給你一個二叉搜索樹的根節點 root ,返回 樹中任意兩不同節點值之間的最小差值 。

示例 1:


輸入:root = [4,2,6,1,3]
輸出:1
示例 2:


輸入:root = [1,0,48,null,null,12,49]
輸出:1
 

提示:

樹中節點數目在范圍 [2, 100] 內
0 <= Node.val <= 105
差值是一個正數,其數值等於兩值之差的絕對值

中序遍歷的過程中不斷更新距離最小值

class Solution {
    int ans = Integer.MAX_VALUE;
    TreeNode prev = null;
    public int minDiffInBST(TreeNode root) {

        Deque<TreeNode> d = new ArrayDeque<>();
        while (root != null || !d.isEmpty()) {
            while (root != null) {
                d.addLast(root);
                root = root.left;
            }
            root = d.pollLast();
            if (prev != null) {
                ans = Math.min(ans, Math.abs(prev.val - root.val));
            }
            prev = root;
            root = root.right;
        }

        return ans;
    }
}

二叉樹中所有距離為 K 的結點

LeetCode 863. 二叉樹中所有距離為 K 的結點

image

給定一個二叉樹(具有根結點 root), 一個目標結點 target ,和一個整數值 K 。

返回到目標結點 target 距離為 K 的所有結點的值的列表。 答案可以以任何順序返回。

 

示例 1:

輸入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
輸出:[7,4,1]
解釋:
所求結點為與目標結點(值為 5)距離為 2 的結點,
值分別為 7,4,以及 1



注意,輸入的 "root" 和 "target" 實際上是樹上的結點。
上面的輸入僅僅是對這些對象進行了序列化描述。
 

提示:

給定的樹是非空的。
樹上的每個結點都具有唯一的值 0 <= node.val <= 500 。
目標結點 target 是樹上的結點。
0 <= K <= 1000.

法一:建圖+BFS

二叉樹一個節點最多有兩條邊連接到左孩子和右孩子,屬於稀疏圖,可以利用鄰接表存儲。

之后從target開始進行k次BFS即可,這里可以理解成擴散,每次擴散用vis數組記錄,以target為中心擴散k次以后就獲得了所有距離target為k的節點

這里鄰接表用到的結構是鏈式前向星。本質就是數組模擬鏈表,並且采用頭插法。

int[] he = new int[N], e = new int[M], ne = new int[M];
int idx;

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = he[a];
    he[a] = idx++;
}
  • idx 是用來對邊進行編號
  • he 數組:存儲是某個節點所對應的邊的集合(鏈表)的頭結點;
  • e 數組:由於訪問某一條邊指向的節點;
  • ne 數組:由於是以鏈表的形式進行存邊,該數組就是用於找到下一條邊,存儲下一條邊的索引;
class Solution {
    // 根據數據范圍最多有 501 個點,每個點最多有 2 條無向邊(兩個子節點)
    int N = 510, M = N * 4;
    int[] he = new int[N], e = new int[M], ne = new int[M];
    int idx;
    void add(int a, int b) {
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx++;
    }
    boolean[] vis = new boolean[N];
    public List<Integer> distanceK(TreeNode root, TreeNode t, int k) {
        List<Integer> ans = new ArrayList<>();
        Arrays.fill(he, -1);
        dfs(root);
        Deque<Integer> d = new ArrayDeque<>();
        d.addLast(t.val);
        vis[t.val] = true;
        while (!d.isEmpty() && k >= 0) {
            int size = d.size();
            while (size-- > 0) {
                int poll = d.pollFirst();
                if (k == 0) {
                    ans.add(poll);
                    continue;
                }
                for (int i = he[poll]; i != -1 ; i = ne[i]) {
                    int j = e[i];
                    if (!vis[j]) {
                        d.addLast(j);
                        vis[j] = true;
                    }
                }
            }
            k--;
        }
        return ans;
    }
    void dfs(TreeNode root) {
        if (root == null) return;
        if (root.left != null) {
            add(root.val, root.left.val);
            add(root.left.val, root.val);
            dfs(root.left);
        }
        if (root.right != null) {
            add(root.val, root.right.val);
            add(root.right.val, root.val);
            dfs(root.right);
        }
    }
}

二叉樹的所有路徑

LeetCode 257. 二叉樹的所有路徑

給定一個二叉樹,返回所有從根節點到葉子節點的路徑。

說明: 葉子節點是指沒有子節點的節點。

示例:

輸入:

   1
 /   \
2     3
 \
  5

輸出: ["1->2->5", "1->3"]

解釋: 所有根節點到葉子節點的路徑為: 1->2->5, 1->3

使用前序遍歷,當遇到葉子節點時進行結果處理,否則不斷添加節點值進行dfs,遇到葉子節點后進行回溯,每次移除list的最后一個元素。

class Solution {
    List<String> ans = new ArrayList<>();
    public List<String> binaryTreePaths(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        dfs(root,list);
        return ans;
    }
    public void dfs(TreeNode root,List<Integer> s){
        if(root == null){
            return;
        }
        s.add(root.val);
        if(root.left==null && root.right==null){
            StringBuilder sb = new StringBuilder("");
            for(int i = 0;i<s.size();i++){
                sb.append(s.get(i));
                if(i!=s.size()-1)
                    sb.append("->");
            }
            ans.add(sb.toString());
            return ;
        }
        if(root.left!=null){
            dfs(root.left,s);
            s.remove(s.size()-1);  //回溯
        }
        if(root.right!=null){
            dfs(root.right,s);
            s.remove(s.size()-1);  //回溯
        }
        
    }
}

左葉子之和

LeetCode 404. 左葉子之和

計算給定二叉樹的所有左葉子之和。

示例:

    3
   / \
  9  20
    /  \
   15   7

在這個二叉樹中,有兩個左葉子,分別是 9 和 15,所以返回 24

法一:遞歸

必須從父節點才能判斷為左葉子

class Solution {
    int ans = 0;
    public int sumOfLeftLeaves(TreeNode root) {
        if(root == null) return 0;
        int leftValue = sumOfLeftLeaves(root.left);
        int rightValue = sumOfLeftLeaves(root.right);
        int midValue = 0;
        if(root.left!=null && root.left.left == null && root.left.right == null){
            midValue = root.left.val;
        }
        return midValue + leftValue + rightValue;
    }
}

法二:層序遍歷BFS

class Solution {
    int ans = 0;

    public int sumOfLeftLeaves(TreeNode root) {
        level(root);
        return ans;
    }

    public void level(TreeNode root) {
        if (root != null) {
            Deque<TreeNode> q = new ArrayDeque<>();
            q.addLast(root);
            while (!q.isEmpty()) {
                TreeNode node = q.pollFirst();
                if (node.left != null) {
                    q.addLast(node.left);
                    if (node.left.left == null && node.left.right == null)
                        ans += node.left.val;
                }
                if (node.right != null) {
                    q.addLast(node.right);

                }
            }
        }
    }
}

路徑總和

LeetCode 112. 路徑總和

給你二叉樹的根節點 root 和一個表示目標和的整數 targetSum ,判斷該樹中是否存在 根節點到葉子節點 的路徑,這條路徑上所有節點值相加等於目標和 targetSum 。

葉子節點 是指沒有子節點的節點。

 

示例 1:


輸入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
輸出:true
示例 2:


輸入:root = [1,2,3], targetSum = 5
輸出:false
示例 3:

輸入:root = [1,2], targetSum = 0
輸出:false
 

提示:

樹中節點的數目在范圍 [0, 5000] 內
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000

法一:遞歸

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null) return false;
        targetSum -= root.val;
        if(root.left == null && root.right == null){
            return targetSum == 0;
        }
        if(root.left != null){
            boolean left = hasPathSum(root.left, targetSum);
            if(left != false) return true;
        }
        if(root.right != null){
            boolean right = hasPathSum(root.right, targetSum);
            if(right != false) return true;
        }
        return false;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM