当前位置:网站首页>Binary tree

Binary tree

2022-07-07 23:11:00 Yake1965

Binary tree (Binary Tree)

Binary search tree

501. The mode in the binary search tree

The middle order traversal sequence of binary search tree is a non decreasing ordered sequence , The same number must be continuous .

class Solution {
    
    List<Integer> ans = new ArrayList<>();
    int base, cnt, max;
    public int[] findMode(TreeNode root) {
    
        dfs(root);
        int[] res = new int[ans.size()];
        for(int i = 0; i < ans.size(); i++) res[i] = ans.get(i);
        return res;
    }
    void dfs(TreeNode node){
    
        if(node == null) return;
        dfs(node.left);
        update(node.val);
        dfs(node.right);
    }
    void update(int x){
    
        if(x == base) cnt++;
        else {
    
            cnt = 1;
            base = x;
        }
        if(cnt == max) ans.add(base);
        else if(cnt > max){
    
            max = cnt;
            ans.clear();
            ans.add(base);
        }
    }
}

use Morris The method of middle order traversal optimizes the space complexity of middle order traversal to O(1).

The finger of the sword Offer 54. The second of binary search tree k Big node

The middle order traversal of binary search tree is Increasing sequence , That is to say The middle order traverses the reverse order by Decreasing sequence .
 Insert picture description here

class Solution {
       
    int k, ans = 0; 
    public int kthLargest(TreeNode root, int k) {
            
        this.k = k;
        dfs(root);
        return ans;
    }
    void dfs(TreeNode root) {
     
        if(root == null) return;
        dfs(root.right);
        if(--k == 0) {
    
            ans = root.val; return;
        }
        dfs(root.left);    
    }
}

class Solution {
    
    public int kthLargest(TreeNode root, int k) {
    
        Deque<TreeNode> q = new ArrayDeque<>();
        // while(q.size() > 0 || root != null){
    
        while(true){
    
            while(root != null){
    
                q.push(root);
                root = root.right;
            }
            root = q.pop();            
            if(--k == 0) return root.val;
            root = root.left;
        }
        // return 0;
    }
}

The finger of the sword Offer 36. Binary search tree and double linked list

class Solution {
    
    Node pre, head;
    public Node treeToDoublyList(Node root) {
    
        if(root == null) return null;
        dfs(root);
        head.left = pre;
        pre.right = head;
        return head;
    }
    void dfs(Node cur){
    
        if(cur == null) return;
        dfs(cur.left);
        if(pre != null) pre.right = cur;
        else head = cur;
        cur.left = pre;
        pre = cur;
        dfs(cur.right);
    }
}

96. Different binary search trees

class Solution {
    
    int[] a = new int[20]; // a[0] = 1; a[1] = 1;
    public int numTrees(int n) {
    
        if(n == 0 || n == 1) return 1;  //  Multiplying left and right requires  t[0] = 1
        if(a[n] > 0) return a[n];
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
    
            //  With  i  Recurse around for the root , Multiply .
            int left = numTrees(i-1);
            int right = numTrees(n-i);
            cnt += left * right;
        }
        a[n] = cnt;
        return cnt;
    }
}

95. Different binary search trees II

class Solution {
        
    public List<TreeNode> generateTrees(int n) {
    
        return f(1, n);
    }
    List<TreeNode> f(int s, int e){
    
        List<TreeNode> ans = new LinkedList();
        if(s > e){
    
            ans.add(null);
            return ans;
        }
        for(int i = s; i <= e; i++){
    
            List<TreeNode> left = f(s, i - 1);
            List<TreeNode> right = f(i + 1, e);
            for(TreeNode l : left){
    
                for(TreeNode r : right){
    
                    TreeNode cur = new TreeNode(i);
                    cur.left = l;
                    cur.right = r;
                    ans.add(cur);
                }
            }             
        }
        return ans;
    }
}

The finger of the sword Offer 34. The path of a value in a binary tree

113. The sum of the paths II

class Solution {
    
    List<List<Integer>> ans = new LinkedList<>();
    Deque<Integer> path = new ArrayDeque<>();
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        
        dfs(root, target);
        return ans;
    }
    void dfs(TreeNode root, int target){
    
        if(root == null) return;
        path.add(root.val);
        target -= root.val;
        if(root.left == null && root.right == null && target == 0){
    
            ans.add(new LinkedList<Integer>(path));
        }   
        dfs(root.left, target);
        dfs(root.right, target);
        path.pollLast();
    }
}

1609. Even tree

class Solution {
    
    public boolean isEvenOddTree(TreeNode root) {
    
        Deque<TreeNode> q = new LinkedList<>();
        q.offer(root);
        boolean even = true;
        
        while(!q.isEmpty()){
    
            int size = q.size(), pre = (even) ? 0 : 1000000;           
            // for(int i = 0; i < q.size(); i++){ //  Can't be used directly  q.size() 
            while(size-- > 0){
                
                TreeNode node = q.poll();
                int x = node.val;
                if(even){
    
                    //  Even subscript ,x  Is even or decreasing 
                    if(x % 2 == 0 || pre >= x) return false;
                }
                else{
      
                    //  Odd subscript ,x  Is odd or increasing  
                    if(x % 2 == 1 || pre <= x) return false;
                }
                pre = x;
                if(node.left != null) q.offer(node.left);          
                if(node.right != null) q.offer(node.right);
            }
            even = !even;  
        }
        return true;
    }
}

1609. Even tree

Leetcode

class Solution:
    def isEvenOddTree(self, root: Optional[TreeNode]) -> bool:
          
        q = deque([root])
        even = True
        while q:
            pre = 0 if even else 1000000      
            for i in range(len(q)): 
                node = q.popleft()
                x = node.val
                if even:
                    if not x % 2 or x <= pre: return False
                else:
                     if x % 2 or x >= pre: return False
                pre = x
                
                node.left and q.append(node.left)
                node.right and q.append(node.right)
            even = not even           
           
        return True

