用于在给定索引处插入,删除和重新排列的高效C#数据结构

时间:2017-04-24 16:41:50

标签: c# .net data-structures

我正在C#中寻找一个高效的数据结构,它允许我保留一份(由用户)订购的项目列表,而不会重复。

我的意思是由用户订购,即:

  • 插入元素1。
  • 在元素1之前插入元素2.
  • 在1和2之间插入元素3.然后随意重新排列。

我需要在更改时在数据库中不断更新订单,以便我可以在开始时加载它。

我需要的操作:

  1. 插入指定的索引
  2. 删除指定的索引
  3. 从索引x移动到索引y(如果没有性能损失,可以表示为2和1的组合)
  4. 所有这些操作都会频繁且同样重要。

4 个答案:

答案 0 :(得分:6)

我认为“有效”是指渐近有效。如果情况并非如此,那么请澄清问题。

索引和任意插入的组合是一个棘手的问题。

  • List<T> s - 它只是数组的一个薄包装 - 最后有O(1)插入/删除,开头有O(n)插入/删除,O(1)索引。检查唯一性是O(n)。
  • 如果您已经知道要放置项目的位置,则链接列表具有O(1)插入/删除,但是O(n)索引以查找该位置。检查唯一性是O(n)
  • 如果你聪明的话,平衡二叉树有O(lg n)插入和删除以及索引。检查唯一性是O(n)。更多奇特的数据结构,如手指树,跳过列表等,都是类似的。
  • 散列集有O(1)插入和删除但没有索引;检查唯一性是O(1)。

没有符合您需求的单一数据结构。我的建议是:

  1. 拥抱不变性。编写满足您需求的不可变数据结构。理由更容易。
  2. 写一个平衡二叉树的组合 - 红黑色,AVL等 - 和一个哈希集。哈希集仅用于唯一性检查。 BBT在每个节点中都有低于它的项目数;这有助于编制索引。插入和删除算法对于BBT来说是正常的,除了它们还重写树的主干以确保项目计数正确更新。
  3. 这将为您提供O(1)唯一性检查和O(lg n)索引,插入和删除。

    我注意到这个数据结构为你提供了O(1)个问题的答案“这个集合中的这个项目是什么?”但O(n)回答了“它在哪里?”的问题。因此,如果您需要快速进行逆索引操作,那么您手上的问题会更大。

答案 1 :(得分:1)

I think I would just use a List and take O(n) Contains or a separate HashSet for uniqueness. List does all the other stuff nicely. Nicely as the operations are all there but most will be O(n). Even on 10,000 O(n) is pretty fast. The database calls are going to be the slowest part by far (try async).

    class MyCollection<T> : IList<T>
    {
        private readonly IList<T> _list = new List<T>();

        public void Insert(int index, T item)
        {
            if (this.Contains(item))
                throw new IndexOutOfRangeException();
            _list.Insert(index, item);
            //make database call
        }

        // implement all the other features of IList with database calls

答案 2 :(得分:1)

这有两个问题:一个用于数据库层,另一个用于内存中集合。但是,如果你让数据库层成为你的真相来源,我认为你几乎可以把它归结为一个问题。

我之所以这样说是因为大约有100个项目作为列表中活动项目的最大可能数量,您几乎可以忽略渐近的复杂性。在性能方面,当您获得这么多项目时,最重要的是跨越网络连接(例如,到数据库)的往返行程。

这是一个你可以使用的相当简单的方法。它类似于我过去所做的事情,具有类似的要求。 (我不记得它是否完全一样,但足够接近。)

  1. 使用数字Order列确定给定列表中商品的顺序。 int应该没问题。
  2. 删除项目时,减少该项目后同一列表中所有项目的订单。这可以通过SQL中的单个UPDATE语句来完成。
  3. 当您添加项目时,根据其添加的位置为其指定一个Order值,并在该项目之后增加同一列表中所有项目的顺序(同样,使用单个Update语句)。
  4. 当您将项目移动到其他位置时,请更改其顺序,然后在其起始位置和结束位置之间递增或递减所有项目的订单。
  5. 每次进行更改时,请按顺序从数据库重新加载整个项目列表,以显示给用户。
  6. 您可能希望使用存储过程在单独的往返过程中完成更多此项工作。绝对是避免竞争条件的交易。

    这样的方法可以轻松扩展个别用户编辑单个列表。如果您需要并发用户的可扩展性,那么像NoSQL商店这样的另一种策略可能会成为可能。如果您需要扩展编辑同一列表的许多并发用户,事情变得非常复杂,您可能需要实现消息总线和其他优点。如果您发现需要扩展到列表中的数万个项目,则需要重新考虑UI以及它与服务器的通信方式(例如,您不希望将整个列表加载到记忆)。但是,当每个操作都是由用户手动执行时,担心内存中的数据结构并不能让您在任何这些情况下都能达到目标。

答案 3 :(得分:0)

就数据结构而言,假设你有直接引用节点linked list对插入和删除的速度很快(在这种情况下,你需要一个双向链表) 。我还没有使用内置的.NET LinkedList,但它似乎有some efficiency problems。如果您遇到List问题,则可能只想使用正常的LinkedList(实际上取决于&#34;效率&#34;您需要这样做。)请参阅List时间复杂性here

至于保存它,您需要做的就是将索引保存在数据库中,并从启动时ORDER BY的查询中填充您的集合。

编辑:

对于重复管理,您可以维护HashSet以检查重复项并阻止插入。