【问题标题】:Is this not an O(n) algorithm?这不是 O(n) 算法吗?
【发布时间】:2017-02-10 20:07:05
【问题描述】:

我试图弄清楚为什么我的算法通过了所有不超时的测试用例。据我所知,它是一个 O(n) 算法,因为它是一系列 O(n) 算法的执行。这让我很好奇为什么它会超时。我想不出一种方法来显着减少这里涉及的操作数量(我认为通过使用更精简的数据结构进行轻微操作,但这并没有降低复杂性)。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

/// <summary>
/// 
/// Solution to https://www.hackerrank.com/challenges/cut-the-tree
/// 
/// Explanation of algorithm:
/// 
/// Given a tree like 
/// 
///       Val=100
///           \
///         Val=200
///           /   \
///          /     Val=100
///      Val=100
///     /       \
///   Val=500    Val=600
///
/// set a field for each node showing the sum of the values
/// in the subtree whose root is that node, making it into
/// 
///       Val=100
///       Sum=1600
///           \
///         Val=200
///         Sum=1500
///          /   \
///         /     Val=100
///        /      Sum=100
///      Val=100
///      Sum=1200
///     /        \
///   Val=500    Val=600
///   Sum=500    Sum=600
/// 
/// Then we can easily find minimum difference between the sum of
/// two trees that result from severing a branch: if the root node
/// is R and we sever node N, then the difference between the two
/// sums is |R.Sum - 2 * N.Sum|. 
///
/// </summary>

class Node
{
    public int Val { get; set; }
    public Node Parent { get; set; } = null;
    public List<Node> Neighbors { get; set; } = new List<Node>();

    /// <summary>
    /// Sum of values in descendant nodes
    /// </summary>   
    public int DescendantsSum { get; set; } = 0;

    /// <summary>
    /// Sum of values in tree whose root is this node
    /// </summary>
    public int TreeSum { get { return Val + DescendantsSum; } }
}

class Solution
{
    /// <summary>
    /// Builds the parent relation between nodes
    /// Complexity: O(n) where n is the number of nodes
    /// </summary>
    static Node BuildToTree(Node[] nodes)
    {
        Node root = nodes[0]; // use arbitrary node as the root 

        var Q = new Queue<Node>();
        Q.Enqueue(root);
        while(Q.Count > 0)
        {
            var current = Q.Dequeue();
            foreach(var neighbor in current.Neighbors.Where(nbr => nbr != current.Parent && nbr.Parent == null))
            {
                neighbor.Parent = current;
                Q.Enqueue(neighbor);
            }
        }
        return root;
    }

    /// <summary>
    /// Sets the sums of the descendant trees of each node
    /// Complexity: O(n) where n is the number of nodes
    /// </summary>
    static void SetSums(Node[] nodes)
    {
        foreach(var node in nodes)
            for (var parent = node.Parent; parent != null; parent = parent.Parent)
                parent.DescendantsSum += node.Val;
    }

    /// <summary>
    /// Gets the minimum difference between the sum of
    /// two trees that result from severing a branch.
    /// </summary>
    static int MinDiff(Node[] nodes, Node root)
    {
        return nodes
                .Skip(1)
                .Min(node => Math.Abs(root.TreeSum - 2 * node.TreeSum));
    }

    static void Main(String[] args)
    {
        string curdir = Directory.GetCurrentDirectory();
        System.IO.StreamReader file = new System.IO.StreamReader(
            Path.GetFullPath(Path.Combine(curdir, @"..\..\", "TestFiles\\SampleInput.txt"))
        );
        int N = Int32.Parse(file.ReadLine());
        int[] vals = Array.ConvertAll(file.ReadLine().Split(' '), Int32.Parse);
        Node[] nodes = vals.Select(val => new Node() { Val = val }).ToArray();
        for (int i = 0, n = N - 1; i < n; ++i)
        {
            int[] pair = Array.ConvertAll(file.ReadLine().Split(' '), Int32.Parse);
            int p = pair[0] - 1, d = pair[1] - 1; 
            nodes[p].Neighbors.Add(nodes[d]);
            nodes[d].Neighbors.Add(nodes[p]);
        }
        Node root = BuildToTree(nodes);
        SetSums(nodes);
        Console.WriteLine(MinDiff(nodes, root));
    }
}

【问题讨论】:

  • O(n) 算法可能会超时,如果它们足够慢的话。
  • 树在插入过程中通常是 log(n) 对吗?也许你想多了。
  • 强烈考虑将其移至Computer Science,其中时间复杂度是主要主题。那里的许多计算机科学家应该能够帮助解决这类问题。如果您确实移动了它,请确保先从此处将其删除,然后再将其发布到那里;交叉发布严重不受欢迎。
  • 算法的复杂度与其执行时间几乎没有关系。无论对于列表中的每个成员,算法都会打印成员或生成斐波那契数列的第 100 万位,它仍然是 O(n)。
  • 如果我没看错的话。看起来好像您在这里计算所有节点的后代。这意味着您正在为每个兄弟节点重复计算祖先。这不在 O(n) 中。您希望使用与遍历树并查找叶节点相同的算法。对向上遍历的每个节点求和一次。 (尽管这也不在 O(n) 中,除非只有 1 个分支。)

标签: c# algorithm tree time-complexity


【解决方案1】:

您的 SetSums() 函数为 O(n^2)(考虑一棵树,其中所有节点都链接到一个列表中)。您应该按后序或反向拓扑顺序遍历树,并根据其子节点的总和计算每个父节点的总和。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 2023-03-25
    • 1970-01-01
    相关资源
    最近更新 更多