100. Same tree

Leetcode

class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if not p and not q: return True
        if not p or not q or p.val != q.val: return False
        # return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)

        #  Middle preface 
        stackP, stackQ, curP, curQ = [p], [q], p.left, q.left
        while stackP or stackQ or curP or curQ:            
            if not curP and curQ or curP and not curQ: return False
            while curP and curQ:
                stackP.append(curP)
                stackQ.append(curQ)
                curP = curP.left                
                curQ = curQ.left
            
            curP = stackP.pop()
            curQ = stackQ.pop()
            
            if curP.val != curQ.val:
                return False

            curP = curP.right
            curQ = curQ.right

        return True

101. Symmetric binary tree

Leetcode

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:        
        # q = [root, root]
        q = [(root, root)]
        while q:
            # u = q.pop()
            # v = q.pop()
            u, v = q.pop()
            if not u and not v: continue
            if not u or not v or u.val != v.val:
                return False

            # q.extend([u.left, v.right]) 
            # q.extend([u.right, v.left])
            q.append((u.left, v.right))            
            q.append((u.right, v.left))

        return True 

104. The maximum depth of a binary tree

Leetcode
The depth of a binary tree node : Refers to the longest simple path from the root node to this node edge The article number .
The height of a binary tree node : Refers to the longest simple path from this node to the leaf node edge The article number .

explain :leetcode Define the node as one degree , The root node depth is 1; Wikipedia Define the edge as one degree , The depth of the root node is 0.

The depth can be checked from top to bottom , So we need preorder traversal ( Around the middle ), And the height can only be checked from bottom to top , So it can only be traversed by postorder ( Right and left ).

The former sequence traversal

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        
        def getDepth(node, depth):  
            nonlocal result      
            result = max(depth, result) #  in 

            node.left and getDepth(node.left, depth + 1) #  Left 
            node.right and getDepth(node.right, depth + 1) #  Right 

        result = 0
        if not root: return 0
        getDepth(root, 1)
        return result

The maximum depth of a binary tree , That is, the height of the root node , So you can also use post order traversal .

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root: return 0

        ## bfs deque
        # que = collections.deque([(root, 1)])

        # while que:
        # node, depth = que.popleft()
        # res = depth # max(res, depth) 
        # node.left and que.append((node.left, depth + 1))
        # node.right and que.append((node.right, depth + 1))
        
        # return res #  You don't need variables  res, Go straight back to  depth  that will do .
       
        ## bfs list
        q = [root]
        res = 0
        while q:
            q = [child for node in q for child in [node.left, node.right] if child]
            res += 1            
            
        return res

        ## dfs
        # return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1 
        # return max(self.maxDepth(node) for node in [root.left, root.right]) + 1 

108. Convert an ordered array to a binary search tree

Leetcode

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if not nums: return None 
        mid = len(nums) // 2 #  Select the value between 
        root = TreeNode(nums[mid])
        if mid >= 1:
            root.left = self.sortedArrayToBST(nums[:mid])
        if mid < len(nums) - 1:
            root.right = self.sortedArrayToBST(nums[mid+1:])
        
        return root

110. Balanced binary trees

Leetcode
Balanced binary trees : A binary tree, each node The absolute value of the height difference between the left and right subtrees is not more than 1 .

