算法系列15天速成——第九天 队列

原文:算法系列15天速成——第九天 队列

可能大家都知道,线性表的变种非常非常多,比如今天讲的“队列”,灰常有意思啊。

一:概念

队列是一个”先进先出“的线性表,牛X的名字就是“First in First Out(FIFO)”,

生活中有很多这样的场景,比如读书的时候去食堂打饭时的”排队“。当然我们拒绝插队。

二:存储结构

前几天也说过,线性表有两种”存储结构“,① 顺序存储,②链式存储。当然“队列”也脱离

不了这两种服务,这里我就分享一下“顺序存储”。

顺序存储时,我们会维护一个叫做”head头指针“和”tail尾指针“,分别指向队列的开头和结尾。

代码段如下:

 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 MaxSize11         {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)。

代码段如下:

 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)。

代码段如下:

 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)。

代码段如下:

 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         }

然后上一下总的运行代码:

  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             SeqQueue<Student> seqQueue = new SeqQueue<Student>(); 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(SeqQueue<Student> seqQueue) 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     /// <summary> 51 /// 学生数据实体 52 /// </summary> 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     /// <summary> 65 /// 队列的数据结构 66 /// </summary> 67 /// <typeparam name="T"></typeparam> 68     public class SeqQueue<T> 69     { 70         private const int maxSize = 100; 71  72         public int MaxSize 73         { 74             get { return maxSize; } 75         } 76  77         /// <summary> 78 /// 顺序队列的存储长度 79 /// </summary> 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     /// <summary> 93 /// 队列的基本操作 94 /// </summary> 95     public class SeqQueueClass 96     { 97         #region 队列的初始化操作 98         /// <summary> 99 /// 队列的初始化操作100 /// </summary>101 /// <typeparam name="T"></typeparam>102 /// <param name="seqQueue"></param>103         public SeqQueue<T> SeqQueueInit<T>(SeqQueue<T> seqQueue)104         {105             seqQueue.head = 0;106             seqQueue.tail = 0;107 108             return seqQueue;109         }110         #endregion111 112         #region 队列是否为空113         /// <summary>114 /// 队列是否为空115 /// </summary>116 /// <typeparam name="T"></typeparam>117 /// <param name="seqQueue"></param>118 /// <returns></returns>119         public bool SeqQueueIsEmpty<T>(SeqQueue<T> seqQueue)120         {121             //如果两指针重合,说明队列已经清空122             if (seqQueue.head == seqQueue.tail)123                 return true;124             return false;125         }126         #endregion127 128         #region 队列是否已满129         /// <summary>130 /// 队列是否已满131 /// </summary>132 /// <typeparam name="T"></typeparam>133 /// <param name="seqQueue"></param>134 /// <returns></returns>135         public bool SeqQueueIsFull<T>(SeqQueue<T> seqQueue)136         {137             //如果尾指针到达数组末尾,说明队列已经满138             if (seqQueue.tail == seqQueue.MaxSize)139                 return true;140             return false;141         }142         #endregion143 144         #region 队列元素入队145         /// <summary>146 /// 队列元素入队147 /// </summary>148 /// <typeparam name="T"></typeparam>149 /// <param name="seqQueue"></param>150 /// <param name="data"></param>151 /// <returns></returns>152         public SeqQueue<T> SeqQueueIn<T>(SeqQueue<T> 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         #endregion164 165         #region 队列元素出队166         /// <summary>167 /// 队列元素出队168 /// </summary>169 /// <typeparam name="T"></typeparam>170 /// <param name="seqQueue"></param>171 /// <returns></returns>172         public T SeqQueueOut<T>(SeqQueue<T> 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         #endregion186 187         #region 获取队头元素188         /// <summary>189 /// 获取队头元素190 /// </summary>191 /// <typeparam name="T"></typeparam>192 /// <param name="seqQueue"></param>193 /// <returns></returns>194         public T SeqQueuePeek<T>(SeqQueue<T> seqQueue)195         {196             if (SeqQueueIsEmpty(seqQueue))197                 throw new Exception("队列已空,不能进行出队操作");198 199             return seqQueue.data[seqQueue.head];200         }201         #endregion202 203         /// <summary>204 /// 获取队列长度205 /// </summary>206 /// <typeparam name="T"></typeparam>207 /// <param name="seqQueue"></param>208 /// <returns></returns>209         public int SeqQueueLen<T>(SeqQueue<T> seqQueue)210         {211             return seqQueue.tail - seqQueue.head;212         }213     }214     #endregion215 }

三:顺序队列的缺陷

大家看这张图,不知道可有什么异样的感觉,在这种状态下,我入队操作,发现程序提示队列

已满,但是tnd我这个数组还有一个空间啊,是的,这就是所谓的“假溢出”。

四:循环队列

俗话说的好啊,“没有跨不过的坎”。

1: 概念

之所以叫“循环”,得益于神奇的“%”。他让队列的首位进行相连,形成了一个我们思维中的

“圈圈”。

2:循环公式

tail=(tail+1)%array.Length;

多看几眼,大家就看通了其中循环的道理,我要做成如下的图:

3:对循环的改造

