跟上一篇《数据结构,你还记得吗(上)》目录进行一一对应讲解C#中各类数据结构,以此来提高理解。html
多维数组,行和列是固定的: int[][] arrMore=new int[3][6]; 锯齿数组只要在第一个方括号设置行数,每行的个数是可变的。 int[][] jagged=new int[3][]; jagged[0]=new int[2]{1,2}; jagged[1]=new int[6]{1,2,3,4,5,6}; jagged[2]=new int[3]{1,2,3};
Array 类是 C# 中全部数组的基类,它是在 System 命名空间中定义(System.Array)。Array 类提供了各类用于数组的属性和方法。
用方括号[] 声明数组是C#中使用Array类的表示法。在后台使用C#语法,会建立一个派生自抽象基类Array的新类。这样,就可使用Array类为每一个C#数组定义的方法和属性了。算法
数组能够直接经过下标访问。数据库
建立一个新的数组时将在 CLR 托管堆中分配一块连续的内存空间,来存放数量为n,类型为所声明类型的数组元素。若是类型为值类型,则将会有n个未装箱的该类型的值被建立。若是类型为引用类型,则将会有n个相应类型的引用被建立。编程
缺点
因为是连续存储,因此在两个元素之间插入新的元素就变得不方便。并且就像上面的代码所显示的那样,声明一个新的数组时,必须指定其长度,这就会存在一个潜在的问题,那就是当咱们声明的长度过长时,显然会浪费内存,当咱们声明长度太短的时候,则面临这溢出的风险。有点投机,针对这种缺点,引出了ArrayList。c#
为了解决数组建立时必须指定长度以及只能存放相同类型的缺点而推出的数据结构。ArrayList是System.Collections命名空间下的一部分,因此若要使用则必须引入System.Collections。正如上文所说,ArrayList解决了数组的一些缺点。数组
为了解决ArrayList不安全类型与装箱拆箱的缺点,因此出现了泛型的概念,做为一种新的数组类型引入。也是工做中常常用到的数组类型。和ArrayList很类似,长度均可以灵活的改变,最大的不一样在于在声明List集合时,咱们同时须要为其声明List集合内数据的对象类型,这点又和Array很类似,其实List
有序列表 SortedList<Tkey,TElement> 只容许每一个键有一个对应的值,若是须要每一个键对应多个值,就须要使用Lookup<Tkey,TElement>数据结构
Array类是一个抽象类,因此不能使用构造函数来建立数组。但除了可使用C#语法建立数组实例以外,还可使用静态方法CreateInstance()建立数组。若是事先不知道元素的类型,该静态方法就很是有用,由于类型能够做为Type对象传递给CreateInstance()方法。多线程
例如: Array arr=Array.CeateInstance(typeof(int),5); for(int i=0;i<5;i++) { arr.SetVaule(i,i); } for(int i=0;i<5;i++) { int vaule=arr.getVaule(i); }
羽毛球筒编程语言
堆栈(Stack)表明了一个后进先出的对象集合。当您须要对各项进行后进先出的访问时,则使用堆栈。当您在列表中添加一项,称为推入元素,当您从列表中移除一项时,称为弹出元素。
public class Stack<T> : IEnumerable<T>, ICollection, IEnumerable public class Stack : ICollection, IEnumerable, ICloneable
属性 | 描述 |
---|---|
Count | 获取 Stack 中包含的元素个数 |
方法 | 描述 |
---|---|
Pop | public virtual object Pop();移除并返回在 Stack 的顶部的对象 |
push | public virtual void Push(object obj);向 Stack 的顶部添加一个对象 |
peek | public virtual object Peek();返回在 Stack 的顶部的对象,但不移除它 |
ToArray | public virtual object[] ToArray();建立数组并将堆栈元素复制到其中 |
Contains | public virtual bool Contains(object obj);判断一个元素是否在栈中 |
Clear | public virtual void Clear();从 Stack 中移除全部的元素。 |
水管子
队列(Queue)表明了一个先进先出的对象集合。当您须要对各项进行先进先出的访问时,则使用队列。当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队。
public class Queue<T> : IEnumerable<T>, IEnumerable, IReadOnlyCollection<T>, ICollection
属性 | 描述 |
---|---|
Count | 获取 Queue 中包含的元素个数 |
方法 | 描述 |
---|---|
Clear | public virtual void Clear(); 从 Queue 中移除全部的元素。 |
Contains | public virtual bool Contains( object obj ); 判断某个元素是否在 Queue 中。 |
Dequeue | public virtual object Dequeue();移除并返回在 Queue 的开头的对象。 |
Enqueue | public virtual void Enqueue( object obj ); 向 Queue 的末尾添加一个对象。 |
ToArray | public virtual object[] ToArray();复制 Queue 到一个新的数组中。 |
TrimToSize | public virtual void TrimToSize();设置容量为 Queue 中元素的实际个数。 |
实现方式 public class Node<T> { public T Data { set; get; } //数据域,当前结点数据 public Node<T> Next { set; get; } //位置域,下一个结点地址 public Node(T item) { this.Data = item; this.Next = null; } public Node() { this.Data = default(T); this.Next = null; } }
有优势就有缺点,因为其在内存空间中不必定是连续排列,因此访问时候没法利用下标,而是必须从头结点开始,逐次遍历下一个节点直到寻找到目标。因此当须要快速访问对象时,数组无疑更有优点。
综上,链表适合元素数量不固定,须要两端存取且常常增减节点的状况。
请转到《数据结构:单链表》查看更详细内容!
LinkedList
链表在存储元素时,不只要存储元素的值,还必须存储每一个元素的下一个元素和上一个元素的信息。这就是LinkedList
链表的优势是,若是将元素插入到列表的中间位置,使用链表就会很快。在插入一个元素时,只须要修改上一个元素的Next引用和下一个元素的Previous引用,使它们引用所插入的元素。在List
链表的缺点是,链表元素只能一个接一个的访问,这须要较长时间来查找位于链表中间或尾部的元素。
LinkedList
在指定位置插入元素:AddAfter(),AddFirst()和AddLast();
删除指定位置的元素:Remove(),RemoveFirst(),RemoveLast();
搜索:Find(),FindLast()。
菜单树
C#中没有实现树的具体类,通常能够经过本身实现。
结点树包含:父结点(根结点的父结点为null)、子结点(List集合)、数据对象。
请转到《 数据结构:树》查看更详细的内容!
图状结构简称图,是另外一种非线性结构,它比树形结构更复杂。树形结构中的结点是一对多的关系,结点间具备明显的层次和分支关系。每一层的结点能够和下一层的多个结点相关,但只能和上一层的一个结点相关。而图中的顶点(把图中的数据元素称为顶点)是多对多的关系,即顶点间的关系是任意的,图中任意两个顶点之间均可能相关。也就是说,图的顶点之间无明显的层次关系,这种关系在现实世界中大量存在。所以,图的应用至关普遍,在天然科学、社会科学和人文科学等许多领域都有着很是普遍的应用。
c#没有实现图的数据结构,可是能够本身实现,参考以下
请转到《数据结构:图》查看更详细内容!
字典树,又称为单词查找树,Tire数,是一种树形结构,它是一种哈希树的变种。
典型应用是用于统计,排序和保存大量的字符串(不只限于字符串),常常被搜索引擎系统用于文本词频统计。
c#也没有实现字典树,能够本身实现,参考以下
请转到《数据结构:字典树》查看更详细内容!
请转到《字典树(Trie树)实现与应用》查看更详细内容!
利用字符串的公共前缀来减小查询时间,最大限度的减小无谓的字符串比较,查询效率比哈希树高。
Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现相似keyvalue的键值对,其中key一般可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。Hashtable中keyvalue键值对均为object类型,因此Hashtable能够支持任何类型的keyvalue键值对.
using System.Collections; using System.Collections.Generic;
//添加一个keyvalue键值对: HashtableObject.Add(key,value); //移除某个keyvalue键值对: HashtableObject.Remove(key); //移除全部元素: HashtableObject.Clear(); // 判断是否包含特定键key: HashtableObject.Contains(key);
遍历哈希表须要用到DictionaryEntry Object,代码以下: for(DictionaryEntry de in ht) //ht为一个Hashtable实例 { Console.WriteLine(de.Key); //de.Key对应于keyvalue键值对key Console.WriteLine(de.Value); //de.Key对应于keyvalue键值对value }
请转到《数据结构:哈希表》查看更详细内容!
表示索引鍵和值的集合。
[System.Runtime.InteropServices.ComVisible(false)] [System.Serializable] public class Dictionary<TKey,TValue> : System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>, System.Collections.Generic.IDictionary<TKey,TValue>, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>, System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>, System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>, System.Collections.IDictionary, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable
由于字典的实现方式就是哈希表的实现方式,只不过字典是类型安全的,也就是说当建立字典时,必须声明key和item的类型。
结论:Dictionary<K,V>是泛型的,当K或V是值类型时,其速度远远超过Hashtable。
因为 Hashtable 和 Dictionary 同时存在, 在使用场景上必然存在选择性, 并不任什么时候刻都能相互替代.
关键字和它在表中存储位置之间存在一种函数关系。这个函数咱们称为为哈希函数。
hash : 翻译为“散列”,就是把任意长度的输入,经过散列算法,变成固定长度的输出,该输出就是散列值。
这种转换是一种压缩映射,散列值的空间一般远小于输入的空间,不一样的输入可能会散列成相同的输出,因此不可能从散列值来惟一的肯定输入值,由此引出hash冲突。
简单的说就是一种将任意长度的消息压缩到固定长度的消息的函数。
hash冲突
就是键(key)通过hash函数获得的结果做为地址去存放当前的键值对(key-value)(这个是hashmap的存值方式),可是却发现该地址已经有人先来了,一山不容二虎,就会产生冲突。这个冲突就是hash冲突了。若是两个不一样对象的hashCode相同,这种现象称为hash冲突。
开发定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另外一个哈希地址p1,若是p1仍然冲突,再以p为基础,产生另外一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。这种方法有一个通用的再散列函数形式:
Hi=(H(key)+di)% m i=1,2,…,n
其中H(key)为哈希函数,m 为表长,di称为增量序列。增量序列的取值方式不一样,相应的再散列方式也不一样。主要有如下三种:
1) 线性探测再散列
2) 二次(平方)探测再散列
3) 伪随机探测再散列
再哈希法
这种方法是同时构造多个不一样的哈希函数:
Hi=RH1(key) i=1,2,…,k
当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突再也不产生。这种方法不易产生汇集,但增长了计算时间。
链地址法
将全部哈希地址相同的都连接在同一个链表中 ,于是查找、插入和删除主要在同义词链中进行。链地址法适用于常常进行插入和删除的状况。
hashmap就是用此方法解决冲突的。
创建一个公共溢出区
将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一概填入溢出表。
综上所述,找了相关的文档以后,发现C#自己没有封装部分数据结构,多是让你们本身发挥,也可能跟它当初设计的缘由有关,由于它不是专们为处理数据而诞生的。写完以后,发现写到这里还不够,因而将标题改成《数据结构,你还记得吗(中)》,接下来还要继续《数据结构,你还记得吗(下)》 未完待续!
其余系列的C#数据结构参考《C# 数据结构》
数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的索引查找复杂度小,为O(1);数组的特色是:寻址容易,插入和删除困难;
链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特色是:寻址困难,插入和删除容易。
那么咱们能不能综合二者的特性,作出一种寻址容易,插入删除也容易的数据结构?答案是确定的,这就是咱们要提起的哈希表。哈希表((Hash table)既知足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。
哈希表综合以上两个优势,但同时还有一个缺点,就是在连续查询的时候性能很是差。那怎么寻址容易,插入,删除也容易,连续查询也容易呢? 这个就引出了数据库底层采用的存储数据结构,B+树。