在C#中为动态层次结构添加序列号

时间:2017-08-18 07:46:47

标签: c# algorithm

假设我有这些记录:

Code     |GroupLevel    |Group
-----------------------------------
X0000    |4             |
X1000    |3             |X0000
X2000    |3             |X0000
X3000    |3             |X0000
X1100    |2             |X1000
X1200    |2             |X1000
X1300    |2             |X1000
X2100    |2             |X2000
X2200    |2             |X2000
X2300    |2             |X2000
X1110    |1             |X1100
X1120    |1             |X1100
X1111    |0             |X1110
X1112    |0             |X1110
X1113    |0             |X1110
X1114    |0             |X1110

我想要实现的是拥有这种序列号:

Seq |Code     |GroupLevel    |Group
-------------------------------------
1   |X0000    |4             |
2   |X1000    |3             |X0000
3   |X1100    |2             |X1000
4   |X1110    |1             |X1100
5   |X1111    |0             |X1110
6   |X1112    |0             |X1110
7   |X1113    |0             |X1110
8   |X1114    |0             |X1110
9   |X1120    |1             |X1100
10  |X1200    |2             |X1000
11  |X1300    |2             |X1000
12  |X2000    |3             |X0000
13  |X2100    |2             |X2000
14  |X2200    |2             |X2000
15  |X2300    |2             |X2000
16  |X3000    |3             |X0000

我尝试使用固定组级别(3),但如果组级别> 5。

这就是我的所作所为:

List<MySequenceModel> _lstPair = new List<MySequenceModel>();
var _lst = _records.Where(x => x.GroupLevel == 3).ToList();
foreach (var item in _lst)
{
    if (_lstPair.Where(x => x.Code.Equals(item.Code)).FirstOrDefault() == null)
        _lstPair.Add(new MySequenceModel { Code = item.Code, SeqNo = _seqCounter++ });

    var _lst2 = _records.Where(x => x.Group.Equals(item.Code) && !x.Code.Equals(item.Code)).OrderBy(x => x.Code).ToList();
    foreach (var _item2 in _lst2)
    {
        if (_lstPair.Where(x => x.Code.Equals(_item2.Code)).FirstOrDefault() == null)
            _lstPair.Add(new MySequenceModel { Code = _item2.Code, SeqNo = _seqCounter++ });
        var _lst3 = _records.Where(x => x.Group.Equals(_item2.Code) && !x.Code.Equals(_item2.Code)).OrderBy(x => x.Code).ToList();
        foreach (var _item3 in _lst3)
        {
            if (_lstPair.Where(x => x.Code.Equals(_item3.Code)).FirstOrDefault() == null)
                _lstPair.Add(new MySequenceModel { Code = _item3.Code, SeqNo = _seqCounter++ });
            var _lst4 = _records.Where(x => x.Group.Equals(_item3.Code) && !x.Code.Equals(_item3.Code)).OrderBy(x => x.Code).ToList();
            foreach (var _item4 in _lst4)
            {
                if (_lstPair.Where(x => x.Code.Equals(_item4.Code)).FirstOrDefault() == null)
                    _lstPair.Add(new MySequenceModel { Code = _item4.Code, SeqNo = _seqCounter++ });
            }
        }
    }
}

MySequenceModel是一个具有代码和序列号的类,它基本上是报告所必需的。伪代码就可以了。

TIA

3 个答案:

答案 0 :(得分:1)

我会将输入列表转换为树结构,然后使用pre order depth first traversal处理它。

对于第一部分,我将使用ToLookup方法,第二部分 - Expand自定义扩展方法,从我对How to flatten tree via LINQ?的回答,最后是Select重载生成序列号的索引:

var recordsByGroup = _records
    .OrderBy(r => r.Code)
    .ToLookup(r => r.Group ?? "");

var result = recordsByGroup[""]
    .Expand(r => recordsByGroup[r.Code])
    .Select((r, i) => new MySequenceModel { Code = r.Code, SeqNo = i + 1 })
    .ToList();

更新:我认为上述算法在提供的输入数据结构的空间和时间复杂度方面是最佳的。但是为了完整性,因为所需的转换只不过是分层排序,使用LINQ OrderBy方法传递路径选择器,通过记录的路径对它进行排序可以实现相同的目的。和比较器:

// Helper structure for fast locating the parent records
var recordsByCode = _records.ToDictionary(r => r.Code);
// Path selector (list of records starting from root and icluding the record in question)
Func<MyRecord, IReadOnlyList<MyRecord>> pathSelector = r =>
{
    var path = new List<MyRecord>();
    do path.Add(r); while (recordsByCode.TryGetValue(r.Group, out r));
    path.Reverse();
    return path;
};
// Path comparer
var pathComparer = Comparer<IReadOnlyList<MyRecord>>.Create((path1, path2) =>
{
    int length = Math.Min(path1.Count, path2.Count); // the common path length
    for (int i = 0; i < length; i++)
    {
        int comparison = path1[i].Code.CompareTo(path2[i].Code);
        if (comparison != 0) return comparison;
    }
    // In case the common path is the same, put the shorter path first in the order
    return path1.Count.CompareTo(path2.Count);
});
// Simply sort using the path selector and comparer
var result2 = _records
    .OrderBy(pathSelector, pathComparer)
    .Select((r, i) => new /*MySequenceModel*/ { Code = r.Code, SeqNo = i + 1 })
    .ToList();

