【问题标题】:Query a binary tree查询二叉树
【发布时间】:2016-12-17 16:32:11
【问题描述】:

我有一个 Root-Parent-Child 二叉树,需要根据几个标准求和并获取子值。我不确定是使用 Linq 还是遍历树。 Linq 查询崩溃(Additional information: Unable to cast object of type 'ID' to type 'Greek'),我不知道如何遍历树并检查每个参数。感谢您提供任何帮助,或链接到网站和书籍以增加我的知识。这个link 有帮助,但我仍然卡住了。

public class Node
{
    public List<Node> Children = new List<Node>();
    public Node Parent = null;

    public Node(Node fromParent = null)
    {
        if (fromParent != null)
        {
            Parent = fromParent;
            fromParent.Children.Add(this);
        }
    }
}

public class ID : Node
{
    public int IdNo;
    public int DealNo;
    public string Strategy;
    public ID(int _ID,int _DealNo,string _Strategy) : base(null)
    {
        IdNo = _ID;
        DealNo = _DealNo;
        Strategy = _Strategy;
    }
}

public class Greek : Node
{
    public string LegOrPos;
    public string GreekType;
    public Greek(string _LegOrPos, string _GreekType, Node fromParent = null) : base(fromParent)
    {
        LegOrPos = _LegOrPos;
        GreekType = _GreekType;
    }
}

public class DataPoint : Node
{
    public int DpNo;
    public double Value;
    public DataPoint(int _DpNo, double _Value, Node fromParent = null) : base(fromParent)
    {
        DpNo = _DpNo;
        Value = _Value;
    }
}


public void SimpleTest()
{
    List<Node> MC = new List<Node>();

    // 1st node
    var oID = new ID(23, 2,"Fly");                  // ID,DealNo,Strategy
    var oGreek = new Greek("Leg", "Delta", oID);    //LegOrPos,GreekType
    var oDP = new DataPoint(14, 0.235, oGreek);     //DpNo,Value
    MC.Add(oID);

    // 2nd node
    oID = new ID(25, 5,"BWB");
    oGreek = new Greek("Leg", "Vega", oID);
    oDP = new DataPoint(16, 0.345, oGreek);
    MC.Add(oID);

    // 3rd node
    oID = new ID(31,2,"Fly");
    oGreek = new Greek("Leg", "Delta", oID);
    oDP = new DataPoint(14, 0.456, oGreek);
    MC.Add(oID);

    // use linq or traverse through tree?

    // get total for several parameters
    var Total = MC.Where(x => ((ID)x).DealNo == 2 && ((ID)x).Strategy == "Fly" && ((Greek)x).GreekType == "Delta" && ((DataPoint)x).DpNo == 14)     // should sum 1st and 3rd nodes
        .SelectMany(x => x.Children)
        .Sum(x => ((DataPoint)x).Value);

    // get specific value
    var Val = Convert.ToDouble(MC.Where(x => ((ID)x).IdNo == 23 && ((Greek)x).GreekType == "Delta" && ((DataPoint)x).DpNo == 14)     // should find 1st node
    .SelectMany(x => x.Children)
    .Select(x => ((DataPoint)x).Value).Single());

    // traverse method
    foreach (var objID in MC)
        {
            if (objID.IdNo == 23)     //compile error-IdNo not found
            {
                foreach (Greek objGreek in objID.Children)
                {
                    if (objGreek.GreekType == "Delta")
                    {
                        foreach (DataPoint objDP in objGreek.Children)
                        {
                            if (objDP.DpNo == 14)
                            {
                                double qVal = objDP.Value;
                            }
                        }
                    }
                }
            }
        }
}

【问题讨论】:

  • 据我所知,您根本不使用二叉树。您只需填充您的节点,并将它们添加到您的列表中。你确定你想要一些像遍历的树吗?或者您也只是不知道如何遍历列表?
  • 你是对的......我如何遍历列表?
  • 这不是二叉树,而是n叉树。
  • 你说得对,谢谢指正

标签: c# linq binary-tree


【解决方案1】:

您似乎遇到的问题是您的标准是不可能的。

请走这条线:MC.Where(x =&gt; ((ID)x).DealNo == 2 &amp;&amp; ((ID)x).Strategy == "Fly" &amp;&amp; ((Greek)x).GreekType == "Delta" &amp;&amp; ((DataPoint)x).DpNo == 14)。意思是说您希望MC 的每个成员同时属于IDGreekDataPoint 类型。

根据您的 cmets,听起来您需要这个:

var query =
    from id in MC.OfType<ID>()
    from greek in id.Children.OfType<Greek>()
    from dp in greek.Children.OfType<DataPoint>()
    group dp.Value by new
    {
        id.DealNo,
        id.Strategy,
        greek.LegOrPos,
        greek.GreekType,
        dp.DpNo
    } into gs
    select new
    {
        gs.Key,
        Value = gs.Sum(),
    };

当我在你的数据上运行它时,我得到了这个:

【讨论】:

  • 感谢遍历现在可以工作了,这可以很容易地写成 linq 查询吗?关于总查询:我明白你对 MC 的所有成员都是同一类型的意思。您的总查询已编译,但 total=0 这不是我想要的 - 我想添加 nodes 1 and 3= 0.235+0.456=0.691 作为检查。
  • 您认为二叉树比Dictionary&lt;Tuple&lt;int,string....&gt;,double&gt;好吗?后者更容易编码,但看起来笨拙且容易出错?
  • @Zeus - 我刚刚翻译了您现有的查询以使其“工作” - 我不理解您的逻辑,所以我无法确保它计算出正确的结果。你的标准是什么?
  • 我试图以一种有效的格式将数据存储在内存中,然后通过过滤不同的参数来计算大量的总和。查询是总结果之一。节点 1 和 3 具有相同的 DealNo(2)、GreekType (Delta) 和 DpNo(14),因此查询应该找到这两个值并将它们相加。一个“key”可以被视为ID-Strategy-LegOrPos-GreekType-DpNo
【解决方案2】:

有几种方法可以遍历列表:

/// regular for
for (int L = 0; L <= MC.Count - 1; L++)
{
    Node oID = MC[L];
    if( oID == null ) continue;

    /// oID is your node that you have added to the list MC 
}

/// foreach
foreach ( var oID in MC.Where(_oID => _oID != null) )
{
    /// oID is your node that you have added to the list MC 
}

无论您选择哪种方式,通过我们刚刚收集的oID,您也可以获取您的子节点:

foreach ( Greek oGreek in oID.Children.Where(_oGreek => _oGreek != null) )
{
    /// use oGreeks that you've added to your oID node
    ///

    foreach ( DataPoint oDP in oGreek.Children.Where(_oDP => _oDP != null) )
    {
        /// use oDP's that you've added to your greek node
    }
}

【讨论】:

  • 谢谢,这是有道理的,但是在循环查找节点 ID==23 时出现编译错误 - 请参阅更新后的问题。有没有办法使用 linq 做到这一点?
  • 可能不是编译错误,而是运行时错误。您应该检查您迭代的对象是否为空。查看更新的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-05
  • 1970-01-01
  • 2017-04-14
  • 1970-01-01
  • 2018-05-16
相关资源
最近更新 更多