算法系列15天速成——第九天 队列
2017-10-31 15:23
621 查看
可能大家都知道,线性表的变种非常非常多,比如今天讲的“队列”,灰常有意思啊。
一:概念
队列是一个”先进先出“的线性表,牛X的名字就是“First in First Out(FIFO)”,
生活中有很多这样的场景,比如读书的时候去食堂打饭时的”排队“。当然我们拒绝插队。
二:存储结构
前几天也说过,线性表有两种”存储结构“,① 顺序存储,②链式存储。当然“队列”也脱离
不了这两种服务,这里我就分享一下“顺序存储”。
顺序存储时,我们会维护一个叫做”head头指针“和”tail尾指针“,分别指向队列的开头和结尾。
![](http://pic002.cnblogs.com/images/2011/214741/2011120115403595.png)
代码段如下:
三:常用操作
队列的操作一般分为:
①: 初始化队列。
②: 出队。
③: 入队。
④: 获取队头。
⑤: 获取队长。
1:初始化队列
这个很简单,刚才也说过了,队列是用一个head和tail的指针来维护。分别设置为0即可。
2:出队
看着“队列”的结构图,大家都知道,出队肯定跟head指针有关,需要做两件事情,
第一: 判断队列是否为空,这个我想大家都知道。
第二: 将head头指针向后移动一位,返回head移动前的元素,时间复杂度为O(1)。
![](http://pic002.cnblogs.com/images/2011/214741/2011120115530722.png)
代码段如下:
3:入队
这个跟”出队“的思想相反,同样也是需要做两件事情。
第一:判断队列是否已满。
第二:将tail指针向后移动一位,时间复杂度为O(1)。
![](http://pic002.cnblogs.com/images/2011/214741/2011120116261689.png)
代码段如下:
4: 获取队头
知道”出队“和”入队“的原理,相信大家都懂的如何进行”获取队头“操作,唯一不一样的就是
他是只读操作,不会破坏”队列“结构,时间复杂度为O(1)。
代码段如下:
5: 获取队长
大家都知道,我们是用数组来实现队列,所以千万不要想当然的认为数组长度是XXX,
我们维护的是一个head和tail的指针,所以长度自然就是tail-head咯,时间复杂度为O(1)。
![](http://pic002.cnblogs.com/images/2011/214741/2011120116274984.png)
代码段如下:
然后上一下总的运行代码:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
View
Code
![](http://pic002.cnblogs.com/images/2011/214741/2011120116184227.png)
三:顺序队列的缺陷
![](http://pic002.cnblogs.com/images/2011/214741/2011120116385389.png)
大家看这张图,不知道可有什么异样的感觉,在这种状态下,我入队操作,发现程序提示队列
已满,但是tnd我这个数组还有一个空间啊,是的,这就是所谓的“假溢出”。
四:循环队列
俗话说的好啊,“没有跨不过的坎”。
1: 概念
之所以叫“循环”,得益于神奇的“%”。他让队列的首位进行相连,形成了一个我们思维中的
“圈圈”。
2:循环公式
tail=(tail+1)%array.Length;
多看几眼,大家就看通了其中循环的道理,我要做成如下的图:
![](http://pic002.cnblogs.com/images/2011/214741/2011120116575324.png)
3:对循环的改造
先前看了一些资料,有的压根就是错的,有的说想要循环,就要牺牲一个单位的空间。
我觉得没必要。我既要循环又不牺牲空间,所以反射了一下framework中的Queue类。
改造后代码如下:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
View
Code
一:概念
队列是一个”先进先出“的线性表,牛X的名字就是“First in First Out(FIFO)”,
生活中有很多这样的场景,比如读书的时候去食堂打饭时的”排队“。当然我们拒绝插队。
二:存储结构
前几天也说过,线性表有两种”存储结构“,① 顺序存储,②链式存储。当然“队列”也脱离
不了这两种服务,这里我就分享一下“顺序存储”。
顺序存储时,我们会维护一个叫做”head头指针“和”tail尾指针“,分别指向队列的开头和结尾。
![](http://pic002.cnblogs.com/images/2011/214741/2011120115403595.png)
代码段如下:
1 #region 队列的数据结构 2 /// <summary> 3 /// 队列的数据结构 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 public class SeqQueue<T> 7 { 8 private const int maxSize = 100; 9 10 public int MaxSize 11 { 12 get { return maxSize; } 13 } 14 15 /// <summary> 16 /// 顺序队列的存储长度 17 /// </summary> 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)。
![](http://pic002.cnblogs.com/images/2011/214741/2011120115530722.png)
代码段如下:
1 #region 队列元素出队 2 /// <summary> 3 /// 队列元素出队 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="seqQueue"></param> 7 /// <returns></returns> 8 public T SeqQueueOut<T>(SeqQueue<T> 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)。
![](http://pic002.cnblogs.com/images/2011/214741/2011120116261689.png)
代码段如下:
1 #region 队列元素入队 2 /// <summary> 3 /// 队列元素入队 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="seqQueue"></param> 7 /// <param name="data"></param> 8 /// <returns></returns> 9 public SeqQueue<T> SeqQueueIn<T>(SeqQueue<T> 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 获取队头元素 /// <summary> /// 获取队头元素 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="seqQueue"></param> /// <returns></returns> public T SeqQueuePeek<T>(SeqQueue<T> seqQueue) { if (SeqQueueIsEmpty(seqQueue)) throw new Exception("队列已空,不能进行出队操作"); return seqQueue.data[seqQueue.head]; } #endregion
5: 获取队长
大家都知道,我们是用数组来实现队列,所以千万不要想当然的认为数组长度是XXX,
我们维护的是一个head和tail的指针,所以长度自然就是tail-head咯,时间复杂度为O(1)。
![](http://pic002.cnblogs.com/images/2011/214741/2011120116274984.png)
代码段如下:
1 /// <summary> 2 /// 获取队列长度 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 /// <param name="seqQueue"></param> 6 /// <returns></returns> 7 public int SeqQueueLen<T>(SeqQueue<T> seqQueue) 8 { 9 return seqQueue.tail - seqQueue.head; 10 }
然后上一下总的运行代码:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
View
Code
![](http://pic002.cnblogs.com/images/2011/214741/2011120116184227.png)
三:顺序队列的缺陷
![](http://pic002.cnblogs.com/images/2011/214741/2011120116385389.png)
大家看这张图,不知道可有什么异样的感觉,在这种状态下,我入队操作,发现程序提示队列
已满,但是tnd我这个数组还有一个空间啊,是的,这就是所谓的“假溢出”。
四:循环队列
俗话说的好啊,“没有跨不过的坎”。
1: 概念
之所以叫“循环”,得益于神奇的“%”。他让队列的首位进行相连,形成了一个我们思维中的
“圈圈”。
2:循环公式
tail=(tail+1)%array.Length;
多看几眼,大家就看通了其中循环的道理,我要做成如下的图:
![](http://pic002.cnblogs.com/images/2011/214741/2011120116575324.png)
3:对循环的改造
先前看了一些资料,有的压根就是错的,有的说想要循环,就要牺牲一个单位的空间。
我觉得没必要。我既要循环又不牺牲空间,所以反射了一下framework中的Queue类。
改造后代码如下:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
View
Code
![](http://pic002.cnblogs.com/images/2011/214741/2011120117033095.png)
相关文章推荐
- 算法系列15天速成——第九天 队列
- 算法系列15天速成——第九天 队列
- 算法系列15天速成 第九天 队列
- 算法系列15天速成——第九天 队列
- 算法系列15天速成——第九天 队列
- 算法系列15天速成——第九天 队列
- 算法系列15天速成——第一天 七大经典排序【上】
- 算法系列15天速成——第十四天 图【上】
- 算法系列15天速成——第二天 七大经典排序【中】
- 算法系列15天速成——第七天 线性表【上】
- 算法系列15天速成——第十一天 树操作(上)
- 算法系列15天速成——第十二天 树操作【中】
- 算法系列15天速成-第一天 七大经典序列(上)
- 算法系列15天速成——第十二天 树操作【中】
- 算法系列15天速成——第六天 五大经典查找【下】
- 基本算法系列15天速成
- 算法系列15天速成——第十五天 图【下】(大结局)
- 算法系列15天速成——第三天 七大经典排序【下】
- 算法系列15天速成——第八天 线性表【下】
- 算法系列15天速成——第十三天 树操作【下】