数据结构,那么怎么去理解这个数据结构这4个字呢,从某种角度来说,就是这一长串数据集合的特性,约束条件.之前的文章中我们对于数据结构的讨论,都会说它的时间复杂度,也就是使用某个数据结构的性能,这里我们还是一样,掌握了多种数据结构,有助于简化代码,提高代码可读性.
栈与队列
从本质上来说,栈与队列就是有了特殊限制的数组,栈与队列一般处理临时的数据,
比如在游戏中,一些消息的推送就是存放在队列中.
栈
就像前面说的,栈存取数据的方式和数组一样,也是在内存中开辟一段连续的空间来存放数据,但是相对于数组来言,它有以下3条约束.
- 栈只能在末尾加入数据
- 栈只能在末尾删除数据
- 栈只能读取末尾的数据
我们可以想象一个薯片筒,商家在包装的时候,是这样:
包装即将完成
当厂家包装完之后消费者买到后消费这筒薯片开始打开食用,是这样:
所以,厂家最后一个放进薯片筒中的薯片会第一个被消费者拿出来吃掉.
在栈中,加入数据称之为"压栈",
在栈中,取出数据称之为"出栈".
让我们来简单的举个例子加强记忆一下.
在VS编译器语法检查中括号都是成对出现的,如果不成对,就会报错,那么这样的用栈来实现该怎么做呢?
如果编译器识别到了一个"{",那么后面必然有一个"}"与之对应.如果括号有嵌套,那么如果不是同类型的括号,要等同类型的括号与之对应,具体如下说明:
括号的判断无非只会有3种情况
- ( … 有左无右
- … ) 有右无左
- { … ) 左右括号不匹配
算法设计思想如下:
挨个读取一行代码,如果读到的字符不是括号类型的,就忽略,再读下一个,如果读到了左括号,那么就将它压到栈里.
如果读到了右括号,那么它就要判断3个东西
-
如果此时的栈中只有一个右括号,那么显然是"有右无左"的错误
-
如果此时的栈中有左括号,但是与这个右括号并不匹配,那么显然就是"左右括号不匹配"的错误
-
如果栈中有与它匹配的左括号,那么就代表已经匹配完毕,那么就可以将这对括号弹出,也就是出栈.
其实还有一个例子也能说明栈的作用,比如你写代码的时候写错了会撤销,那么你写的每一个步骤,每一个代码都是存在栈中的,当你按下Ctrl+z的时候也就是一个出栈的过程.
下面是详细的栈的使用方法:
1 using System;
2 using System.Collections;
3
4 namespace CollectionsApplication
5 {
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 Stack st = new Stack();
11
12 st.Push('A');//压栈
13 st.Push('M');
14 st.Push('G');
15 st.Push('W');
16
17 Debug.Log("Current stack: ");
18
19 foreach (char c in st)
20 {
21 Console.Write(c + " ");
22 }
23//输出的结果是W G M A,为什呢?想想薯片筒
24 st.Push('V');
25 st.Push('H');
26 Debug.Log("The next poppable value in stack: {0}",
27 st.Peek());
28 Debug.Log("Current stack: ");
29 foreach (char c in st)
30 {
31 Debug.Log(c + " ");
32 }
33 Debug.Log("");
34
35 Debug.Log("Removing values ");
36 st.Pop();
37 st.Pop();
38 st.Pop();
39
40 Debug.Log("Current stack: ");
41 foreach (char c in st)
42 {
43 Debug.Log(c + " ");
44 }
45 }
46 }
47}
Stack 类的方法和属性
下表列出了 Stack 类的一些常用的方法:
队列
如果将栈比喻成薯片筒的话,那么对列就可以比喻成排队,排在前面的人最先得到服务并也可以提前推出排队,队列和栈相比,有差别的就是它的取数据的方式.
但是为了好区分,我们还是写下它的特性
- 只能在末尾插入数据
- 只能读取排在第一位的数据
- 只能删除排在第一位的数据
也就是遵循先进先出的原则.这样的数据结构,在游戏消息推送消息中很有必要这样设计.
1 using System;
2 using System.Collections;
3
4 namespace CollectionsApplication
5{
6 class Program
7 {
8 static void Main(string[] args)
9 {
10 Queue q = new Queue();
11
12 q.Enqueue('A');
13 q.Enqueue('M');
14 q.Enqueue('G');
15 q.Enqueue('W');
16
17 Debug.Log("Current queue: ");
18 foreach (char c in q)
19 {
20 Debug.Log(c + " ");
21 }
22//这里就是按照顺序输出的了,想想排队
23 Console.WriteLine();
24 q.Enqueue('V');
25 q.Enqueue('H');
26 Console.WriteLine("Current queue: ");
27 foreach (char c in q)
28 Debug.Log(c + " ");
29 Console.WriteLine();
30 Console.WriteLine("Removing some values ");
31 char ch = (char)q.Dequeue();
32 Console.WriteLine("The removed value: {0}", ch);
33 ch = (char)q.Dequeue();
34 Debug.Log("The removed value: {0}", ch);
35
36 }
37 }
38}
Queue 类的方法和属性
下表列出了 Queue 类的一些常用的 属性:
下表列出了 Queue 类的一些常用的 方法:
总结
栈与队列用在合适的地方就能少写很多代码,其实这么说是不适合的,应该说的精巧简练的代码,学会了栈和队列,就解锁了下一章内容,基于栈的递归算法,这也是一个基本的算法.下篇文章见.