此方法使用更多临时内存来保存记录比较器中的路径和更重(因此更慢)的比较。

答案 1 :(得分:1)

现在已经很晚了,这可能会更好但可行但

dotnetfiddler linke

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

public class Program
{
    public static void Main()
    {
        var fun = new List<Data>();
        fun.Add(new Data() { Code ="X0000", GroupLevel=4, Group = null});
        fun.Add(new Data() { Code ="X1000", GroupLevel=3, Group = "X0000"});
        fun.Add(new Data() { Code ="X2000", GroupLevel=3, Group = "X0000"});
        fun.Add(new Data() { Code ="X3000", GroupLevel=3, Group = "X0000"});
        fun.Add(new Data() { Code ="X1100", GroupLevel=2, Group = "X1000"});
        fun.Add(new Data() { Code ="X1200", GroupLevel=2, Group = "X1000"});
        fun.Add(new Data() { Code ="X1300", GroupLevel=2, Group = "X1000"});
        fun.Add(new Data() { Code ="X2100", GroupLevel=2, Group = "X2000"});
        fun.Add(new Data() { Code ="X2200", GroupLevel=2, Group = "X2000"});
        fun.Add(new Data() { Code ="X2300", GroupLevel=2, Group = "X2000"});
        fun.Add(new Data() { Code ="X1110", GroupLevel=1, Group = "X1100"});
        fun.Add(new Data() { Code ="X1120", GroupLevel=1, Group = "X1100"});
        fun.Add(new Data() { Code ="X1111", GroupLevel=0, Group = "X1110"});
        fun.Add(new Data() { Code ="X1112", GroupLevel=0, Group = "X1110"});
        fun.Add(new Data() { Code ="X1113", GroupLevel=0, Group = "X1110"});
        fun.Add(new Data() { Code ="X1114", GroupLevel=0, Group = "X1110"});

        var result = (from f1 in fun
                     join f2 in fun on f1.Code equals (f2.Group ?? f2.Code)
                      orderby f2.Code
                     select f2).Select((v,i) => new Data(v,i)).ToList();

        result.ForEach(x => Console.WriteLine(x));
    }

}

public class Data
{
    public int Seq {get;set;}
    public string Code {get;set;}
    public int GroupLevel {get;set;}
    public string Group {get;set;}

    public Data(){}

    public Data(Data v, int i)
    {
        Seq = i+1;
        Code = v.Code;
        GroupLevel = v.GroupLevel;
        Group = v.Group;
    }

    public override string ToString()
    {
        return string.Format("{3} - {0} - {1} - {2}",Code, GroupLevel,Group, Seq);
    }
}

结果;

  

1 - X0000 - 4 -
  2 - X1000 - 3 - X0000
  3 - X1100 - 2 - X1000
  4 - X1110 - 1 - X1100
  5 - X1111 - 0 - X1110
  6 - X1112 - 0 - X1110
  7 - X1113 - 0 - X1110
  8 - X1114 - 0 - X1110
  9 - X1120 - 1 - X1100
  10 - X1200 - 2 - X1000
  11 - X1300 - 2 - X1000
  12 - X2000 - 3 - X0000
  13 - X2100 - 2 - X2000
  14 - X2200 - 2 - X2000
  15 - X2300 - 2 - X2000
  16 - X3000 - 3 - X0000

答案 2 :(得分:1)

给出以下输入数据(取自问题):

MySequenceModel[] _records = new MySequenceModel[]
{
    new MySequenceModel { Code = "X0000", GroupLevel = 4, Group = "" },
    new MySequenceModel { Code = "X1000", GroupLevel = 3, Group = "X0000" },
    new MySequenceModel { Code = "X2000", GroupLevel = 3, Group = "X0000" },
    new MySequenceModel { Code = "X3000", GroupLevel = 3, Group = "X0000" },
    new MySequenceModel { Code = "X1100", GroupLevel = 2, Group = "X1000" },
    new MySequenceModel { Code = "X1200", GroupLevel = 2, Group = "X1000" },
    new MySequenceModel { Code = "X1300", GroupLevel = 2, Group = "X1000" },
    new MySequenceModel { Code = "X2100", GroupLevel = 2, Group = "X2000" },
    new MySequenceModel { Code = "X2200", GroupLevel = 2, Group = "X2000" },
    new MySequenceModel { Code = "X2300", GroupLevel = 2, Group = "X2000" },
    new MySequenceModel { Code = "X1110", GroupLevel = 1, Group = "X1100" },
    new MySequenceModel { Code = "X1120", GroupLevel = 1, Group = "X1100" },
    new MySequenceModel { Code = "X1111", GroupLevel = 0, Group = "X1110" },
    new MySequenceModel { Code = "X1112", GroupLevel = 0, Group = "X1110" },
    new MySequenceModel { Code = "X1113", GroupLevel = 0, Group = "X1110" },
    new MySequenceModel { Code = "X1114", GroupLevel = 0, Group = "X1110" },
};

然后这个有效:

var lookup = _records.ToLookup(x => x.Group);
Func<string, IEnumerable<MySequenceModel>> traverse = null;
traverse = grp => lookup[grp].SelectMany(x => new [] { x }.Concat(traverse(x.Code)));

MySequenceModel[] results =
    traverse("")
        .Select((x, n) => new MySequenceModel
        {
            SeqNo = n + 1,
            Code = x.Code,
            GroupLevel = x.GroupLevel,
            Group = x.Group
        })
        .ToArray();

它给了我:

results