这不是一个O(n)算法吗?

本文关键字:算法 一个 这不是 | 更新日期: 2023-09-27 18:09:51

我想弄清楚为什么我的算法通过了所有不超时的测试用例。据我所知,这是一个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)算法吗?

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