zoukankan      html  css  js  c++  java
  • 专题2 二叉树(go)

    1、 需要记忆的部分:

         分治法的基本思想是将一个规模为n 的问题分解为k 个规模较小的子问题,这些子问题互相独立且与原问题相同。递归的解这些子问题,然后将各子问题的解合并得到原问题的解。

    对于二叉树问题,首先需要熟练记住二叉树的前序中序遍历的递归版本和迭代版本,后序也可以看一下,记住BFS的实现过程,归并排序,快速排序,二叉搜索树BST。

    完全二叉树:左右节点高度差不能超过1;

    二叉搜索树:root左边的节点都小于root,root右边的节点都大于root的值。判断标准是:中序遍历是一个增序列。

    AVL平衡二叉树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

    总结:

    1)二叉树问题基本都是考察递归,几乎所有的二叉树问题时间复杂度都是O(N),空间复杂度和二叉树的高度有关系,因为每一次递归都需要系统内存开一个栈空间进行存储,

    2)其中递归函数中使用void的函数是遍历版本,使用有返回值的函数是分治版本,使用全局变量会影响多线程,

    3)helper函数是在递归中使用的,因为原有的函数可能只返回一个值,而中间的递归过程需要返回多个值,此时就需要再写一个helper函数

    4)在求最大值问题时候,非法情况需要些root = NULL,此时返回一个负无穷大;

    再求最小值问题的时候,此时返回一个正无穷大。

    写分治自己的感悟是,先想想最简单的情况是怎么写的。

    二叉树问题思考过程:首先r思考递归基的情况root ==NULL,return;

    然后思考左右子树递归完有什么实际意义,然后分别进行处理;

    2、下面是二叉树问题的模板:

    1)遍历形式的模板;

    Template 1: Traverse

    2)divide and conquer模板

    View Code

    注意:this是指针,谁调用struct新建变量,this就指向这个变量,它是个指针记住,所以使用->;

    struct 后面接变量不加括号,最后需要加分号;,初始化方法有三种,列表初始化1,自己构造的不能使用new,再新建变量。

     struct resultType{
            int singlePath;
            int maxPath;
            // 1=>  resultType(int x,int y):singlePath(x),maxPath(y){ }
            // 2 => resultType(int x,int y){
            //     singlePath = x;
            //     maxPath = y;
            // }
              resultType(int x,int y){
                this -> singlePath = x;
                this -> maxPath = y;
            }
        };

    使用new的时候必须要记得,在自由分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针,所以new的时候,前面必须是指针。

    resultType* result = new resultType(0,INT_MIN);//必须要加*

     3、二叉树分治法题目

    深度优先搜索DFS:

      3.1  Lowest Common Ancestor

     https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/#/description

    思路:需要注意接口给出的变量里面必须要有root或者parent,不然该题没法做,

    1)有parent的题目,首先遍历得到两个节点的父亲节点,存在两个vector或者list中,然后分别进行遍历比较,寻找第一个不同的节点,就是所求。

    2)有root的题目,使用上述模板,注意分析,该节点是root就可以直接返回,a和b节点在左右子树,则返回root,只有一个节点在二叉树中,则直接返回该节点,都不在二叉树中,则返回NULL。

    注意递归基的时候,只有某个节点等于root节点,那么就已经直接返回这个节点,自己也是自己的祖先。

      // 在root为根的二叉树中找A,B的LCA:
        // 如果找到了就返回这个LCA
        // 如果只碰到A,就返回A
        // 如果只碰到B,就返回B
        // 如果都没有,就返回null
    /**
     * Definition for a binary tree node.
     * type TreeNode struct {
     *     Val int
     *     Left *TreeNode
     *     Right *TreeNode
     * }
     */
     func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
         if root == nil || root == p || root == q{
             return root
         }
      
         lTree := lowestCommonAncestor(root.Left, p, q)
         rTree := lowestCommonAncestor(root.Right, p, q)
         if lTree != nil && rTree != nil {
             return root
         }
         
         if lTree != nil {
             return lTree
         }
         if rTree != nil {
             return rTree
         }
         
         return nil
    }
    lowestCommonAncestor

    235. Lowest Common Ancestor of a Binary Search Tree

    https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/

    使用递归可以轻松解决此问题。对于此题我们可以分为三种情况讨论:

    1. P, Q都比root小,则LCA在左树,我们继续在左树中寻找LCA

    2. P, Q都比root大,则LCA在右树,我们继续在右树中寻找LCA

    3. 其它情况,表示P,Q在root两边,或者二者其一是root,或者都是root,这些情况表示root就是LCA,直接返回root即可。

    /**
     * Definition for a binary tree node.
     * type TreeNode struct {
     *     Val   int
     *     Left  *TreeNode
     *     Right *TreeNode
     * }
     */
    
    func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
        if root == nil || root == p || root == q {
            return root
        }
        
        if p.Val < root.Val && q.Val < root.Val {
            return lowestCommonAncestor(root.Left, p, q)
        } else if p.Val > root.Val && q.Val > root.Val {
            return lowestCommonAncestor(root.Right, p, q)
        } else {
            return root
        }
    }
    lowestCommonAncestor

    3.2 Maximum Depth of Binary Tree

    https://leetcode.com/problems/maximum-depth-of-binary-tree/#/description

    Given the root of a binary tree, return its maximum depth.
    A binary tree's maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

    思路:递归到左右子树计算,看哪边深度大,关键理解最后一步每次返回到上一层都需要将深度增一。

    根结点的深度是约定的,这道题目约定的是根节点的深度是1;根结点的深度是1,属于第1层。经过多少条边,深度就是多少。

    /**
     * Definition for a binary tree node.
     * type TreeNode struct {
     *     Val int
     *     Left *TreeNode
     *     Right *TreeNode
     * }
     */
    func maxDepth(root *TreeNode) int {
        if root == nil {
            return 0
        }
        
        left := maxDepth(root.Left)
        right := maxDepth(root.Right)
        
        if left > right {
            return left + 1
        }
        return right + 1
    }
    maxDepth

    111. Minimum Depth of Binary Tree

    https://leetcode.com/problems/minimum-depth-of-binary-tree/description/

    这道题是树的题目,其实跟Maximum Depth of Binary Tree非常类似,只是这道题因为是判断最小深度,所以必须增加一个叶子的判断(因为如果一个节点如果只有左子树或者右子树,我们不能取它左右子树中小的作为深度,因为那样会是0,我们只有在叶子节点才能判断深度,而在求最大深度的时候,因为一定会取大的那个,所以不会有这个问题)。这道题同样是递归和非递归的解法,递归解法比较常规的思路,比Maximum Depth of Binary Tree多加一个左右子树的判断.

    /**
     * Definition for a binary tree node.
     * type TreeNode struct {
     *     Val int
     *     Left *TreeNode
     *     Right *TreeNode
     * }
     */
    func minDepth(root *TreeNode) int {
        if root == nil {
            return 0
        }
        
        if root.Left == nil {
            return minDepth(root.Right) + 1
        }
        
        if root.Right == nil {
            return minDepth(root.Left) + 1
        }
        
        return min(minDepth(root.Left), minDepth(root.Right)) + 1
    }
    
    func min(a, b int) int {
        if a < b {
            return a
        }
        return b
    }
    minDepth

    3.3 Balanced Binary Tree

    https://leetcode.com/problems/balanced-binary-tree/#/description

    思路:基于二叉树最大深度那题,当左右子树深度大于1的时候深度返回-1这步是关键

    /**
     * Definition for a binary tree node.
     * type TreeNode struct {
     *     Val int
     *     Left *TreeNode
     *     Right *TreeNode
     * }
     */
    func maxDepth(root *TreeNode) int {
        if root == nil {
            return 0
        }  
        //不平衡的时候用-1表示
        left := maxDepth(root.Left)
        right := maxDepth(root.Right)
        
        if left == -1 || right == -1 || abs(left - right) > 1 {
            return -1
        }
        
        return max(left, right) + 1
    }
    
    func max(a, b int) int {
        if a < b {
            return b
        }
        return a
    }
    
    func abs(c int) int {
        if c < 0 {
            return -c
        }
        return c
    }
    
    func isBalanced(root *TreeNode) bool {
        if root == nil {
            return true
        }
        
        return maxDepth(root) != -1
    }
    isBalanced

    4 宽度优先搜搜BFS

    4,.1  Binary Tree Level Order Traversal (*)

    https://leetcode.com/problems/binary-tree-level-order-traversal/#/description

    Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

    思路:二叉树的层次遍历就是宽度优先搜索,总共有三种实现方式,

    - 2 Queues(一个存parent,一个存儿子节点,每次都需要清空一个queue)
    - 1 Queue + Dummy Node(每个虚拟节点代表该层结束)
    - 1 Queue (best)

    这里需要注意,首先将root节点入栈,接下来是双循环,外层是队列不为空,内层是遍历每一层,进入内层循环之前要使用一个int记录队列的大小,代表该层节点个数。

    /**
     * Definition for a binary tree node.
     * type TreeNode struct {
     *     Val int
     *     Left *TreeNode
     *     Right *TreeNode
     * }
     */
    func levelOrder(root *TreeNode) [][]int {
        if root == nil {
            return [][]int{}
        }
        queue := []*TreeNode{root}
        res := [][]int{}
        
        for len(queue) != 0 {
            curRes := []int{}
            newQueue := []*TreeNode{}
            
            for _, node := range queue {
                curRes = append(curRes, node.Val)
                if node.Left != nil {
                    newQueue = append(newQueue, node.Left)
                }
                if node.Right != nil {
                    newQueue = append(newQueue, node.Right)
                }
            }
            
            res = append(res, curRes) 
            queue = newQueue
        }
        
        return res
    }
    层次遍历levelOrder

    5 二叉搜索树 Binary Search Tree

     5.1 Validate Binary Search Tree

    https://leetcode.com/problems/validate-binary-search-tree/#/description

    Given the root of a binary tree, determine if it is a valid binary search tree (BST).

    A valid BST is defined as follows:

    • The left subtree of a node contains only nodes with keys less than the node's key.
    • The right subtree of a node contains only nodes with keys greater than the node's key.
    • Both the left and right subtrees must also be binary search trees.

    思路:验证是否是二叉搜索树,使用中序遍历,如果是递增序列,那么就是二叉搜索树。平衡二叉树:root为空也是二叉搜索树,每个元素不相等,左边小于root,右边大于root;

    切片的传递,&arr,这样才能修改切片的值,引用类型指的是引用底层的数组。

    /**
     * Definition for a binary tree node.
     * type TreeNode struct {
     *     Val int
     *     Left *TreeNode
     *     Right *TreeNode
     * }
     */
    func helper(root *TreeNode, res *[]int) {
        if root != nil {
            helper(root.Left, res)
            *res = append(*res, root.Val)
            helper(root.Right, res)
        }
    }
    
    func isValidBST(root *TreeNode) bool {
        if root == nil {
            return true
        }
        
        res := []int{}
        helper(root, &res)
        
        for i:= 0; i < len(res) - 1;i++ {
            if res[i] >= res[i + 1] {
                return false
            }
        }
        
        return true
    }
    isValidBST

    5.2 Insert Node in a Binary Search Tree 

    http://www.lintcode.com/en/problem/insert-node-in-a-binary-search-tree/

    思路:记住需要插入的节点一定是在二叉树某个叶子节点下面插入的(这里是理解重点),就是递归到递归基的时候,还有就是记得左右递归的if条件。

    insert Node

    5.3 Search Range in Binary Search Tree

    http://www.lintcode.com/en/problem/search-range-in-binary-search-tree/

    思路:注意递归的时候如果比k1大,就进入左子树,如果比k2小,就进行右子树。在两者之间,就压入result数组。

      

    search Range

     6.124. Binary Tree Maximum Path Sum

    https://leetcode.com/problems/binary-tree-maximum-path-sum/#/description

    思路:先思考一个简化版的情况,然后再思考该题。该题最大路径和可能在左子树,可能在右子树,可能是左子树的节点经过root然后到右子树的节点。

    root等于空的时候是非法情况,求最大就需要返回最小,求最小就需要返回最大值。自己定义一个struct,里面包含两个值,一个是简化版的single path,一个是该题的max path。。

    接下来进行分治,首先分:想左子树右子树递归完分别希望得到什么结果,接下来治:分别求出single path 和max path。

    使用new的版本
    maxPath

    7.129. Sum Root to Leaf Numbers

    https://leetcode.com/problems/sum-root-to-leaf-numbers/#/description

    You are given the root of a binary tree containing digits from 0 to 9 only.

    Each root-to-leaf path in the tree represents a number.

    • For example, the root-to-leaf path 1 -> 2 -> 3 represents the number 123.

    Return the total sum of all root-to-leaf numbers.

    A leaf node is a node with no children.

    The root-to-leaf path 1->2 represents the number 12.
    The root-to-leaf path 1->3 represents the number 13.

    Return the sum = 12 + 13 = 25.

    思路:这题还是不怎么理解,采用的是自顶向下的递归设计,所以sum = 目前值加上10倍前面的值,最后返回的时候需要将左右子树的值相加放回。因为每次都需要将sum传递给下一个递归函数,所以首先对sum进行计算,已经到达叶子节点则该路径的值以及计算出来,需要将值返回,不是叶子结点的话,就需要将左右子树的值加起来返回。

    func sumNumbers(root *TreeNode) int {
        sum := 0
        
        var s func(*TreeNode, int)
        
        s = func(root *TreeNode, total int) {
            if root == nil {return}
            
            if root.Left == nil && root.Right == nil {
                sum += (total * 10) + root.Val       
                return
            }
            
            total = ((total * 10) + root.Val)
            
            s(root.Left, total)
            s(root.Right, total)        
        }
        
        s(root, 0)
        
        return sum
    }
    sumNumbers

    8.112. Path Sum

    https://leetcode.com/problems/path-sum/#/description

    是否存在一条路径等于指定的sum

    /**
     * Definition for a binary tree node.
     * type TreeNode struct {
     *     Val int
     *     Left *TreeNode
     *     Right *TreeNode
     * }
     */
    func hasPathSum(root *TreeNode, targetSum int) bool {
        if root == nil {
            return false
        }
        if root.Left == nil && root.Right == nil {
            return root.Val == targetSum
        }
        return hasPathSum(root.Left, targetSum - root.Val) || 
        hasPathSum(root.Right, targetSum - root.Val)
    }
    hasPathSum

    9.173. Binary Search Tree Iterator

    https://leetcode.com/problems/binary-search-tree-iterator/#/description

    Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST.

    Calling next() will return the next smallest number in the BST.

    Note: next() and hasNext() should run in average O(1) time and uses O(h) memory, where h is the height of the tree.

    思路:其实就是中序遍历迭代版,这题要看清题目,是二叉搜索树,左<中<右;遍历到最左边就是最小的元素。

    https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/er-cha-shu-de-zhong-xu-bian-li-by-leetcode-solutio/

    type BSTIterator struct {
        stack []*TreeNode
        cur   *TreeNode
    }
    
    func Constructor(root *TreeNode) BSTIterator {
        return BSTIterator{cur: root}
    }
    
    func (it *BSTIterator) Next() int {
        for node := it.cur; node != nil; node = node.Left {
            it.stack = append(it.stack, node)
        }
        it.cur, it.stack = it.stack[len(it.stack)-1], it.stack[:len(it.stack)-1]
        val := it.cur.Val
        it.cur = it.cur.Right
        return val
    }
    
    func (it *BSTIterator) HasNext() bool {
        return it.cur != nil || len(it.stack) > 0
    }
    BSTIterator
  • 相关阅读:
    iaas,paas,saas理解
    July 06th. 2018, Week 27th. Friday
    July 05th. 2018, Week 27th. Thursday
    July 04th. 2018, Week 27th. Wednesday
    July 03rd. 2018, Week 27th. Tuesday
    July 02nd. 2018, Week 27th. Monday
    July 01st. 2018, Week 27th. Sunday
    June 30th. 2018, Week 26th. Saturday
    June 29th. 2018, Week 26th. Friday
    June 28th. 2018, Week 26th. Thursday
  • 原文地址:https://www.cnblogs.com/dingxiaoqiang/p/14598932.html
Copyright © 2011-2022 走看看