C# 这不是一个O(n)算法吗?
我试图弄明白为什么我的算法通过了所有没有超时的测试用例。据我所知,这是一个O(n)算法,因为它是一系列O(n)算法的执行。这让我很好奇为什么它会超时。我想不出一种方法可以显著减少这里所涉及的操作数量(我想通过使用更精简的数据结构来实现一些轻微的操作,但这并不能降低复杂性)C# 这不是一个O(n)算法吗?,c#,algorithm,tree,time-complexity,C#,Algorithm,Tree,Time Complexity,我试图弄明白为什么我的算法通过了所有没有超时的测试用例。据我所知,这是一个O(n)算法,因为它是一系列O(n)算法的执行。这让我很好奇为什么它会超时。我想不出一种方法可以显著减少这里所涉及的操作数量(我想通过使用更精简的数据结构来实现一些轻微的操作,但这并不能降低复杂性) 使用系统; 使用System.Collections.Generic; 使用System.IO; 使用System.Linq; /// /// ///解决方案https://www.hackerrank.com/chall
使用系统;
使用System.Collections.Generic;
使用System.IO;
使用System.Linq;
///
///
///解决方案https://www.hackerrank.com/challenges/cut-the-tree
///
///算法说明:
///
///给一棵像树一样的树
///
///Val=100
/// \
///Val=200
/// / \
////Val=100
///Val=100
/// / \
///Val=500 Val=600
///
///为每个节点设置一个字段,显示值的总和
///在根为该节点的子树中,使其成为
///
///Val=100
///总和=1600
/// \
///Val=200
///总和=1500
/// / \
////Val=100
////Sum=100
///Val=100
///总和=1200
/// / \
///Val=500 Val=600
///总和=500总和=600
///
///这样我们就可以很容易地找到两个变量之和之间的最小差值
///断开分支后产生的两棵树:如果根节点
///是R,我们连接节点N,那么两者之间的区别是什么
///总和是| R.Sum-2*N.Sum |。
///
///
类节点
{
公共int Val{get;set;}
公共节点父节点{get;set;}=null;
公共列表邻居{get;set;}=new List();
///
///子节点中的值之和
///
公共int子体sum{get;set;}=0;
///
///根为此节点的树中的值之和
///
public int TreeSum{get{return Val+genderantssum;}}
}
类解决方案
{
///
///在节点之间建立父关系
///复杂性:O(n),其中n是节点数
///
静态节点BuildToTree(节点[]节点)
{
节点根=节点[0];//使用任意节点作为根
var Q=新队列();
排队(根);
而(Q.Count>0)
{
var current=Q.Dequeue();
foreach(当前.nextries.Where(nbr=>nbr!=current.Parent&&nbr.Parent==null)中的var邻居)
{
邻居。父项=当前;
排队(邻居);
}
}
返回根;
}
///
///设置每个节点的子代树的总和
///复杂性:O(n),其中n是节点数
///
静态无效集合(节点[]节点)
{
foreach(节点中的var节点)
for(var parent=node.parent;parent!=null;parent=parent.parent)
parent.degenantssum+=node.Val;
}
///
///获取两个值之和之间的最小差值
///因切断一根树枝而导致的两棵树。
///
静态int MinDiff(节点[]节点,节点根)
{
返回节点
.Skip(1)
.Min(node=>Math.Abs(root.TreeSum-2*node.TreeSum));
}
静态void Main(字符串[]参数)
{
字符串curdir=Directory.GetCurrentDirectory();
System.IO.StreamReader文件=新的System.IO.StreamReader(
Path.GetFullPath(Path.Combine(curdir,@.\..\,“TestFiles\\SampleInput.txt”))
);
intn=Int32.Parse(file.ReadLine());
int[]vals=Array.ConvertAll(file.ReadLine().Split(“”),Int32.Parse);
Node[]nodes=vals.Select(val=>newnode(){val=val}).ToArray();
对于(int i=0,n=n-1;i
您的SetSums()函数是O(n^2)(考虑一棵树,其中所有节点都链接到一个列表中)。您应该按后序或反向拓扑顺序遍历树,并根据其子树的和计算每个父树的和。O(n)算法可能会超时,如果它们足够慢。树在插入过程中通常是对数(n)的,对吗?也许你在思考它。强烈地考虑把它移到时间复杂度是主要话题的地方。那里的许多计算机科学家应该能够帮助解决这类问题。如果您确实移动了它,请确保首先从此处删除它,然后将其发布到那里;交叉发布是非常不受欢迎的。算法的复杂性与其执行时间几乎没有关系。无论是对于列表中的每个成员,算法都会打印该成员,还是生成斐波那契序列的第百万位,它仍然是O(n)。看起来您正在计算此处所有节点的后代。这意味着您要为每个同级节点重复计算祖先。这不在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));
}
}