【问题标题】:Minimizing memory consumption and execution time of Range Sum of Binary Tree最小化二叉树范围和的内存消耗和执行时间
【发布时间】:2020-11-01 20:20:07
【问题描述】:

我需要最小化内存消耗和计算二叉搜索树范围和的函数的执行时间 (https://leetcode.com/problems/range-sum-of-bst/)。

我目前的结果是:

运行时间:88 毫秒,比 69.00% 的 Go online 提交的 BST 范围和要快。

内存使用:7.9 MB,不到 BST Range Sum 的 Go online 提交的 5.67%。

我当前的代码:

func rangeSumBST(root *TreeNode, min int, max int) int {
    sum := 0
    arr := []*TreeNode{root}
    var node *TreeNode

    for len(arr) > 0 {
        node = arr[len(arr)-1]
        arr = arr[:len(arr)-1]
        
        if node.Val >= min && node.Val <= max {
            sum += node.Val
        }

        if node.Left != nil && node.Val > min {
            arr = append(arr, node.Left)
        }

        if node.Right != nil && node.Val < max {
            arr = append(arr, node.Right)
        }
    }

    return sum
}

我尝试以递归方式解决问题,这很优雅,但当然比迭代解决方案更慢且更占用内存。

我所拥有的迭代解决方案尽可能精简和简单。我声明并重用 node 变量,而不是在 for 循环中声明它。我将节点添加到切片的末尾而不是开头。

我还能做些什么来使其更快并使用更少的内存?还是有更有效的算法?或者是 Leetcode 以某种方式错误地测量了执行时间和内存消耗?

【问题讨论】:

  • 另外,问题陈述表明:1.树中的节点数最多为10000。2.最终答案保证小于2^31。它与可能的优化有关吗?
  • 朴素递归解决方案实际上更快(运行时间:84 毫秒,比 93.67% 的 BST 范围和的 Go 在线提交要快。)但消耗的内存略多。
  • 回答你应该分析代码以确定它在哪里花费时间。一种明显的优化是预先分配 arr 而不是分配 JIT。
  • 由于它是一个 BST,您可以使用 BST 的 Inorder Morris 遍历在 O(1) 空间复杂度中完成,因此您无法做得比 O(N) 时间复杂度更好。您当前的实现使用的是堆栈,因此在最坏的情况下,您当前的空间复杂度为 O(N),此时树基本上是一条路径。

标签: algorithm go binary-search-tree


【解决方案1】:

由于它是一个 BST,因此您可以使用 Inorder Morris 遍历 对 BST 进行 O(1) 空间复杂度,因此对于单个查询,您不能做得比 O(N) 时间复杂度更好,除非您有一些树本身的一种预处理。您当前的实现使用的是堆栈,因此在最坏的情况下,您当前的空间复杂度为 O(N),此时树基本上是一条路径。

Go 中的实现(能够超过 99%):

func rangeSumBST(root *TreeNode, min int, max int) int {
    if root == nil {
        return 0
    }
    
    var pre *TreeNode
    curr := root
    sum := 0
    for curr != nil {
        if curr.Left == nil {
            if curr.Val >= min && curr.Val <= max {
                sum += curr.Val
            }
            if curr.Val > max {
                break
            }
            curr = curr.Right
        } else {
            pre = curr.Left
            
            for pre.Right != nil && pre.Right != curr {
                pre = pre.Right
            }
            
            if pre.Right == nil {
                pre.Right = curr
                curr = curr.Left
            } else {
                pre.Right = nil
                if curr.Val >= min && curr.Val <= max {
                    sum += curr.Val
                }
                if curr.Val > max {
                    break
                }                
                curr = curr.Right
            }
            
        }
    }
    return sum
}

时间复杂度:O(节点数)

空间复杂度:O(1)

注意:不知何故,它并没有显示出内存性能的任何改善,可能是因为测试还不够,而且当测试不太大时,leetcode 会显示以前提交的解决方案的旧统计数据。

【讨论】:

    猜你喜欢
    • 2020-02-07
    • 1970-01-01
    • 2017-09-02
    • 2012-11-28
    • 2022-11-15
    • 2013-07-02
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多