【问题标题】:What C# Data Structure Supports The Following?什么 C# 数据结构支持以下内容?
【发布时间】:2013-08-12 02:32:59
【问题描述】:

我需要一个按以下方式工作的 C# 数据结构:

  1. 用静态尺寸定义它
  2. 将新数据添加到列表末尾
  3. 最旧的数据丢失。
  4. 数据元素的随机访问

示例:如果我定义了 5 个元素的结构并添加了以下内容

1,2,3,4,5,6,7,8

数据结构如下所示:

4,5,6,7,8

我不确定哪种结构会以这种方式工作。向量?列表?堆?数据结构支持像数组一样的静态大小,并推送旧数据的数据。

堆栈/队列不提供随机访问。 List 没有“推送”操作。

也许是 LinkedList 并为“push”添加自定义操作以删除第一个元素?不过,LinkList 随机访问是 o(n) 次操作。

【问题讨论】:

标签: c# data-structures


【解决方案1】:

为了获得最大效率,这可能是一个实现循环缓冲区的自定义类。

只需在实例化时创建一个固定大小的数组来保存数据。此外还有一个起始索引、一个大小成员和一个容量,这样您就可以知道缓冲区中有多少数据以及它从哪里开始。

因此,首先,您的列表不包含任何数据,起始位置为 0,大小为 0。

当您添加一个项目时,它会进入元素 (start + size) % capacity,如果它尚未位于 capacity,则 size 会递增。如果它capacity,你也增加start,如果需要,环绕:start = (start + 1) % capacity

要从列表中获取索引n 处的元素,您实际上可以使用start 对其进行调整:

return element[(start + n) % capacity];

我没有涉及删除列表的开头,因为这不在您的规范中。然而,这是一个简单的检查,以确保 size 不为 0,然后提取 element[start] 处的项目,然后使用上面显示的相同环绕递增 start

在伪代码中(未经测试但应该接近):

def listNew (capacity):
    me = new object
    me.capacity = capacity
    me.data = new array[capacity]
    me.start = 0
    me.size = 0
    return me

def listAdd (me, item):
    if me.size = me.capacity:
        me.data[me.start] = item
        me.start = (me.start + 1) % me.capacity
    else:
        me.data[(me.start + me.size) % me.capacity] = item
        me.size = me.size + 1

def listGet (me, index):
    if index > size:
        return 0 # or raise error of some sort
    return me.data[(me.start + index) % me.capacity]

def listRemove (me):
    if size == 0:
        return 0 # or raise error of some sort
    item = me.data[(me.start + index) % me.capacity]
    me.start = (me.start + 1) % me.capacity
    me.size = me.size - 1
    return item

根据要求,所有这些操作都是 O(1) 时间复杂度。

对于将数字 18 添加到五元素列表的特定示例,您最终会得到:

  0   1   2   3   4 <--- index
+---+---+---+---+---+
| 6 | 7 | 8 | 4 | 5 |
+---+---+---+---+---+
              ^
              +--------- start    = 3
                         size     = 5
                         capacity = 5

这样,从缓冲区中提取虚拟索引 3(第四个数字)将为您提供一个实际索引:

  (start + 3) % capacity
= (  3   + 3) %    5
=       6     %    5
=             1

【讨论】:

  • 谢谢。还有可能有用的固定语句。
  • @CurtisWhite fixed 语句仅适用于不安全的上下文,这里绝对不适用。这是 C# 知识的“高级”领域,只有在使用本机(非 .Net)代码进行 Interop/PInvoke 时才真正有用。
  • @Pax 乍一看这似乎是一个相当不错的实现emoticode.net/c-sharp/circular-buffer-class.html
  • @Curtis,是的,乍一看还不错。没有非满缓冲区的概念(它总是被认为是完全填充的),但如果你需要它可以很容易地添加。
【解决方案2】:

这是一个最大长度队列(因此​​,一旦一个元素达到最大长度,您必须先出列并丢弃一个元素,然后再将另一个元素排入队列)。您可以在 C# 队列上进行随机访问,但它是 O(n)(使用 ElementAt LINQ 扩展),如果 5 是典型大小,这可能不是真正的问题。如果你想要 O(1),我怀疑你必须自己动手(https://github.com/mono/mono/blob/master/mcs/class/System/System.Collections.Generic/Queue.cs?source=cc 可能会有所帮助!)

【讨论】:

    【解决方案3】:

    队列在这种情况下是最好的,您应该编写自己的包装类,在入队(将新数据添加到列表末尾)之前检查您的计数(限制),使其表现得像一个固定的,这里是一个例子:

    public class FixedQueue<T> : Queue<T>
    {
    //to sets the total number of elements that can be carried without resizing,
    //we called the base constrctor that takes the capacity
        private Random random;
        public int Size { get; private set; }
    
        public FixedQueue(int size, Random random)
            :base(size)                 
        {
             this.Size = size;
             this.random = random;
        }
    
        public new void Enqueue(T element)
        {
            base.Enqueue(element);
            lock (this)
                while (base.Count > Size)
                    base.Dequeue();  //as you said, "Oldest data falls off" :)
        }
    
        public T RandomAcess()
        {
            return this.ElementAt(random.Next(Count));
        }
    }
    

    【讨论】:

    • 我不认为这就是 OP 所说的“随机访问”的意思,尽管我必须承认它引起了一个微笑 :-) 随机访问 OP 要求意味着能够访问任何元素而不必遍历其他元素,不是随机访问元素的能力。无论如何,我认为队列中的 ElementAt 是 O(n) 而不是 O(1)。
    猜你喜欢
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 2015-04-27
    相关资源
    最近更新 更多