先前看了一些资料,有的压根就是错的,有的说想要循环,就要牺牲一个单位的空间。

我觉得没必要。我既要循环又不牺牲空间,所以反射了一下framework中的Queue类。

改造后代码如下:

  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             SeqQueue<Student> seqQueue = new SeqQueue<Student>(); 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     /// <summary> 42 /// 学生数据实体 43 /// </summary> 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     /// <summary> 56 /// 队列的数据结构 57 /// </summary> 58 /// <typeparam name="T"></typeparam> 59     public class SeqQueue<T> 60     { 61         private const int maxSize = 3; 62  63         public int MaxSize 64         { 65             get { return maxSize; } 66         } 67  68         /// <summary> 69 /// 顺序队列的存储长度 70 /// </summary> 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     /// <summary> 86 /// 队列的基本操作 87 /// </summary> 88     public class SeqQueueClass 89     { 90         #region 队列的初始化操作 91         /// <summary> 92 /// 队列的初始化操作 93 /// </summary> 94 /// <typeparam name="T"></typeparam> 95 /// <param name="seqQueue"></param> 96         public SeqQueue<T> SeqQueueInit<T>(SeqQueue<T> seqQueue) 97         { 98             seqQueue.size = seqQueue.head = seqQueue.tail = 0; 99 100             return seqQueue;101         }102         #endregion103 104         #region 队列是否为空105         /// <summary>106 /// 队列是否为空107 /// </summary>108 /// <typeparam name="T"></typeparam>109 /// <param name="seqQueue"></param>110 /// <returns></returns>111         public bool SeqQueueIsEmpty<T>(SeqQueue<T> seqQueue)112         {113             //如果两指针重合,说明队列已经清空114             if (seqQueue.size == 0)115                 return true;116             return false;117         }118         #endregion119 120         #region 队列是否已满121         /// <summary>122 /// 队列是否已满123 /// </summary>124 /// <typeparam name="T"></typeparam>125 /// <param name="seqQueue"></param>126 /// <returns></returns>127         public bool SeqQueueIsFull<T>(SeqQueue<T> seqQueue)128         {129             //采用循环队列后,头指针130             if (seqQueue.size == seqQueue.MaxSize)131                 return true;132             return false;133         }134         #endregion135 136         #region 队列元素入队137         /// <summary>138 /// 队列元素入队139 /// </summary>140 /// <typeparam name="T"></typeparam>141 /// <param name="seqQueue"></param>142 /// <param name="data"></param>143 /// <returns></returns>144         public SeqQueue<T> SeqQueueIn<T>(SeqQueue<T> 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         #endregion160 161         #region 队列元素出队162         /// <summary>163 /// 队列元素出队164 /// </summary>165 /// <typeparam name="T"></typeparam>166 /// <param name="seqQueue"></param>167 /// <returns></returns>168         public T SeqQueueOut<T>(SeqQueue<T> 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         #endregion182 183         #region 获取队头元素184         /// <summary>185 /// 获取队头元素186 /// </summary>187 /// <typeparam name="T"></typeparam>188 /// <param name="seqQueue"></param>189 /// <returns></returns>190         public T SeqQueuePeek<T>(SeqQueue<T> seqQueue)191         {192             if (SeqQueueIsEmpty(seqQueue))193                 throw new Exception("队列已空,不能进行出队操作");194 195             return seqQueue.data[seqQueue.head];196         }197         #endregion198 199         #region 获取队列长度200         /// <summary>201 /// 获取队列长度202 /// </summary>203 /// <typeparam name="T"></typeparam>204 /// <param name="seqQueue"></param>205 /// <returns></returns>206         public int SeqQueueLen<T>(SeqQueue<T> seqQueue)207         {208             return seqQueue.size;209         }210         #endregion211     }212     #endregion213 }

时间: 2024-10-08 08:56:54

算法系列15天速成——第九天 队列的相关文章

算法系列15天速成——第十天 栈

原文:算法系列15天速成--第十天 栈 今天跟大家聊聊栈,在程序设计中,栈的使用还是非常广泛的,比如有“括号匹配问题“,”html结构匹配问题“. 所以说掌握了”栈“的使用,对我们学习算法还是很有帮助的. 一: 概念 栈,同样是一种特殊的线性表,是一种Last In First Out(LIFO)的形式,现实中有很多这样的例子, 比如:食堂中的一叠盘子,我们只能从顶端一个一个的取. 二:存储结构 ”栈“不像”队列“,需要两个指针来维护,栈只需要一个指针就够了,这得益于栈是一种一端受限的线性表.

算法系列15天速成——第三天 七大经典排序【下】

原文:算法系列15天速成--第三天 七大经典排序[下] 今天跟大家聊聊最后三种排序: 直接插入排序,希尔排序和归并排序. 直接插入排序: 这种排序其实蛮好理解的,很现实的例子就是俺们斗地主,当我们抓到一手乱牌时,我们就要按照大小梳理扑克,30秒后, 扑克梳理完毕,4条3,5条s,哇塞......  回忆一下,俺们当时是怎么梳理的. 最左一张牌是3,第二张牌是5,第三张牌又是3,赶紧插到第一张牌后面去,第四张牌又是3,大喜,赶紧插到第二张后面去, 第五张牌又是3,狂喜,哈哈,一门炮就这样产生了.

