队列
在世界上有很多东西,储存的数据并不需要在意中间部分的数据。
例如贪吃蛇,中间的身子部分不参与移动。每次移动只有尾巴消失,头增加一格。
像这样的东西,方法和属性就比列表少得多。因为也没什么能操作的。
Queue<int> queue = new Queue<int>();
入列
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
使用Enqueue方法可以把一个值添加到末尾处。
出列
int i1 = queue.Dequeue();
bool b1 = queue.TryDequeue(out int i2);
Dequeue方法会移除最开始的值。这个值同时会作为返回值被返回。
如果队列里没有元素,那么Dequeue方法会报错。
如果不想检查元素数量,可以使用TryDequeue方法,他使用out参数输出值。
方法本身返回bool值,指示是否成功输出。
瞄一眼
有时候需要先确定开头的值已经达到想要的状态,才拿出来。
这样也需要获得值,但不移除。可以使用Peek或TryPeek方法。
int i3 = queue.Peek();
bool b2 = queue.TryPeek(out int i4);
如果没有元素,Peek方法也会报错。
同样可以使用TryPeek方法代替,out参数输出值,方法本身返回是否成功获得值。
其他
Count属性指示当前剩余元素。
Contains方法确认指定元素是否已经存在。
EnsureCapacity扩大这个队列背后数组的容量,直到达到指定值。
栈
和队列相反,拿出来的是最近放进去的东西,最早放进去的东西拿的最晚。
像这样的路径点,如果新路径和上一个路径一样,那么移除一个路径点。
总体使用方法和队列差不多,只是放入和取出两个方法的名字变了。
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);//Push方法入栈
var i1 = stack.Pop();
var i2 = stack.Pop();//Pop方法出栈(移除最新放入的元素,并作为返回值返回)
var b1 = stack.TryPop(out int i3);//尝试出栈
var i4 = stack.Peek();//瞄一眼
var b2 = stack.TryPeek(out int i5);//尝试瞄一眼
双链表
链表的每个节点都是独立的。
数组如果想从中间插入一个数据,那么从这个位置到末尾的数据全部都要挪动。
删除数据也是,后面的数据全部都要往前挪一位。
链表的插入和删除就不需要这么复杂,只需要对自己和自己链接的节点操作就行了。
但是缺点也很大,节点记录了前一个节点和后一个节点的位置,多出了更多的空间占用。
并且如果想找到中间某个节点的位置,只能按着节点的指示顺着查找,每次查找都要进行一次遍历。
所以其他的数据集合背后都使用数组作为容器。数组作为最基本的结构仍然具有很多优势。
LinkedList<int>linkedList=new LinkedList<int>();
添加元素
linkedList.AddFirst(0);
linkedList.AddLast(1);
链表储存的是节点类,但收尾处的数据添加可以直接使用数据添加,他会自动帮你打包成节点类。
获取节点
双链表的First和Last属性可以获得首尾的节点。
或者,Find方法和FindLast方法,可以搜索包含指定值的节点。
LinkedListNode<int> L1 = linkedList.First;
LinkedListNode<int> L2 = linkedList.Last;
LinkedListNode<int> L3 = linkedList.Find(0);
LinkedListNode<int> L4 = linkedList.Find(1);
在找不到节点时,会返回null而不会报错(特指First和Last属性)。
节点属性
每个节点都包含了本身的值,上一个节点,下一个节点,所属的双链表。
c#的这个双链表用于作为链式储存的数据集,不能提供一般链表的拆分、周期等功能。
int i = L1.Value;//获得值
LinkedListNode<int> L2 = L1.Previous;//获得上一个节点
LinkedListNode<int> L3 = L1.Next;//获得下一个节点
LinkedList<int> link = L1.List;//获得所属链表