使用 LINQ 构建链表

发布于 2024-10-11 17:52:11 字数 427 浏览 2 评论 0原文

使用 LINQ 按前驱(或父)元素索引对无序列表元素进行排序的最快方法是什么?

每个元素都有一个唯一的 ID 和该元素的前驱(或父)元素的 ID,从中可以构建一个链表来表示有序状态。

示例

ID      | Predecessor's ID
--------|--------------------
20      | 81
81      | NULL
65      | 12
12      | 20
120     | 65

排序顺序为 {81, 20, 12, 65, 120}。可以轻松地从这些元素迭代地组装(有序)链表,但是可以用更少的 LINQ 语句来完成吗?

编辑:我应该指定 ID 不一定是连续的。为了简单起见,我选择了 1 到 5。查看更新后的随机元素索引。

What is the fastest way to order an unordered list of elements by predecessor (or parent) element index using LINQ?

Each element has a unique ID and the ID of that element's predecessor (or parent) element, from which a linked list can be built to represent an ordered state.

Example

ID      | Predecessor's ID
--------|--------------------
20      | 81
81      | NULL
65      | 12
12      | 20
120     | 65

The sorted order is {81, 20, 12, 65, 120}. An (ordered) linked list can easily be assembled iteratively from these elements, but can it be done in fewer LINQ statements?

Edit: I should have specified that IDs are not necessarily sequential. I chose 1 to 5 for simplicity. See updated element indices which are random.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

心奴独伤 2024-10-18 17:52:11

假设“索引”与排序无关(只是一个 ID),假设这样声明:

public class Node
{
    public int ID { get; set; }
    public int? PredID { get; set; }
}

然后您可以构建从前一个到后一个的映射,然后单击链接。然而,这不是纯粹的 LINQ,但是可读且相当高效:

public static LinkedList<int> GetLinkedList(IEnumerable<Node> nodes)
{
    if(nodes == null)
       throw new ArgumentNullException("nodes");

    return new LinkedList<int>(GetOrderedNodes(nodes.ToList()));
}

private static IEnumerable<int> GetOrderedNodes(IEnumerable<Node> nodes)
{
    // Build up dictionary from pred to succ.
    var links = nodes.Where(node => node.PredID != null)
                     .ToDictionary(node => node.PredID, node => node.ID);

    // Find the head, throw if there are none or multiple.
    var nextId = nodes.Single(node => node.PredID == null).ID;

    // Follow the links.
    do
    {
        yield return nextId;

    } while (links.TryGetValue(nextId, out nextId));
}

编辑:这是一个极其低效但实用的解决方案。

这个想法是,一个元素的索引必须是 1 + 其前任元素的索引,这很容易递归指定。当然,基础情况是头 - 一个其前任 ID 为 null 的节点。

static LinkedList<int> GetLinkedList(IEnumerable<Node> nodes)
{
    return new LinkedList<int>
           (  from node in nodes
              orderby GetIndex(nodes, node)
              select node.ID
           );                                       
}


static int GetIndex(IEnumerable<Node> nodes, Node node)
{
    return node.PredID == null ? 0 :
           1 + GetIndex(nodes, nodes.Single(n => n.ID == node.PredID));
}

通过首先创建从后继到前驱的映射,可以稍微改进第二个解决方案。但它的性能仍然不如命令式解决方案。

编辑:将第一个解决方案变成迭代器块。

Assuming that "index" has nothing to do with ordering (just an ID), let's say declared this way:

public class Node
{
    public int ID { get; set; }
    public int? PredID { get; set; }
}

Then you could build up a map from predecessor to successor, and then follow the links. This isn't pure LINQ however, but is readable and quite efficient:

public static LinkedList<int> GetLinkedList(IEnumerable<Node> nodes)
{
    if(nodes == null)
       throw new ArgumentNullException("nodes");

    return new LinkedList<int>(GetOrderedNodes(nodes.ToList()));
}

private static IEnumerable<int> GetOrderedNodes(IEnumerable<Node> nodes)
{
    // Build up dictionary from pred to succ.
    var links = nodes.Where(node => node.PredID != null)
                     .ToDictionary(node => node.PredID, node => node.ID);

    // Find the head, throw if there are none or multiple.
    var nextId = nodes.Single(node => node.PredID == null).ID;

    // Follow the links.
    do
    {
        yield return nextId;

    } while (links.TryGetValue(nextId, out nextId));
}

EDIT: Here's a horribly inefficient but functional solution.

The idea is that an element's index must be 1 + its predecessor's index, which is easy to specify recursively. The base, case, of course, is the head - a node whose predecessor's ID is null.

static LinkedList<int> GetLinkedList(IEnumerable<Node> nodes)
{
    return new LinkedList<int>
           (  from node in nodes
              orderby GetIndex(nodes, node)
              select node.ID
           );                                       
}


static int GetIndex(IEnumerable<Node> nodes, Node node)
{
    return node.PredID == null ? 0 :
           1 + GetIndex(nodes, nodes.Single(n => n.ID == node.PredID));
}

It's possible to improve the second solution somewhat by first creating a map from successor to predecessor. It's still nowhere as performant as the imperative solution though.

EDIT: Turned first solution into an iterator block.

浅笑轻吟梦一曲 2024-10-18 17:52:11

对于排序来说,节点链接的事实是无关紧要的,只是 id 很重要。在这种情况下,您可以使用 OrderBy ,在本例中会将可空 int 放在顶部,然后升序排序。

public class ListNode
{
    public int Id { get; set; }
    public int? Predecessor { get; set; }
}


List<ListNode> myList = new List<ListNode> {
                                new ListNode() { Id = 2, Predecessor = 1},
                                new ListNode() { Id = 1, Predecessor = null},
                                new ListNode() { Id = 5, Predecessor = 4},
                                new ListNode() { Id = 3, Predecessor = 2},
                                new ListNode() { Id = 4, Predecessor = 3}};


var sortedbyPredecessor = myList.OrderBy(n => n.Predecessor).ToList();

For ordering the fact that the nodes are linked is irrelevant, just the ids matter. That being the case you can just use OrderBy, which in this case will put the nullable int at the top, then sort ascending.

public class ListNode
{
    public int Id { get; set; }
    public int? Predecessor { get; set; }
}


List<ListNode> myList = new List<ListNode> {
                                new ListNode() { Id = 2, Predecessor = 1},
                                new ListNode() { Id = 1, Predecessor = null},
                                new ListNode() { Id = 5, Predecessor = 4},
                                new ListNode() { Id = 3, Predecessor = 2},
                                new ListNode() { Id = 4, Predecessor = 3}};


var sortedbyPredecessor = myList.OrderBy(n => n.Predecessor).ToList();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文