1

我有一个列表(.NET),需要构建一个缩进的树结构。列表中的每个项目都有一个指示缩进级别的 Position 属性。最终结构应如下所示:

// (1) 1
//      (2) 1-1
//      (2) 1-2
//      (2) 1-3
// (1) 2
//      (2) 2-1
//          (3) 2-1-1
// (1) 3

括号内的数字是 Position 属性。给定级别的每个后续项目都应该有一个标签,表明它在该级别的项目列表中的计数。如示例所示,较低的职位级别将具有某种大纲格式标签。请记住,我必须正确生成这些标签。

老实说,我已经有一段时间没有做过递归工作了,虽然我认为这将是最好的解决方案,但我只是坚持如何完成工作。我可能过于复杂了。我的想法是,当我到达“叶子”时,我应该从列表中删除该项目并返回,但我不确定。只是稍微转动一下我的轮子。

谢谢,杰

更新:

这是接近的东西,但我无法弄清楚最后一个案例。到目前为止,如果下一个项目落在同一级别或缩进一个级别,它会很好,但我需要一个案例来说明列表中的下一个项目位于小于当前位置的位置(即缩进更远左边)。另外,我不确定方法顶部的基本情况:

var sb = new StringBuilder();
BuildTree(sb, list, 0, 1, string.Empty);
return sb.ToString();

private void BuildTree(StringBuilder sb, List<Item> list, int currIndex, int count, string parentId)
{
    if (list.Count == currIndex)
    {
        return;
    }

    // Build my item.
    string currId = parentId == string.Empty ? count.ToString() : parentId + "-" + count;
    sb.Append(currId + "<br />");

    if (list[currIndex + 1].Position == list[currIndex].Position)
    {
        BuildTree(sb, list, currIndex + 1, count + 1, parentId);
    }

    if (list[currIndex + 1].Position > list[currIndex].Position)
    {
        BuildTree(sb, list, currIndex + 1, 1, currId);
    }
}

更新:

另一种实现,但对于缩进较少的行仍然失败:

private void BuildTree(StringBuilder sb, List<Item> list, int currIndex, int count, string parentId)
{
    if (list.Count == 0)
    {
        return;
    }

    // Build my item.
    string currId = parentId == string.Empty ? count.ToString() : parentId + "-" + count;
    sb.Append(currId + "<br />");

    if (list[currIndex + 1].Position == list[currIndex].Position)
    {
        BuildTree(sb, list, currIndex + 1, count + 1, parentId);
    }

    if (list[currIndex + 1].Position > list[currIndex].Position)
    {
        BuildTree(sb, list, currIndex + 1, 1, currId);
    }

    list.RemoveAt(currIndex);
}

更新:

这是一个黑客,但它有效。我基本上是在根目录下构建多个子树。我更喜欢适当的解决方案而不是 hack,但这是我迄今为止最好的尝试。欢迎替代品:

var sb = new StringBuilder();
List<Person> list = GetTheList();
int cnt = 0;
while (list.Count > 0)
{
    BuildTree(sb, list, 0, ++cnt, string.Empty);
}

return sb.ToString();


private void BuildTree(StringBuilder sb, List<Person> list, int currIndex, int count, string parentId)
{
    // Build my item.
    string currId = parentId == string.Empty ? count.ToString() : parentId + "-" + count;
    sb.Append(currId + "<br />");

    if (list.Count > 1)
    {
        if (list[currIndex + 1].Position == list[currIndex].Position)
        {
            BuildTree(sb, list, currIndex + 1, count + 1, parentId);
        }

        if (list[currIndex + 1].Position > list[currIndex].Position)
        {
            BuildTree(sb, list, currIndex + 1, 1, currId);
        }
    }

    list.RemoveAt(currIndex);
}
4

2 回答 2

1
var sb = new StringBuilder();
List<Person> list = GetTheList();
int cnt = 0;
// The loop is necessary to iterate over the root elements.
while (list.Count > 0)
{
    BuildTree(sb, list, 0, ++cnt, string.Empty);
}

return sb.ToString();


private void BuildTree(StringBuilder sb, List<Person> list, int currIndex, int count, string parentId)
{
    string currId = parentId == string.Empty ? count.ToString() : parentId + "-" + count;
    sb.Append(currId + "<br />");

    if (list.Count > 1)
    {
        if (list[currIndex + 1].Position == list[currIndex].Position)
        {
            BuildTree(sb, list, currIndex + 1, count + 1, parentId);
        }

        if (list[currIndex + 1].Position > list[currIndex].Position)
        {
            BuildTree(sb, list, currIndex + 1, 1, currId);
        }
    }

    list.RemoveAt(currIndex);
}
于 2012-06-30T20:19:56.880 回答
0

这是一种选择。它没有什么花哨的,甚至可能不漂亮,并且肯定没有任何健全性检查或错误处理逻辑,但它支持您说明的结构。

输出应该与您在问题顶部的内容相似,它有望引导您朝着正确的方向前进。

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    public class Program
    {
        private static readonly IndentedList indentedList = new IndentedList();

        static void Main()
        {
            Fill();
            indentedList.Items.ForEach(Print);
        }

        private static void Fill()
        {
            indentedList.Add(new Node()); // 1
            indentedList.Add(new Node()); // 2
            indentedList.Add(new Node()); // 3

            indentedList.Items[0].Add(new Node()); // 1-1
            indentedList.Items[0].Add(new Node()); // 1-2
            indentedList.Items[0].Add(new Node()); // 1-3

            indentedList.Items[1].Add(new Node()); // 2-1
            indentedList.Items[1].Children[0].Add(new Node()); // 2-1-1
        }

        private static void Print(Node node)
        {
            var indentation = new String('\t', node.IndentationLevel - 1);
            Console.WriteLine("{0}({1}) {2}", indentation, node.IndentationLevel, node);
            node.Children.ForEach(Print);
        }
    }

    public class IndentedList
    {
        private readonly Node superNode = new Node();

        public List<Node> Items { get { return superNode.Children; } }

        public void Add(Node node)
        {
            superNode.Add(node);
        }
    }

    public class Node
    {
        public int IndentationLevel { get { return parent == null ? 0 : parent.IndentationLevel + 1; } }
        public int Position { get { return parent.Children.IndexOf(this) + 1; } }
        public List<Node> Children { get; private set; }

        private Node parent;
        private bool IsRootNode { get { return parent.parent == null; } }

        public Node()
        {
            Children = new List<Node>();
        }

        public void Add(Node child)
        {
            child.parent = this;
            Children.Add(child);
        }

        public override string ToString()
        {
            return IsRootNode ? Position.ToString() : String.Format("{0}-{1}", parent, Position);
        }
    }
}
于 2012-06-23T20:47:46.297 回答