Method 1 : Top down recursion

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
    	#  Maximum height of subtree 
        def height(root: TreeNode) -> int:
            return max(height(root.left), height(root.right)) + 1 if root else 0

        return not root or abs(height(root.left) - height(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right) 

Method 2 : Bottom up recursion

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def height(root: TreeNode) -> int:
            if not root: return 0
            left = height(root.left)
            right = height(root.right)
            return -1 if left == -1 or right == -1 or abs(left - right) > 1 else max(left, right) + 1

        return height(root) >= 0

111. Minimum depth of binary tree

Leetcode

Method 1 : Depth-first search

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root: return 0        
        if not root.left and not root.right: return 1
        
        min_depth = 10**9
        if root.left:
            min_depth = min(self.minDepth(root.left), min_depth)
        if root.right:
            min_depth = min(self.minDepth(root.right), min_depth)
        
        return min_depth + 1

Method 2 : Breadth first search

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root: return 0

        que = collections.deque([(root, 1)])
        while que:
            node, depth = que.popleft()
            if not node.left and not node.right:  #  leaf node 
                return depth
            node.left and que.append((node.left, depth + 1))
            node.right and que.append((node.right, depth + 1))

230. Binary search tree K Small elements

Leetcode

Method 1 : In the sequence traversal

Binary search tree :

  • The left subtree of a node contains only the number less than the current node .
  • The right subtree of a node contains only a number greater than the current node .
  • All left and right subtrees themselves must also be binary search trees .

In the sequence traversal :
Visit according to The left subtree — Root node — Right subtree Traversing the binary tree ; When accessing its left and right subtrees , Traverse in the same way ; Until you walk through the whole tree .

「 Middle order traversal of binary trees 」 Reference resources **「94. Official solution to the problem of traversing the middle order of binary trees 」**
Use iterative methods , This way you can stop when you find the answer , You don't need to traverse the whole tree .

class Solution:
    def kthSmallest(self, root: TreeNode, k: int) -> int:
        stack = []
        while root or stack:
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            k -= 1
            if k == 0:
                return root.val
            root = root.right

Method 2 : Record the node number of subtree

Record the number of nodes of the subtree with each node as the root node , Directly find the k Small value .

class Bst:
    def __init__(self, root: TreeNode):
        self.root = root

        #  Count the number of nodes of the subtree with each node as the root node , And stored in the hash table 
        self._node_num = {
    }
        self._count_node_num(root)

    def kth_smallest(self, k: int):
        """ Returns the... In the binary search tree k Small elements """
        node = self.root
        while node:
            left = self._get_node_num(node.left)
            if left < k - 1:   #  The left tree is not enough to add the right tree 
                node = node.right
                k -= left + 1
            elif left == k - 1: #  The left tree is just one , Return node value .
                return node.val
            else:
                node = node.left #  Continue to the left tree 

    def _count_node_num(self, node) -> int:
        """ Statistics to node Is the number of nodes of the subtree of the root node """
        if not node:
            return 0
        self._node_num[node] = 1 + self._count_node_num(node.left) + self._count_node_num(node.right)
        return self._node_num[node]

    def _get_node_num(self, node) -> int:
        """ To get to node Is the number of nodes of the subtree of the root node """
        return self._node_num[node] if node is not None else 0
        # node is not None  differ  not node  because  node  May be  null


class Solution:
    def kthSmallest(self, root: TreeNode, k: int) -> int:
        bst = Bst(root)
        return bst.kth_smallest(k)

Method 3 : Balanced binary search trees (AVL Trees )

Balanced binary search trees :

  • The height difference between the left subtree and the right subtree of each node in the balanced binary search tree is at most 1;
  • The subtree of the balanced binary search tree is also a balanced binary search tree ;

A being n The height of a balanced binary search tree of nodes is O(logn).

The time complexity of searching binary search tree in method 2 is O(H), among H Is the height of the tree ; When the tree is a balanced tree , Minimum time complexity O(logN). therefore , On the basis of recording the number of nodes of the subtree , Transform binary search tree into balanced binary search tree , And maintain its balance during insert and delete operations .

among , Transform binary search tree into balanced binary search tree , You can refer to **「1382. Solve the official problem of balancing binary search tree 」**.

# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class AVL:
    """ Balanced binary search trees (AVL Trees ): Duplicate values are allowed """

    class Node:
        """ Balanced binary search tree node """
        __slots__ = ("val", "parent", "left", "right", "size", "height")

        def __init__(self, val, parent=None, left=None, right=None):
            self.val = val
            self.parent = parent
            self.left = left
            self.right = right
            self.height = 0  #  Node height : With  node  Is the height of the subtree of the root node ( Height definition : The height of leaf nodes is  0)
            self.size = 1  #  Number of node elements : With  node  Is the total number of nodes in the subtree of the root node 

    def __init__(self, vals):
        self.root = self._build(vals, 0, len(vals) - 1, None) if vals else None

    def _build(self, vals, l, r, parent):
        """ according to vals[l:r] Construct balanced binary search tree  ->  Return to root node """
        m = (l + r) // 2
        node = self.Node(vals[m], parent=parent)
        if l <= m - 1:
            node.left = self._build(vals, l, m - 1, parent=node)
        if m + 1 <= r:
            node.right = self._build(vals, m + 1, r, parent=node)
        self._recompute(node)
        return node

    def kth_smallest(self, k: int) -> int:
        """ Returns the... In the binary search tree k Small elements """
        node = self.root
        while node:
            left = self._get_size(node.left)
            if left < k - 1:
                node = node.right
                k -= left + 1
            elif left == k - 1:
                return node.val
            else:
                node = node.left

    def insert(self, v):
        """ Insert value is v New node of """
        if self.root is None:
            self.root = self.Node(v)
        else:
            #  Calculate the adding position of the new node 
            node = self._subtree_search(self.root, v)
            is_add_left = (v <= node.val)  #  Whether to add a new node to node Left child node of 
            if node.val == v:  #  If the value is v The node of already exists 
                if node.left:  #  The value is v The node of has left child nodes , Is added to the rightmost part of its left subtree 
                    node = self._subtree_last(node.left)
                    is_add_left = False
                else:  #  The value is v The node of does not have a left child node , Then it is added to its left child node 
                    is_add_left = True

            #  Add a new node 
            leaf = self.Node(v, parent=node)
            if is_add_left:
                node.left = leaf
            else:
                node.right = leaf

            self._rebalance(leaf)

    def delete(self, v) -> bool:
        """ The deletion value is v The node of  ->  Returns whether the node was successfully deleted """
        if self.root is None:
            return False

        node = self._subtree_search(self.root, v)
        if node.val != v:  #  No node to delete was found 
            return False

        #  Handle the situation that the current node has both left and right subtrees 
        #  If the left subtree is lower than the right subtree , Then replace the current node with the leftmost node of the right subtree , And remove the leftmost node of the right subtree 
        #  If the right subtree is lower than the left subtree , Then replace the current node with the rightmost node of the left subtree , And remove the rightmost node of the left subtree 
        if node.left and node.right:
            if node.left.height <= node.right.height:
                replacement = self._subtree_first(node.right)
            else:
                replacement = self._subtree_last(node.left)
            node.val = replacement.val
            node = replacement

        parent = node.parent
        self._delete(node)
        self._rebalance(parent)
        return True

    def _delete(self, node):
        """ Delete node p And replace it with its child nodes , node p At most, there can only be 1 Child nodes """
        if node.left and node.right:
            raise ValueError('node has two children')
        child = node.left if node.left else node.right
        if child is not None:
            child.parent = node.parent
        if node is self.root:
            self.root = child
        else:
            parent = node.parent
            if node is parent.left:
                parent.left = child
            else:
                parent.right = child
        node.parent = node

    def _subtree_search(self, node, v):
        """ In order to node The search value in the subtree of the root node is v The node of , If there is no value for v The node of , The return value is v The parent node of the location where the node of should be """
        if node.val < v and node.right is not None:
            return self._subtree_search(node.right, v)
        elif node.val > v and node.left is not None:
            return self._subtree_search(node.left, v)
        else:
            return node

    def _recompute(self, node):
        """ Recalculate node Height of nodes and number of elements """
        node.height = 1 + max(self._get_height(node.left), self._get_height(node.right))
        node.size = 1 + self._get_size(node.left) + self._get_size(node.right)

    def _rebalance(self, node):
        """ from node Node start ( contain node node ) Rebalance the binary tree up one by one , And update the node height and the number of elements """
        while node is not None:
            old_height, old_size = node.height, node.size
            if not self._is_balanced(node):
                node = self._restructure(self._tall_grandchild(node))
                self._recompute(node.left)
                self._recompute(node.right)
            self._recompute(node)
            if node.height == old_height and node.size == old_size:
                node = None  #  If the node height and the number of elements have not changed, there is no need to continue to adjust upward 
            else:
                node = node.parent

    def _is_balanced(self, node):
        """ Judge node Whether the nodes are balanced """
        return abs(self._get_height(node.left) - self._get_height(node.right)) <= 1

    def _tall_child(self, node):
        """ obtain node Subtree with higher node """
        if self._get_height(node.left) > self._get_height(node.right):
            return node.left
        else:
            return node.right

    def _tall_grandchild(self, node):
        """ obtain node The higher subtree in the subtree with higher nodes """
        child = self._tall_child(node)
        return self._tall_child(child)

    @staticmethod
    def _relink(parent, child, is_left):
        """ Reconnect parent and child nodes ( Child nodes are allowed to be empty )"""
        if is_left:
            parent.left = child
        else:
            parent.right = child
        if child is not None:
            child.parent = parent

    def _rotate(self, node):
        """ Rotation operation """
        parent = node.parent
        grandparent = parent.parent
        if grandparent is None:
            self.root = node
            node.parent = None
        else:
            self._relink(grandparent, node, parent == grandparent.left)

        if node == parent.left:
            self._relink(parent, node.right, True)
            self._relink(node, parent, False)
        else:
            self._relink(parent, node.left, False)
            self._relink(node, parent, True)

    def _restructure(self, node):
        """trinode operation """
        parent = node.parent
        grandparent = parent.parent

        if (node == parent.right) == (parent == grandparent.right):  #  Handle situations that require a rotation 
            self._rotate(parent)
            return parent
        else:  #  Handle situations that require two rotations : The first 1 One rotation is needed after one rotation 
            self._rotate(node)
            self._rotate(node)
            return node

    @staticmethod
    def _subtree_first(node):
        """ Return to node Is the th of the subtree of the root node 1 Elements """
        while node.left is not None:
            node = node.left
        return node

    @staticmethod
    def _subtree_last(node):
        """ Return to node Is the last of the subtree of the root node 1 Elements """
        while node.right is not None:
            node = node.right
        return node

    @staticmethod
    def _get_height(node) -> int:
        """ To get to node Is the height of the subtree of the root node """
        return node.height if node is not None else 0

    @staticmethod
    def _get_size(node) -> int:
        """ To get to node Is the number of nodes of the subtree of the root node """
        return node.size if node is not None else 0


class Solution:
    def kthSmallest(self, root: TreeNode, k: int) -> int:
        def inorder(node):
            if node.left:
                inorder(node.left)
            inorder_lst.append(node.val)
            if node.right:
                inorder(node.right)

        #  Middle order traversal generates a list of values 
        inorder_lst = []
        inorder(root)

        #  Construct balanced binary search tree 
        avl = AVL(inorder_lst)

        #  simulation 1000 Insert and delete operations 
        random_nums = [random.randint(0, 10001) for _ in range(1000)]
        for num in random_nums:
            avl.insert(num)
        random.shuffle(random_nums)  #  List disorder 
        for num in random_nums:
            avl.delete(num)

        return avl.kth_smallest(k)

559. N The maximum depth of the fork tree

Leetcode
Same as 104. The maximum depth of a binary tree

class Solution:
    def maxDepth(self, root: 'Node') -> int:
        res = 0
        if not root:return 0
        q = [root]
        while q:
            q = [child for node in q for child in node.children]
            res += 1
        
        return res
        # return max((self.maxDepth(child) for child in root.children), default=0) + 1 if root else 0

563. The slope of the binary tree

Leetcode

class Solution:
    def __init__(self):
        self.ans = 0

    def findTilt(self, root: TreeNode) -> int:
        #  return  node  The sum of subtrees , Accumulate the slope at the same time .
        #  You can use member variables , It can also be used.  nonlocal.
        def dfs(root):
            if not root: return 0
            sumLeft = dfs(root.left)
            sumRight = dfs(root.right)
            self.ans += abs(sumLeft - sumRight)
            return sumLeft + sumRight + root.val
        
        dfs(root)
        return self.ans

671. The second smallest node in a binary tree

Leetcode

Method 1 : Depth-first search

The value of the root node is the first minimum . If the second smallest value does not exist , The output -1, Set initial value ans = -1.

Suppose the current node is node, If node.val > root.val, also ans = -1 ( for the first time ) perhaps node.val < ans, So update ans.

Besides , If node.val > ans, Then the values of all child nodes of the current node are >= ans, There is no need to traverse the subtree .

Finally, after traversing the complete binary tree , return ans.

class Solution:
    def findSecondMinimumValue(self, root: TreeNode) -> int:
        ans = -1

        def dfs(node: TreeNode) -> None:
            nonlocal ans
            if not node or node.val >= ans > -1: return
            if node.val > root.val: # ans  First update   or   smaller  
                ans = node.val
            
            dfs(node.left)
            dfs(node.right)

        dfs(root)
        return ans

Implement... With collections , Judgment is simpler .

class Solution(object):
    def findSecondMinimumValue(self, root: TreeNode)--> int:
        res = set()
        def dfs(node):
            if not node: return
            
            if node.val != root.val:
                res.add(node.val)
                return
                
            dfs(node.left)
            dfs(node.right)
        
        dfs(root)
        res = sorted(res)
        return -1 if not res else res[0]

Method 2 :

The idea is simple , If the node is not empty , Save its value , The left and right nodes are not empty , Join the queue .

class Solution:
    def findSecondMinimumValue(self, root: TreeNode) -> int:
        s = set()
        queue = [root]
        while queue:
            node = queue.pop()
            if len(s)>1 and node.val >= sorted(s)[1]:continue #  Optimize : Remove the unqualified ones 
            s.add(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        
        return -1 if len(s) < 2 else sorted(s)[1]

Further optimization

class Solution:
    def findSecondMinimumValue(self, root: TreeNode) -> int:
        ans = -1
        queue = [root]
        while queue:
            node = queue.pop()
            # if len(s)>1 and node.val >= sorted(s)[1]:continue
            
            if node.val >= ans > -1: continue
            if node.val > root.val: # ans  First update   or   smaller  
                ans = node.val
            	continue
            	
            # if node.val > root.val:
            # # ans  Has not been reassigned (-1), The direct assignment is  node.val  Assigned , Minimum value 
            # ans = node.val if ans == -1 else min(ans, node.val)
            # continue

            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        
        return ans

Traversing the binary tree

A binary tree consists of root nodes (val)、 The left subtree (left) And right subtree (right) Three parts , If specified M、L、R Represent the val、left、right, Then the traversal way of binary tree is 6 Kind of :

MLR - MRL - LMR - LRM - RDL - RLD

Because first traverse left And traverse first right There is no essential difference in algorithm design , therefore , Just look at the most widely used 3 Kind of ( Left right after the first , It is more in line with our current writing order , It's good for understanding ) that will do (dfs: Deep optimization ), Of course, in addition to these methods , Traversal method and sequence traversal method (bfs: breadth-first ):

  • MLR The former sequence traversal : val -> left -> right
  • LMR In the sequence traversal :left -> val -> right
  • LRM After the sequence traversal :left -> right -> val

The above traversal method , Can be achieved by the following methods :

  • Recursive : recursive
  • Iteration : iteration
  • Morris : And the method derived to solve the problem of high spatial complexity in the above implementation scheme ( Morris Traverse )

By recursion ( front ) -> iteration ( in )-> Morris ( in ) In order to achieve

Preamble recursion val -> left -> right

class Solution:
    def findSecondMinimumValue(self, root: TreeNode) -> int:
        '''  According to the meaning :  The root of a node is the minimum of all its child nodes   Therefore, the root node is the minimum value in the binary tree  '''
        self.minValue = root.val
        self.res = -1

        # dfs -> preorder
        self.preorder(root)

        #  Returns the result value 
        return self.res
    
    ''' dfs -> inorder @param node '''
    def preorder(self, node: TreeNode) -> int:
        #  When the node is empty , No processing required 
        #  When  res  Assigned , And the current node  val  Greater than  res  when , Just go back 
        if not node or node.val >= self.res > -1: return    
        #  The current value is not equal to the minimum value , And more than before  res  smaller , to update  res  value 
        if node.val > self.minValue: self.res = node.val      

        #  recursive  left
        self.preorder(node.left)

        #  recursive  right
        self.preorder(node.right)

Medium order iteration left -> val -> right
This method is related to recursion in time , There is not much advantage in space , But it solves the risk of stack overflow if the recursion depth is too high .
 Insert picture description here

class Stack(object):
    """ Stack """
    def __init__(self): self.items = []
    def isEmpty(self):  return self.items == []
    def push(self, item):  self.items.append(item)
    def pop(self):  return self.items.pop()

class Solution:
    def findSecondMinimumValue(self, root: TreeNode) -> int:
        res, minValue = -1, root.val
        help = Stack()         
        help.push(root) #  First  root  Pressing stack 

        #  In the sequence traversal , First traverse  left, Define the current node as  root.left
        cur = root.left

        #  Stack pressing is performed only when the current node is not empty or the stack is not empty 
        while cur or not help.isEmpty():
            # cur  Not empty stack  
            while cur:
                help.push(cur)
                cur = cur.left        
            ''' root < left < left ... None > left ... < right < left ...  Every round   After pressing the stack ,cur  The current node is empty   Take the top node out of the stack  '''
            cur = help.pop()
            if cur.val > minValue:
                # res  Has not been reassigned (-1), The direct assignment is  cur.val  Assigned , Minimum value 
                res = cur.val if res == -1 else min(res, cur.val)
			#  Take the top node out of the stack to  cur, If  cur.right  Not empty for the next round of stack , If it is empty, continue to stack .
            cur = cur.right

        return res

Middle preface Morris

Morris Traversal is different from the above two methods in , There is no need for recursion or temporary stack space to assist in completing the middle order traversal , Spatial complexity : Constant level , O(1).

But in order to solve the problem from child Trees To parent Trees , You need to temporarily modify the structure of the tree , In other words, it will lose the data structure advantage brought by the tree itself , Just to complete the traversal , The defects brought about , But don't worry , Restore the pointer after use

In order to better understand the principle, we need the current cur and temporary predecessor 2 A pointer to the , The specific steps are as follows :

  • If the left subtree node is empty , Set the current node val Add to result set res in ( there res It's different from the code , there res To better understand
    Morris The process ),cur Point to its right subtree right node (cur = cur.right)

  • If the left subtree is not empty , Traverse the rightmost side of the left subtree right Child node (predecessor), In the process of searching, the following will appear 2 One scenario :

    • predecessor.right It's empty time , take right Pointer to cur node , cur Point to its left subtree node (cur = cur.leftcur=cur.left)

    • predecessor.right Isn't empty , Indicates that the original leaf node connection already exists ( Has traversed cur.left)

      • take predecessor.right = nullpredecessor.right=null
      • cur node val Add to result set res in res.add(cur.val)res.add(cur.val)
      • cur Point to Its right node (cur = cur.rightcur=cur.right)
  • Repeat above 2 Step until cur Node is empty

 Insert picture description here
The above process is for better understanding Morris Ergodic process
This question : According to the above res Result set , Find out the ratio root.val The second small value of large is enough , See the code comments for the specific process

class Solution:
    def findSecondMinimumValue(self, root: TreeNode) -> int:
        res, minValue = -1, root.val
        cur = root

        while cur:

        # cur  The left tree is empty , Put it  val  Judge , Compliance  res  The update logic of 
        #  If the conditions are met, the current node  val  Update to  res, , `cur`  Point to its right subtree  `right`  node  (cur = cur.right)

            if not cur.left:
                if cur.val > minValue:
                    res = cur.val if res == -1 else min(res, cur.val)
                cur = cur.right
            else:
                #  If the left subtree is not empty ,  look for  predecessor  node 
                predecessor = cur.left
                while predecessor.right and predecessor.right != cur:
                    predecessor = predecessor.right

                #  The following corresponds to  2  Kinds of scenes 
                # predecessor.right  It's empty 
                if not predecessor.right:
                    #  take  predecessor.right  And  cur  Building links 
                    predecessor.right = cur

                    #  The current node points to its  left  Trees 
                    cur = cur.left

                else:
                    if cur.val > minValue:

                        res = cur.val if res == -1 else min(res, cur.val)
                   
                    predecessor.right = None
                    cur = cur.right
  
        return res

700. Search in binary search tree

Leetcode

class Solution:
    def searchBST(self, root: TreeNode, val: int) -> TreeNode:
        ##  Method 1 :for
        cur = root
        while cur:
            if cur.val == val: return cur
            cur = cur.left if cur.val > val else cur.right
        return None
        
        ##  Method 2 : recursive 
        if not root: return None
        if root.val == val: return root
        return self.searchBST(root.left if root.val > val else root.right, val)

863. All distances in a binary tree are K The node of

Leetcode

Method 1 : Depth-first search + Hashtable

If the target As the root node of the tree , from target set out , Use depth first search with target A distance of k All of the nodes .

The binary tree does not record the parent node , From the root root set out , Use depth first search to traverse the whole tree , At the same time, a hash table is used to record the parent node of each node . The node value is unique , You can use the node value as the key of the hash table .

And then from target set out , Use depth first search to traverse the whole tree , Search left 、 Right and parent nodes .

Besides , In order to avoid repeated access to nodes during depth first search , Additional incoming source nodes frm.

class Solution:
    def distanceK(self, root: TreeNode, target: TreeNode, k: int) -> List[int]:
    	#  The values of all nodes correspond to the parent node  pre = {node.val:preNode}
        def findPre(node):
            # if node.left:
            # pre[node.left.val] = node
            # findPre(node.left)
                
            # if node.right:
            # pre[node.right.val] = node
            # findPre(node.right)

            for n in [node.left,node.right]:
                if n:
                    pre[n.val] = node
                    findPre(n)

        def findAns(node, frm, depth, k):
            if not node: return
            if depth == k:
                ans.append(node.val)
                return
          
            # if node.left != frm:
            # findAns(node.left, node, depth + 1, k)
                
            # if node.right != frm:
            # findAns(node.right, node, depth + 1, k)
                
            # if pre.get(node.val) != frm:
            # findAns(pre.get(node.val), node, depth + 1, k)
			
			# pre.get(node.val)  Parent node 
            for n in [node.left, node.right, pre.get(node.val)]:
                if n != frm:     
                    findAns(n, node, depth + 1, k)       

        pre = {
    } # key  The value of the node :value  Corresponding parent node 
        ans = []

        #  from  root  set out  DFS, Record the parent node of each node through the value of the node 
        findPre(root)

        #  from  target  set out  DFS, Find all depth  k  The node of 
        findAns(target, None, 0, k)

        return ans

Method 2 :

Ideas There are two situations :
1、 Traverse all child nodes of any node findNext()
Such as :5 => [6, 2, 7, 4] 7, 4 Match the answer
3 => [1, 0, 8] 1 Match the answer

2、 Traverse 5 All parent nodes , Just mark target Parent node of . That is to say target To root The path of .findPre()
When traversing parent nodes , Use it 1 Method traverses another branch .
Such as :5 => 3

class Solution:
    def distanceK(self, root: TreeNode, target: TreeNode, k: int) -> List[int]:
        
        # ⑵  Recursive traversal  node  Child nodes of 
        def findNext(node, depth):
            if not node: return
            if depth == k:
                res.append(node.val)
                return
            for n in [node.left, node.right]:
                if n:
                    findNext(n, depth + 1)
        
        # ⑶  Recursive traversal  node  Parent node , Process another branch at the same time , You need to mark the parent node first .
        def findPre(node, depth):
            # pre.get(node.val)  Parent node  p
            p = pre.get(node.val)   
            if not p: return #  Only  root.parents = None
            if depth + 1 == k:
                res.append(p.val)
                return

			#  use  findNext()  Search for another branch of the parent node  
            for x in [p.left, p.right]:
                if x != node:   #  Get another branch  node、x  yes  p  Left and right child nodes of . 
                    findNext(x, depth + 2)     
			
			#  Recursive traversal   Parent node 
            findPre(p, depth + 1)

        res = [] #  Qualified node value 
        pre = {
    } # key  Node values  : value  node  target  To  root  route 

        # ⑴  Mark only  k  The parent node is ok , There are many actual marks . 
        #  You can also use recursive methods , This is used here.  list.
        help = [root]        
        pre[root] = None       
	
        while help:
            node = help.pop()            
            for cur in [node.left, node.right]:
                if cur:
                    pre[cur.val] = node
                    help.append(cur)                   
                    if cur == target:break #  If you find  target  End 
        
        findPre(target, 0)
        findNext(target, 0)  

        return res  

1104. Binary trees find their way

Leetcode

Method 1 :

Left to right binary tree , The parent node is the child node // 2;

  • The first i Yes 2 i-1 Nodes , The node label is 2 i-1 to 2 i - 1;
  • For the label x The node of , When x > 1 when , The label of its parent node is x // 2 .

Press one dragon , The parent node is the child node symmetric node // 2.

  • Every line The sum of the labels of nodes and symmetric nodes is equal , Such as :15 ~ 8 node 14 The symmetric node of is 15 + 8 - 14 = 9
class Solution:
    def pathInZigZagTree(self, label: int) -> List[int]:

        row = int(math.log(label,2)) + 1
        ans = [0] * row        
        for i in range(row,0,-1):
            ans[i-1] = label
            label = (pow(2,i) - 1 - label + pow(2,i-1)) // 2
          
        return ans

Binary tree path

Depth-first search (DFS) And breadth first search (BFS)
On the left is BFS, Search by layer ; On the right of the picture is DFS, Go all the way first , And then go back and search .
 Insert picture description here

112. The sum of the paths

Leetcode
The sum of values on the path from root node to leaf node : Judge on the leaf node .

Method 1 : recursive DFS

class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root:return False
        if not root.left and not root.right: #  leaf node 
            return root.val == targetSum
        #  The problem is transformed into left-right subtree recursion 
        return self.hasPathSum(root.right, targetSum - root.val) or self.hasPathSum(root.left, targetSum - root.val) 

Method 2 : Breadth first search BFS

class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root: return False
        q = deque([(root, targetSum)]) #  Carry values through tuples 
        while q:
            cur, tem = q.popleft()
            tem -= cur.val
            if not cur.left and not cur.right: #  leaf node 
                if tem == 0: return True
                continue
                
            cur.left and q.append((cur.left, tem))  
            cur.right and q.append((cur.right, tem))      
        
        return False

113. The sum of the paths II

Leetcode

Method 1 :DFS

cclass Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:

        def dfs(cur, tem):
        	#  If  path  Pass in as a parameter , This is used here  path = path + cur.val, You don't have to  copy  And backtracking .
            path.append(cur.val) 
            tem -= cur.val

            if not (cur.left or cur.right or tem):
                res.append(path[:]) # copy
            
            cur.left and dfs(cur.left, tem)
            cur.right and dfs(cur.right, tem)
            path.pop() #  to flash back 

        if not root:return []
        res , path = [], []
        dfs(root, targetSum)
        
        return res

Method 2 :BFS

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        if not root: return [] 
        res = []
        q = deque([(root, [], targetSum)])  #  node , route , Path and 

        while q:
            cur, path, tem = q.popleft()
            # path += [cur.val]
            # path.append(cur.val) #  list  +=,append  The object remains the same  !!! 
            path = path + [cur.val] #  The object has changed 
            tem -= cur.val
            #  If it's a leaf node , Simultaneous sum is zero .
            if not cur.left and not cur.right and not tem: 
                    res.append(path) #  Save the path 

            cur.left and q.append((cur.left, path, tem))
            cur.right and q.append((cur.right, path, tem))

        return res

257. All paths of binary tree

Leetcode

DFS

One 、 Empty node processing

1、 Recursive hollow node returns

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        def dfs(node, path):
            if not node: return #  Empty node return 
            path += str(node.val)

            if not (node.left or node.right): #  leaf node 
                res.append(path)
                return
            
            dfs(node.left, path + '->')
            dfs(node.right, path + '->')
        
        res = []
        dfs(root, '')
        return res

2、 Judge the empty node , There is no empty node in recursion

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        def dfs(node, path):
            path += str(node.val)

            if not (node.left or node.right): #  leaf node 
                res.append(path)
            
            node.left and dfs(node.left, path + '->') #  Judge the empty node 
            node.right and dfs(node.right, path + '->')
        
        res = []
        dfs(root, '')
        return res

Two 、 Path global variables and parameters

Global variables need to be copied and traced , When the parameter is an immutable object, you do not need , Parameters are different , Above . The parameter is the list , Use path = path + [root.val] Additive elements , in fact path Has changed. .

1、path Defined as list

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        def dfs(node):
            path.append(node.val)

            if not (node.left or node.right): #  leaf node 
                res.append(path[:]) # copy
            
            node.left and dfs(node.left)
            node.right and dfs(node.right)
            path.pop() #  to flash back 
        path = []
        res = []
        dfs(root)
        return ['->'.join(map(str,x)) for x in res]

2、path Defined as str

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        def dfs(node):
            nonlocal path
            path += str(node.val) if node == root else  '->' + str(node.val)

            if not (node.left or node.right): #  leaf node 
                res.append(path) 
            
            node.left and dfs(node.left)
            node.right and dfs(node.right)
            path = path.rsplit('->', 1)[0] #  to flash back 

        path = ''
        res = []
        dfs(root)
        return res

BFS

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        q = deque([(root, str(root.val))]) #  Carry path tuples 
        res = []
        while q:
            cur, path = q.popleft()
            if not cur.left and not cur.right: #  leaf node 
                res.append(path) 

            cur.left and q.append((cur.left, path + '->' + str(cur.left.val)))
            cur.right and q.append((cur.right, path + '->' + str(cur.right.val)))

        return res   

437. The sum of the paths III

Leetcode

Method 1 : Double recursion

class Solution:
    def pathSum(self, root: TreeNode, targetSum: int) -> int:

        def calPathSum(root, sum):
            if not root: return 0
            tmp = 0
            sum -= root.val
            if sum == 0:                
                tmp += 1
            return tmp + calPathSum(root.left, sum) + calPathSum(root.right, sum)

        if not root: return 0
        return calPathSum(root, targetSum) + self.pathSum(root.left, targetSum) + self.pathSum(root.right, targetSum)

Method 2 :

class Solution:
    def pathSum(self, root: TreeNode, targetSum: int) -> int:
        def f(r, s):
            if r:
                s = [i + r.val for i in s] + [r.val]
                return s.count(targetSum) + f(r.left, s) + f(r.right, s)
            return 0
        return f(root, []) 

988. The smallest string starting from the leaf node

Leetcode

Method 1 :DFS

class Solution:
    def smallestFromLeaf(self, root: TreeNode) -> str:

        def dfs(node, A):
            if node:
                A.append(node.val)
                if not node.left and not node.right:
                    self.ans = min(self.ans, A[::-1]) 
                
                dfs(node.left, A)
                dfs(node.right, A)
                A.pop()

        self.ans = [27]
        dfs(root, [])
        return ''.join(map(lambda x:chr(x + ord('a')), self.ans))

Method 2 :BFS

class Solution:
    def smallestFromLeaf(self, root: TreeNode) -> str:
        res = []
        q = deque([(root, [])])
        while q:
            cur, path = q.popleft()
            path = [cur.val] + path
            if not cur.left and not cur.right:
                res.append(path)

            cur.left and q.append((cur.left, path))
            cur.right and q.append((cur.right, path))

        ans = min(res)
        return ''.join(map(lambda x:chr(x + 97), ans))
class Solution:
    def smallestFromLeaf(self, root: TreeNode) -> str:
        res = [27]
        q = deque([(root, [])])
        while q:
            cur, path = q.popleft()
            path = [cur.val] + path
            if not cur.left and not cur.right:
                res = min(res, path)

            cur.left and q.append((cur.left, path))
            cur.right and q.append((cur.right, path))

        return ''.join(map(lambda x:chr(x + 97), res))

To be sorted out …
124. The maximum path in a binary tree and
687. The longest path of the same value
543. The diameter of a binary tree

vector<vector> res;
vector<vector> pathSum(TreeNode *root, int targetSum)
{
vector path;
dfs(root, targetSum, path);
return res;
}

void dfs(TreeNode*root, int sum, vector path)
{
if (!root)
return;
sum -= root->val;
path.push_back(root->val);
if (!root->left && !root->right && sum == 0)
{
res.push_back(path);
return;
}
dfs(root->left, sum, path);
dfs(root->right, sum, path);
}
437. The sum of the paths III
Double recursion : First call dfs Function from root Start finding the path , Call again pathsum Function to root The left and right subtrees start searching
Don't pass

class Solution:   
    def __init__(self):
        self.count = 0

    def pathSum(self, root: TreeNode, sum: int) -> int:        
        @lru_cache(None)
        def dfs(node, sum):
            sum -= root.val
            #  Be careful not to. return, Because it is not required to end at the leaf node , So there may be another path under one path 
            if sum == 0: 
                self.count += 1  #  If a path global variable is found  + 1
            
            node.left and dfs(node.left, sum)
            node.right and dfs(node.right, sum)
        
        if not root: return 0
        
        dfs(root, sum) 
        self.pathSum(root.left, sum)
        self.pathSum(root.right, sum)
        return self.count
  1. The smallest string starting from the leaf node
    The soup does not change the dressing , Apply template 1

vector path;
string smallestFromLeaf(TreeNode *root)
{
dfs(root, “”);
sort(path.begin(), path.end()); // Ascending sort
return path[0];
}

void dfs(TreeNode *root, string s)
{
if (!root)
return;
s += ‘a’ + root->val;
if (!root->left && !root->right)
{
reverse(s.begin(), s.end()); // The topic requires from root node to leaf node , So reverse
path.push_back(s);
return;
}
dfs(root->left, s);
dfs(root->right, s);
}
Two 、 Not top-down
124. The maximum path in a binary tree and
/left,right They are the maximum paths of the left and right subtrees of the root node and , Be careful : If the maximum path and <0, It means that this path and make a negative contribution to the total path and , So don't count in the total path , Set it to 0

int res = INT_MIN; // Note that the node value may be negative , So set it to the minimum
int maxPathSum(TreeNode *root)
{
maxPath(root);
return res;
}

int maxPath(TreeNode *root) // With root The longest path that is the starting point of the path
{
if (!root)
return 0;
int left = max(maxPath(root->left), 0);
int right = max(maxPath(root->right), 0);
res = max(res, left + right + root->val); // Compare the current maximum path with the larger value of the longest path of the left and right subtrees plus the root node value , Update global variables
return max(left + root->val, right + root->val); // Return the long path of the left and right subtrees plus the root node value
}
687. The longest path of the same value

int longestUnivaluePath(TreeNode *root)
{
if (!root)
return 0;
longestPath(root);
return res;
}

int longestPath(TreeNode *root)
{
if (!root)
return 0;
int left = longestPath(root->left), right = longestPath(root->right);
// If the left child node and the root node have the same value , Update left longest path ; Otherwise, the left longest path is 0
if (root->left && root->val == root->left->val)
left++;
else
left = 0;
if (root->right && root->val == root->right->val)
right++;
else
right = 0;
res = max(res, left + right);
return max(left, right);
}
543. The diameter of a binary tree

int res1 = 0;
int diameterOfBinaryTree(TreeNode *root)
{
maxPath(root);
return res1;
}

int maxPath(TreeNode *root)
{
// Here, the recursive end condition should pay special attention to : It can't be !root( And there is no need to judge root It's empty , Because only non null will enter recursion ), Because the path length of a single node is also 0
if (!root->left && !root->right)
return 0;
int left = root->left ? maxPath(root->left) + 1 : 0; // Determine whether the left child node is empty , This updates the longest path on the left
int right = root->right ? maxPath(root->right) + 1 : 0;
res1 = max(res, left + right); // Update global variables
return max(left, right); // Return to the larger of the left and right paths
}

原网站

版权声明
本文为[Yake1965]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207072001212639.html