.NET中的数据结构——队列和栈

  .NET 是一个很好的开发平台,它提供了大量的非常有用的类库,大大方便了开发人员的效率。其中的集合类就是开发过程当中必不可少的一部分,虽然用了挺长一段时间了,但是都没有认真的研究过它们的实现方式,以及它们分别属于哪种数据结构。只有了解了上述的问题,我们才能更好的在日常的开发当中更有效的使用它们,所以最近使用 ILSpy 反编译软件好好学习了一下.NET 中的集合类,现在将自己学习的心得总结一下,希望对大家有帮助,有不正确的地方也请大家帮助纠正。

队列的实现

队列的数组实现

QueueNamespaceSystem.CollectionsAssemblymscorlib.dll

 

概述

       Queue实现了一个“先进先出”的特殊表——队列,它的内部维护了一个 object 类型的数组 _array,用来储存队列的元素,这也意味着对队列中的元素进行某些操作时会产生额外的装箱和拆箱操作。

       Queue中的元素是以循环的方式存放在 _array 中的,即当队尾到达 _array 的最后一个元素时,后面入队的元素会继续从 _array 的第一个元素开始存放(只要 _array 的这些元素善未分配,如果所有的元素都已分配存放相应的队列元素就会进行扩容)。

       因为队列是一个不断有元素入队出队的特殊表,那就会涉及到扩容的问题,而 Queue 的内部是使用数组来实现的,所以每次扩容时都需要创建容量更大的数组,并把已有元素全部复制到新数组中,最后使用这个新数组替代旧数组作为内部储存元素的容器。

       Queue的扩容和“成长因子”有关系。成长因子是决定 Queue扩容的比例,如果该Queue的成长因子是2Queue扩容后容量会扩大到原来的两倍。

 

功能说明

       Queue 提供了队列的创建、清除、复制,元素的入队、出队、搜索和队首元素的获取的功能。虽然它的内部是通过数组实现的,能够使用元素的位置对元素进行随机存取,但是这样就破坏了队列“先进先出”的特性,所以它对外并不提供这个功能。由于 Queue 并没有提供元素的存取功能,因此 Queue 中的元素都是只读的(对于引用类型来说只是元素保存的内存地址不会被改变,而存放在该内存地址的对象的状态是可以改变的)。

 

队列的创建:实例化一个队列,并可以指定队列的初始容量和成长因子。

队列的清除:将队列中所有的元素都重置为默认值。

队列的复制:将队列中所有的元素都复制到目标数组中的指定位置。

元素的入队:将元素添加到队列的尾部,如果队列已满则触发队列扩容。

元素的出队:将队首元素移除,并返回该元素。

元素的搜索:从队首开始按顺序搜索是否存在指定元素相等的元素,并返回 true false

队首元素的获取:返回队首的元素,但是并不将其从队列中移除(这是与元素的出队不同的地方)。

 

性能说明

元素的入队:直接将元素添加到队列的尾部,只有当队列已满的情况下回发生扩容操作,所以该操作的时间复杂度为 O(1)

元素的出队:直接将队首元素返回并从队列中移除,该操作的时间复杂度为 O(1)

元素的搜索:因为每次搜索都只能从队首一直按顺序搜索到队尾,所以该操作的时间复杂度为 O(N)

队首元素的获取:直接返回队首的元素,该操作的时间复杂度为 O(1)

由上可以知道,对 Queue 的操作的时间复杂度基本上都是 O(1),只有搜索的时间复杂度是 O(N),可见其整体性能是很好的。

 

适用场景

       当我们要操作的数据满足下面条件时就比较适合使用 Queue

1、  对元素的访问顺序与元素加入的顺序一致(即“先进先出”);

2、  数据集中的元素都是只读的;

3、  每次都只会访问加入数据集中时间最长的那个元素。


 

Queue<T>NamespaceSystem.Collections.GenericAssemblySystem.dll

 

概述

       Queue<T> 实现了一个强类型的“先进先出”的特殊表——队列。它所提供的功能、各功能的性能以及内部的实现机制与 Queue 都是一样的,唯一不同的地方就是 Queue<T> 的强类型的,有类型安全保障。

 

功能说明

       参照 Queue 的功能说明。

 

性能说明

       参照 Queue 的性能说明。

 

适用场景

       因为 Queue<T> Queue 的唯一区别就是:Queue<T> 是强类型的,有类型安全保障,所以 Queue<T> Queue的适用场景也只有一个区别:能够保证加入数据集中所有元素的类型是相兼容的,我们就使用 Queue<T>;否则就使用 Queue

 

 

队列的链表实现

       .NET 中只提供了数组实现的队列,上面的Queue Queue<T>都是以数组方式实现的。但是个人感觉使用单向链表实现的队列应该更优秀:它克服了数组实现的队列在扩容方面的弊端。所以这里本人实现了一个使用单向链表实现的队列类——LinkedQueue<T>

 

LinkedQueue<T>NamespaceCommon.Collection.GenericAssemblyCommon.Collection.Generic

 这里可以:下载LinkedQueue<T>的源代码


概述

       LinkedQueue<T> 提供了一个以单向链表方式实现的队列,内部维护了一组 LinkedQueueNode<T> 类型的节点。在链表形式的队列中存在节点和元素两个概念:元素是链表需要存储的真正数据;而节点是组成链表的基本单位,其中储存了元素 _item 和访问其它节点的信息 _next

       链表有单向和双向之分,LinkedQueue<T> 为什么采用了单向链表呢?因为对于队列来说,访问元素的方式是固定的,都是从队尾插入元素,从队首提取元素,在遍历所有元素时也是固定从队首一致按顺序访问到队尾,所以每个节点只需要知道怎么获取自己的下一个节点就可以了。

 

功能说明

       参照 Queue<T> 的功能说明

 

性能说明

       参照 Queue<T> 的功能说明,LinkedQueue<T> 在性能上的优势就是:它是由节点一个一个连接而成,所以没有扩容的概念。

 

适用场景

       参照 Queue<T> 的适用场景。


栈的实现

StackNamespaceSystem.CollectionsAssemblymscorlib.dll

Stack<T>NamespaceSystem.Collections.GenericAssemblySystem.dll

       Stack实现了一个“先进后出”的特殊表——栈,它的内部维护了一个 object 类型的数组 _array,用来储存栈的元素,这也意味着对栈中的元素进行某些操作时会产生额外的装箱和拆箱操作,而且在 _array 的所有元素都已分配后再有元素入栈就需要扩容:创建容量更大的数组,并把已有元素全部复制到新数组中。

       由于栈“先进后出”的特性,所有的操作都只针对栈顶元素,所以不需要循环利用 _array 中的元素,Stack 的内部也只需要记录栈顶元素在 _array 中的位置。

       Stack提供了栈的创建、清除、复制,元素的入栈、出栈、搜索和栈顶元素的获取的功能。其实现方式和性能与 Queue 基本相同。而Stack<T> 就是一个强类型版本的 Stack,所以它所提供的功能、实现方式和性能也都和 Stack 基本相同,只在某些操作上避免了装箱和拆箱的操作。

posted @ 2011-09-27 17:10  Web游离者  阅读(3025)  评论(6编辑  收藏  举报