可能大家都知道,线性表的变种非常非常多,比如今天讲的“队列”,灰常有意思啊。
一:概念
队列是一个”先进先出“的线性表,牛X的名字就是“First in First Out(FIFO)”,
生活中有很多这样的场景,比如读书的时候去食堂打饭时的”排队“。当然我们拒绝插队。
二:存储结构
前几天也说过,线性表有两种”存储结构“,① 顺序存储,②链式存储。当然“队列”也脱离
不了这两种服务,这里我就分享一下“顺序存储”。
顺序存储时,我们会维护一个叫做”head头指针“和”tail尾指针“,分别指向队列的开头和结尾。
代码段如下:
1 #region 队列的数据结构
2 ///
3 /// 队列的数据结构
4 ///
5 ///
6 public class SeqQueue
7 {
8 private const int maxSize = 100;
9
10 public int MaxSize
11 {
12 get { return maxSize; }
13 }
14
15 ///
16 /// 顺序队列的存储长度
17 ///
18 public T[] data = new T[maxSize];
19
20 //头指针
21 public int head;
22
23 //尾指针
24 public int tail;
25
26 }
27 #endregion
三:常用操作
队列的操作一般分为:
①: 初始化队列。
②: 出队。
③: 入队。
④: 获取队头。
⑤: 获取队长。
1:初始化队列
这个很简单,刚才也说过了,队列是用一个head和tail的指针来维护。分别设置为0即可。
2:出队
看着“队列”的结构图,大家都知道,出队肯定跟head指针有关,需要做两件事情,
第一: 判断队列是否为空,这个我想大家都知道。
第二: 将head头指针向后移动一位,返回head移动前的元素,时间复杂度为O(1)。
代码段如下:
1 #region 队列元素出队
2 ///
3 /// 队列元素出队
4 ///
5 ///
6 ///
7 ///
8 public T SeqQueueOut(SeqQueue seqQueue)
9 {
10 if (SeqQueueIsEmpty(seqQueue))
11 throw new Exception("队列已空,不能进行出队操作");
12
13 var single = seqQueue.data[seqQueue.head];
14
15 //head指针自增
16 seqQueue.data[seqQueue.head++] = default(T);
17
18 return single;
19
20 }
21 #endregion
3:入队
这个跟”出队“的思想相反,同样也是需要做两件事情。
第一:判断队列是否已满。
第二:将tail指针向后移动一位,时间复杂度为O(1)。
代码段如下:
1 #region 队列元素入队
2 ///
3 /// 队列元素入队
4 ///
5 ///
6 ///
7 ///
8 ///
9 public SeqQueueSeqQueueIn (SeqQueue seqQueue, T data)
10 {
11 //如果队列已满,则不能进行入队操作
12 if (SeqQueueIsFull(seqQueue))
13 throw new Exception("队列已满,不能入队操作");
14
15 //入队操作
16 seqQueue.data[seqQueue.tail++] = data;
17
18 return seqQueue;
19 }
20 #endregion
4: 获取队头
知道”出队“和”入队“的原理,相信大家都懂的如何进行”获取队头“操作,唯一不一样的就是
他是只读操作,不会破坏”队列“结构,时间复杂度为O(1)。
代码段如下:
#region 获取队头元素
///
/// 获取队头元素
///
///
///
///
public T SeqQueuePeek(SeqQueue seqQueue)
{
if (SeqQueueIsEmpty(seqQueue))
throw new Exception("队列已空,不能进行出队操作");
return seqQueue.data[seqQueue.head];
}
#endregion
5: 获取队长
大家都知道,我们是用数组来实现队列,所以千万不要想当然的认为数组长度是XXX,
我们维护的是一个head和tail的指针,所以长度自然就是tail-head咯,时间复杂度为O(1)。
代码段如下:
1 ///
2 /// 获取队列长度
3 ///
4 ///
5 ///
6 ///
7 public int SeqQueueLen(SeqQueue seqQueue)
8 {
9 return seqQueue.tail - seqQueue.head;
10 }
然后上一下总的运行代码:
![](http://img.e-com-net.com/image/info8/b8d97b5613f94ed2ba791cad57d0b2ed.gif)
![](http://img.e-com-net.com/image/info8/2f88dd3f1cd145f59c0e47b51acdbd4b.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace SeqQueue
7 {
8 public class Program
9 {
10 static void Main(string[] args)
11 {
12 SeqQueueseqQueue = new SeqQueue ();
13
14 SeqQueueClass queueManage = new SeqQueueClass();
15
16 Console.WriteLine("目前队列是否为空:" + queueManage.SeqQueueIsEmpty(seqQueue) + "\n");
17
18 Console.WriteLine("将ID=1和ID=2的实体加入队列");
19 queueManage.SeqQueueIn(seqQueue, new Student() { ID = 1, Name = "hxc520", Age = 23 });
20 queueManage.SeqQueueIn(seqQueue, new Student() { ID = 2, Name = "一线码农", Age = 23 });
21
22 Display(seqQueue);
23
24 Console.WriteLine("将队头出队");
25 //将队头出队
26 var student = queueManage.SeqQueueOut(seqQueue);
27
28 Display(seqQueue);
29
30 //获取队顶元素
31 student = queueManage.SeqQueuePeek(seqQueue);
32
33 Console.Read();
34 }
35 //展示队列元素
36 static void Display(SeqQueueseqQueue)
37 {
38 Console.WriteLine("******************* 链表数据如下 *******************");
39
40 for (int i = seqQueue.head; i < seqQueue.tail; i++)
41 Console.WriteLine("ID:" + seqQueue.data[i].ID +
42 ",Name:" + seqQueue.data[i].Name +
43 ",Age:" + seqQueue.data[i].Age);
44
45 Console.WriteLine("******************* 链表数据展示完毕 *******************\n");
46 }
47 }
48
49 #region 学生数据实体
50 ///
51 /// 学生数据实体
52 ///
53 public class Student
54 {
55 public int ID { get; set; }
56
57 public string Name { get; set; }
58
59 public int Age { get; set; }
60 }
61 #endregion
62
63 #region 队列的数据结构
64 ///
65 /// 队列的数据结构
66 ///
67 ///
68 public class SeqQueue
69 {
70 private const int maxSize = 100;
71
72 public int MaxSize
73 {
74 get { return maxSize; }
75 }
76
77 ///
78 /// 顺序队列的存储长度
79 ///
80 public T[] data = new T[maxSize];
81
82 //头指针
83 public int head;
84
85 //尾指针
86 public int tail;
87
88 }
89 #endregion
90
91 #region 队列的基本操作
92 ///
93 /// 队列的基本操作
94 ///
95 public class SeqQueueClass
96 {
97 #region 队列的初始化操作
98 ///
99 /// 队列的初始化操作
100 ///
101 ///
102 ///
103 public SeqQueueSeqQueueInit (SeqQueue seqQueue)
104 {
105 seqQueue.head = 0;
106 seqQueue.tail = 0;
107
108 return seqQueue;
109 }
110 #endregion
111
112 #region 队列是否为空
113 ///
114 /// 队列是否为空
115 ///
116 ///
117 ///
118 ///
119 public bool SeqQueueIsEmpty(SeqQueue seqQueue)
120 {
121 //如果两指针重合,说明队列已经清空
122 if (seqQueue.head == seqQueue.tail)
123 return true;
124 return false;
125 }
126 #endregion
127
128 #region 队列是否已满
129 ///
130 /// 队列是否已满
131 ///
132 ///
133 ///
134 ///
135 public bool SeqQueueIsFull(SeqQueue seqQueue)
136 {
137 //如果尾指针到达数组末尾,说明队列已经满
138 if (seqQueue.tail == seqQueue.MaxSize)
139 return true;
140 return false;
141 }
142 #endregion
143
144 #region 队列元素入队
145 ///
146 /// 队列元素入队
147 ///
148 ///
149 ///
150 ///
151 ///
152 public SeqQueueSeqQueueIn (SeqQueue seqQueue, T data)
153 {
154 //如果队列已满,则不能进行入队操作
155 if (SeqQueueIsFull(seqQueue))
156 throw new Exception("队列已满,不能入队操作");
157
158 //入队操作
159 seqQueue.data[seqQueue.tail++] = data;
160
161 return seqQueue;
162 }
163 #endregion
164
165 #region 队列元素出队
166 ///
167 /// 队列元素出队
168 ///
169 ///
170 ///
171 ///
172 public T SeqQueueOut(SeqQueue seqQueue)
173 {
174 if (SeqQueueIsEmpty(seqQueue))
175 throw new Exception("队列已空,不能进行出队操作");
176
177 var single = seqQueue.data[seqQueue.head];
178
179 //head指针自增
180 seqQueue.data[seqQueue.head++] = default(T);
181
182 return single;
183
184 }
185 #endregion
186
187 #region 获取队头元素
188 ///
189 /// 获取队头元素
190 ///
191 ///
192 ///
193 ///
194 public T SeqQueuePeek(SeqQueue seqQueue)
195 {
196 if (SeqQueueIsEmpty(seqQueue))
197 throw new Exception("队列已空,不能进行出队操作");
198
199 return seqQueue.data[seqQueue.head];
200 }
201 #endregion
202
203 ///
204 /// 获取队列长度
205 ///
206 ///
207 ///
208 ///
209 public int SeqQueueLen(SeqQueue seqQueue)
210 {
211 return seqQueue.tail - seqQueue.head;
212 }
213 }
214 #endregion
215 }
三:顺序队列的缺陷
大家看这张图,不知道可有什么异样的感觉,在这种状态下,我入队操作,发现程序提示队列
已满,但是tnd我这个数组还有一个空间啊,是的,这就是所谓的“假溢出”。
四:循环队列
俗话说的好啊,“没有跨不过的坎”。
1: 概念
之所以叫“循环”,得益于神奇的“%”。他让队列的首位进行相连,形成了一个我们思维中的
“圈圈”。
2:循环公式
tail=(tail+1)%array.Length;
多看几眼,大家就看通了其中循环的道理,我要做成如下的图:
3:对循环的改造
先前看了一些资料,有的压根就是错的,有的说想要循环,就要牺牲一个单位的空间。
我觉得没必要。我既要循环又不牺牲空间,所以反射了一下framework中的Queue类。
改造后代码如下:
![](http://img.e-com-net.com/image/info8/b8d97b5613f94ed2ba791cad57d0b2ed.gif)
![](http://img.e-com-net.com/image/info8/2f88dd3f1cd145f59c0e47b51acdbd4b.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace SeqQueue
7 {
8 public class Program
9 {
10 static void Main(string[] args)
11 {
12 SeqQueueseqQueue = new SeqQueue ();
13
14 SeqQueueClass queueManage = new SeqQueueClass();
15
16 Console.WriteLine("目前队列是否为空:" + queueManage.SeqQueueIsEmpty(seqQueue) + "\n");
17
18 Console.WriteLine("将ID=1,2,3的实体加入队列\n");
19 queueManage.SeqQueueIn(seqQueue, new Student() { ID = 1, Name = "hxc520", Age = 23 });
20 queueManage.SeqQueueIn(seqQueue, new Student() { ID = 2, Name = "一线码农", Age = 23 });
21 queueManage.SeqQueueIn(seqQueue, new Student() { ID = 3, Name = "51cto", Age = 23 });
22
23 Console.WriteLine("\n当前队列个数:" + queueManage.SeqQueueLen(seqQueue) + "");
24
25 Console.WriteLine("\n*********************************************\n");
26
27 Console.WriteLine("我要出队了\n");
28 queueManage.SeqQueueOut(seqQueue);
29
30 Console.WriteLine("哈哈,看看跟顺序队列异样之处,我再入队,看是否溢出\n");
31 queueManage.SeqQueueIn(seqQueue, new Student() { ID = 4, Name = "博客园", Age = 23 });
32 Console.WriteLine("\n....一切正常,入队成功");
33
34 Console.WriteLine("\n当前队列个数:" + queueManage.SeqQueueLen(seqQueue) + "");
35
36 Console.Read();
37 }
38 }
39
40 #region 学生数据实体
41 ///
42 /// 学生数据实体
43 ///
44 public class Student
45 {
46 public int ID { get; set; }
47
48 public string Name { get; set; }
49
50 public int Age { get; set; }
51 }
52 #endregion
53
54 #region 队列的数据结构
55 ///
56 /// 队列的数据结构
57 ///
58 ///
59 public class SeqQueue
60 {
61 private const int maxSize = 3;
62
63 public int MaxSize
64 {
65 get { return maxSize; }
66 }
67
68 ///
69 /// 顺序队列的存储长度
70 ///
71 public T[] data = new T[maxSize];
72
73 //头指针
74 public int head;
75
76 //尾指针
77 public int tail;
78
79 //队列中有效的数字个数
80 public int size;
81 }
82 #endregion
83
84 #region 队列的基本操作
85 ///
86 /// 队列的基本操作
87 ///
88 public class SeqQueueClass
89 {
90 #region 队列的初始化操作
91 ///
92 /// 队列的初始化操作
93 ///
94 ///
95 ///
96 public SeqQueueSeqQueueInit (SeqQueue seqQueue)
97 {
98 seqQueue.size = seqQueue.head = seqQueue.tail = 0;
99
100 return seqQueue;
101 }
102 #endregion
103
104 #region 队列是否为空
105 ///
106 /// 队列是否为空
107 ///
108 ///
109 ///
110 ///
111 public bool SeqQueueIsEmpty(SeqQueue seqQueue)
112 {
113 //如果两指针重合,说明队列已经清空
114 if (seqQueue.size == 0)
115 return true;
116 return false;
117 }
118 #endregion
119
120 #region 队列是否已满
121 ///
122 /// 队列是否已满
123 ///
124 ///
125 ///
126 ///
127 public bool SeqQueueIsFull(SeqQueue seqQueue)
128 {
129 //采用循环队列后,头指针
130 if (seqQueue.size == seqQueue.MaxSize)
131 return true;
132 return false;
133 }
134 #endregion
135
136 #region 队列元素入队
137 ///
138 /// 队列元素入队
139 ///
140 ///
141 ///
142 ///
143 ///
144 public SeqQueueSeqQueueIn (SeqQueue seqQueue, T data)
145 {
146 //如果队列已满,则不能进行入队操作
147 if (SeqQueueIsFull(seqQueue))
148 throw new Exception("队列已满,还入啥队列啊!");
149
150 //采用循环队列,必须先赋值,在自增tail指针
151 seqQueue.data[seqQueue.tail] = data;
152 seqQueue.tail = (seqQueue.tail + 1) % seqQueue.MaxSize;
153
154 //队列实际元素增加
155 seqQueue.size++;
156
157 return seqQueue;
158 }
159 #endregion
160
161 #region 队列元素出队
162 ///
163 /// 队列元素出队
164 ///
165 ///
166 ///
167 ///
168 public T SeqQueueOut(SeqQueue seqQueue)
169 {
170 if (SeqQueueIsEmpty(seqQueue))
171 throw new Exception("队列已空,大哥,不要在出队了!");
172
173 //循环队列出队,展现的是head的灵活性
174 seqQueue.head = (seqQueue.head + 1) % seqQueue.MaxSize;
175
176 //队列实际元素递减
177 seqQueue.size--;
178
179 return seqQueue.data[seqQueue.head];
180 }
181 #endregion
182
183 #region 获取队头元素
184 ///
185 /// 获取队头元素
186 ///
187 ///
188 ///
189 ///
190 public T SeqQueuePeek(SeqQueue seqQueue)
191 {
192 if (SeqQueueIsEmpty(seqQueue))
193 throw new Exception("队列已空,不能进行出队操作");
194
195 return seqQueue.data[seqQueue.head];
196 }
197 #endregion
198
199 #region 获取队列长度
200 ///
201 /// 获取队列长度
202 ///
203 ///
204 ///
205 ///
206 public int SeqQueueLen(SeqQueue seqQueue)
207 {
208 return seqQueue.size;
209 }
210 #endregion
211 }
212 #endregion
213 }