算法系列15天速成——第四天 五大经典查找【上】

原文:算法系列15天速成--第四天 五大经典查找[上] 在我们的生活中,无处不存在着查找,比如找一下班里哪个mm最pl,猜一猜mm的芳龄....... 对的这些都是查找. 在我们的算法中,有一种叫做线性查找. 分为:顺序查找. 折半查找. 查找有两种形态: 分为:破坏性查找,   比如有一群mm,我猜她们的年龄,第一位猜到了是23+,此时这位mm已经从我脑海里面的mmlist中remove掉了. 哥不找23+的,所以此种查找破坏了原来的结构. 非破坏性查找, 这种就反之了,不破坏结构. 顺序查找

算法系列15天速成——第一天 七大经典排序【上】

原文:算法系列15天速成--第一天 七大经典排序[上] 今天是开篇,得要吹一下算法,算法就好比程序开发中的利剑,所到之处,刀起头落. 针对现实中的排序问题,算法有七把利剑可以助你马道成功. 首先排序分为四种: 交换排序: 包括冒泡排序,快速排序. 选择排序: 包括直接选择排序,堆排序. 插入排序: 包括直接插入排序,希尔排序. 合并排序: 合并排序. 那么今天我们讲的就是交换排序,我们都知道,C#类库提供的排序是快排,为了让今天玩的有意思点, 我们设计算法来跟类库提供的快排较量较量.争取KO对手

算法系列15天速成——第七天 线性表【上】

原文:算法系列15天速成--第七天 线性表[上] 人活在社会上不可能孤立,比如跟美女有着千丝万缕的关系,有的是一对一,有的是一对多,有的是多对多. 哈哈,我们的数据也一样,存在这三种基本关系,用术语来说就是: <1>  线性关系. <2>  树形关系. <3>  网状关系. 一: 线性表 1 概念: 线性表也就是关系户中最简单的一种关系,一对一. 如:学生学号的集合就是一个线性表. 2 特征: ① 有且只有一个“首元素“. ② 有且只有一个“末元素”. ③ 除“末元素”

算法系列15天速成——第六天 五大经典查找【下】

原文:算法系列15天速成--第六天 五大经典查找[下] 大家是否感觉到,树在数据结构中大行其道,什么领域都要沾一沾,碰一碰. 就拿我们前几天学过的排序就用到了堆和今天讲的”二叉排序树“,所以偏激的说,掌握的树你就是牛人了. 今天就聊聊这个”五大经典查找“中的最后一个”二叉排序树“. 1. 概念: <1> 其实很简单,若根节点有左子树,则左子树的所有节点都比根节点小. 若根节点有右子树,则右子树的所有节点都比根节点大. <2> 如图就是一个”二叉排序树“,然后对照概念一比较比较. 2

算法系列15天速成——第十一天 树操作(上)

原文:算法系列15天速成--第十一天 树操作(上) 最近项目赶的紧,歇了一个星期没写博客了,趁周末继续写这个系列. 先前我们讲的都是“线性结构”,他的特征就是“一个节点最多有一个”前驱“和一个”后继“.那么我们今天讲的树会是怎样的呢? 我们可以对”线性结构“改造一下,变为”一个节点最多有一个"前驱“和”多个后继“.哈哈,这就是我们今天说的”树“. 一: 树 我们思维中的”树“就是一种枝繁叶茂的形象,那么数据结构中的”树“该是怎么样呢?对的,他是一种现实中倒立的树. 1:术语 其实树中有很多术语的

算法系列15天速成——第五天 五大经典查找【中】

原文:算法系列15天速成--第五天 五大经典查找[中] 大家可否知道,其实查找中有一种O(1)的查找,即所谓的秒杀. 哈希查找: 对的,他就是哈希查找,说到哈希,大家肯定要提到哈希函数,呵呵,这东西已经在我们脑子里面形成 固有思维了.大家一定要知道“哈希“中的对应关系. 比如说: ”5“是一个要保存的数,然后我丢给哈希函数,哈希函数给我返回一个”2",那么此时的”5“ 和“2”就建立一种对应关系,这种关系就是所谓的“哈希关系”,在实际应用中也就形成了”2“是key,”5“是value. 那么有的

算法系列15天速成——第二天 七大经典排序【中】

原文:算法系列15天速成--第二天 七大经典排序[中] 首先感谢朋友们对第一篇文章的鼎力支持,感动中.......  今天说的是选择排序,包括“直接选择排序”和“堆排序”. 话说上次“冒泡排序”被快排虐了,而且“快排”赢得了内库的重用,众兄弟自然眼红,非要找快排一比高下. 这不今天就来了两兄弟找快排算账. 1.直接选择排序: 先上图: 说实话,直接选择排序最类似于人的本能思想,比如把大小不一的玩具让三岁小毛孩对大小排个序, 那小孩首先会在这么多玩具中找到最小的放在第一位,然后找到次小的放在第二位