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

五一回老家待了几天,昨天晚上回来看见有很多网友留言,谢谢支持,继续写点东东奉献给大家

 

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

 

一:概念

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

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

 

二:存储结构

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

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

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

 

代码段如下:

复制代码
#region 队列的数据结构
     /// <summary>
 /// 队列的数据结构
 /// </summary>
 /// <typeparam name="T"></typeparam>
     public class SeqQueue<T>
     {
         private const int maxSize = 100;
 
         public int MaxSize
         {
             get { return maxSize; }
         }
 
         /// <summary>
 /// 顺序队列的存储长度
 /// </summary>
         public T[] data = new T[maxSize];
 
         //头指针
         public int head;
 
         //尾指针
         public int tail;
 
     }
     #endregion

 复制代码

 

三:常用操作

      队列的操作一般分为:

      ①: 初始化队列。

      ②:   出队。

      ③: 入队。

      ④: 获取队头。

      ⑤: 获取队长。

 

1:初始化队列

        这个很简单,刚才也说过了,队列是用一个head和tail的指针来维护。分别设置为0即可。

 

2:出队

       看着“队列”的结构图,大家都知道,出队肯定跟head指针有关,需要做两件事情,

       第一: 判断队列是否为空,这个我想大家都知道。

       第二: 将head头指针向后移动一位,返回head移动前的元素,时间复杂度为O(1)。

   

代码段如下:

复制代码 
#region 队列元素出队
         /// <summary>
 /// 队列元素出队
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public T SeqQueueOut<T>(SeqQueue<T> seqQueue)
         {
             if (SeqQueueIsEmpty(seqQueue))
                 throw new Exception("队列已空,不能进行出队操作");
 
             var single = seqQueue.data[seqQueue.head];
 
             //head指针自增
             seqQueue.data[seqQueue.head++] = default(T);
 
             return single;
 
         }
         #endregion



3:入队

      这个跟”出队“的思想相反,同样也是需要做两件事情。

      第一:判断队列是否已满。

      第二:将tail指针向后移动一位,时间复杂度为O(1)。

代码段如下:

复制代码
#region 队列元素入队
         /// <summary>
 /// 队列元素入队
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <param name="data"></param>
 /// <returns></returns>
         public SeqQueue<T> SeqQueueIn<T>(SeqQueue<T> seqQueue, T data)
         {
             //如果队列已满,则不能进行入队操作
             if (SeqQueueIsFull(seqQueue))
                 throw new Exception("队列已满,不能入队操作");
 
             //入队操作
             seqQueue.data[seqQueue.tail++] = data;
 
             return seqQueue;
         }
         #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)。

代码段如下:

复制代码
/// <summary>
 /// 获取队列长度
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public int SeqQueueLen<T>(SeqQueue<T> seqQueue)
         {
             return seqQueue.tail - seqQueue.head;
         }

 

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

View Code 
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 
 namespace SeqQueue
 {
     public class Program
     {
         static void Main(string[] args)
         {
             SeqQueue<Student> seqQueue = new SeqQueue<Student>();
 
             SeqQueueClass queueManage = new SeqQueueClass();
 
             Console.WriteLine("目前队列是否为空:" + queueManage.SeqQueueIsEmpty(seqQueue) + "\n");
 
             Console.WriteLine("将ID=1和ID=2的实体加入队列");
             queueManage.SeqQueueIn(seqQueue, new Student() { ID = 1, Name = "hxc520", Age = 23 });
             queueManage.SeqQueueIn(seqQueue, new Student() { ID = 2, Name = "一线码农", Age = 23 });
 
             Display(seqQueue);
 
             Console.WriteLine("将队头出队");
             //将队头出队
             var student = queueManage.SeqQueueOut(seqQueue);
 
             Display(seqQueue);
 
             //获取队顶元素
             student = queueManage.SeqQueuePeek(seqQueue);
 
             Console.Read();
         }
         //展示队列元素
         static void Display(SeqQueue<Student> seqQueue)
         {
             Console.WriteLine("******************* 链表数据如下 *******************");
 
             for (int i = seqQueue.head; i < seqQueue.tail; i++)
                 Console.WriteLine("ID:" + seqQueue.data[i].ID +
                                   ",Name:" + seqQueue.data[i].Name +
                                   ",Age:" + seqQueue.data[i].Age);
 
             Console.WriteLine("******************* 链表数据展示完毕 *******************\n");
         }
     }
 
     #region 学生数据实体
     /// <summary>
 /// 学生数据实体
 /// </summary>
     public class Student
     {
         public int ID { get; set; }
 
         public string Name { get; set; }
 
         public int Age { get; set; }
     }
     #endregion
 
     #region 队列的数据结构
     /// <summary>
 /// 队列的数据结构
 /// </summary>
 /// <typeparam name="T"></typeparam>
     public class SeqQueue<T>
     {
         private const int maxSize = 100;
 
         public int MaxSize
         {
             get { return maxSize; }
         }
 
         /// <summary>
 /// 顺序队列的存储长度
 /// </summary>
         public T[] data = new T[maxSize];
 
         //头指针
         public int head;
 
         //尾指针
         public int tail;
 
     }
     #endregion
 
     #region 队列的基本操作
     /// <summary>
 /// 队列的基本操作
 /// </summary>
     public class SeqQueueClass
     {
         #region 队列的初始化操作
         /// <summary>
 /// 队列的初始化操作
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
         public SeqQueue<T> SeqQueueInit<T>(SeqQueue<T> seqQueue)
         {
             seqQueue.head = 0;
             seqQueue.tail = 0;
 
             return seqQueue;
         }
         #endregion
 
         #region 队列是否为空
         /// <summary>
 /// 队列是否为空
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public bool SeqQueueIsEmpty<T>(SeqQueue<T> seqQueue)
         {
             //如果两指针重合,说明队列已经清空
             if (seqQueue.head == seqQueue.tail)
                 return true;
             return false;
         }
         #endregion
 
         #region 队列是否已满
         /// <summary>
 /// 队列是否已满
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public bool SeqQueueIsFull<T>(SeqQueue<T> seqQueue)
         {
             //如果尾指针到达数组末尾,说明队列已经满
             if (seqQueue.tail == seqQueue.MaxSize)
                 return true;
             return false;
         }
         #endregion
 
         #region 队列元素入队
         /// <summary>
 /// 队列元素入队
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <param name="data"></param>
 /// <returns></returns>
         public SeqQueue<T> SeqQueueIn<T>(SeqQueue<T> seqQueue, T data)
         {
             //如果队列已满,则不能进行入队操作
             if (SeqQueueIsFull(seqQueue))
                 throw new Exception("队列已满,不能入队操作");
 
             //入队操作
             seqQueue.data[seqQueue.tail++] = data;
 
             return seqQueue;
         }
         #endregion
 
         #region 队列元素出队
         /// <summary>
 /// 队列元素出队
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public T SeqQueueOut<T>(SeqQueue<T> seqQueue)
         {
             if (SeqQueueIsEmpty(seqQueue))
                 throw new Exception("队列已空,不能进行出队操作");
 
             var single = seqQueue.data[seqQueue.head];
 
             //head指针自增
             seqQueue.data[seqQueue.head++] = default(T);
 
             return single;
 
         }
         #endregion
 
         #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
 
         /// <summary>
 /// 获取队列长度
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public int SeqQueueLen<T>(SeqQueue<T> seqQueue)
         {
             return seqQueue.tail - seqQueue.head;
         }
     }
     #endregion
 }

 

 

 

三:顺序队列的缺陷

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

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

 

四:循环队列

 

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

 

1: 概念

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

       “圈圈”。

 

2:循环公式

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

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

 

3:对循环的改造

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

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

      改造后代码如下:

 

View Code 
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 
 namespace SeqQueue
 {
     public class Program
     {
         static void Main(string[] args)
         {
             SeqQueue<Student> seqQueue = new SeqQueue<Student>();
 
             SeqQueueClass queueManage = new SeqQueueClass();
 
             Console.WriteLine("目前队列是否为空:" + queueManage.SeqQueueIsEmpty(seqQueue) + "\n");
 
             Console.WriteLine("将ID=1,2,3的实体加入队列\n");
             queueManage.SeqQueueIn(seqQueue, new Student() { ID = 1, Name = "hxc520", Age = 23 });
             queueManage.SeqQueueIn(seqQueue, new Student() { ID = 2, Name = "一线码农", Age = 23 });
             queueManage.SeqQueueIn(seqQueue, new Student() { ID = 3, Name = "51cto", Age = 23 });
 
             Console.WriteLine("\n当前队列个数:" + queueManage.SeqQueueLen(seqQueue) + "");
 
             Console.WriteLine("\n*********************************************\n");
 
             Console.WriteLine("我要出队了\n");
             queueManage.SeqQueueOut(seqQueue);
 
             Console.WriteLine("哈哈,看看跟顺序队列异样之处,我再入队,看是否溢出\n");
             queueManage.SeqQueueIn(seqQueue, new Student() { ID = 4, Name = "博客园", Age = 23 });
             Console.WriteLine("\n....一切正常,入队成功");
 
             Console.WriteLine("\n当前队列个数:" + queueManage.SeqQueueLen(seqQueue) + "");
 
             Console.Read();
         }
     }
 
     #region 学生数据实体
     /// <summary>
 /// 学生数据实体
 /// </summary>
     public class Student
     {
         public int ID { get; set; }
 
         public string Name { get; set; }
 
         public int Age { get; set; }
     }
     #endregion
 
     #region 队列的数据结构
     /// <summary>
 /// 队列的数据结构
 /// </summary>
 /// <typeparam name="T"></typeparam>
     public class SeqQueue<T>
     {
         private const int maxSize = 3;
 
         public int MaxSize
         {
             get { return maxSize; }
         }
 
         /// <summary>
 /// 顺序队列的存储长度
 /// </summary>
         public T[] data = new T[maxSize];
 
         //头指针
         public int head;
 
         //尾指针
         public int tail;
 
         //队列中有效的数字个数
         public int size;
     }
     #endregion
 
     #region 队列的基本操作
     /// <summary>
 /// 队列的基本操作
 /// </summary>
     public class SeqQueueClass
     {
         #region 队列的初始化操作
         /// <summary>
 /// 队列的初始化操作
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
         public SeqQueue<T> SeqQueueInit<T>(SeqQueue<T> seqQueue)
         {
             seqQueue.size = seqQueue.head = seqQueue.tail = 0;
 
             return seqQueue;
         }
         #endregion
 
         #region 队列是否为空
         /// <summary>
 /// 队列是否为空
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public bool SeqQueueIsEmpty<T>(SeqQueue<T> seqQueue)
         {
             //如果两指针重合,说明队列已经清空
             if (seqQueue.size == 0)
                 return true;
             return false;
         }
         #endregion
 
         #region 队列是否已满
         /// <summary>
 /// 队列是否已满
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public bool SeqQueueIsFull<T>(SeqQueue<T> seqQueue)
         {
             //采用循环队列后,头指针
             if (seqQueue.size == seqQueue.MaxSize)
                 return true;
             return false;
         }
         #endregion
 
         #region 队列元素入队
         /// <summary>
 /// 队列元素入队
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <param name="data"></param>
 /// <returns></returns>
         public SeqQueue<T> SeqQueueIn<T>(SeqQueue<T> seqQueue, T data)
         {
             //如果队列已满,则不能进行入队操作
             if (SeqQueueIsFull(seqQueue))
                 throw new Exception("队列已满,还入啥队列啊!");
 
             //采用循环队列,必须先赋值,在自增tail指针
             seqQueue.data[seqQueue.tail] = data;
             seqQueue.tail = (seqQueue.tail + 1) % seqQueue.MaxSize;
 
             //队列实际元素增加
             seqQueue.size++;
 
             return seqQueue;
         }
         #endregion
 
         #region 队列元素出队
         /// <summary>
 /// 队列元素出队
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public T SeqQueueOut<T>(SeqQueue<T> seqQueue)
         {
             if (SeqQueueIsEmpty(seqQueue))
                 throw new Exception("队列已空,大哥,不要在出队了!");
 
             //循环队列出队,展现的是head的灵活性
             seqQueue.head = (seqQueue.head + 1) % seqQueue.MaxSize;
 
             //队列实际元素递减
             seqQueue.size--;
 
             return seqQueue.data[seqQueue.head];
         }
         #endregion
 
         #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
 
         #region 获取队列长度
         /// <summary>
 /// 获取队列长度
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="seqQueue"></param>
 /// <returns></returns>
         public int SeqQueueLen<T>(SeqQueue<T> seqQueue)
         {
             return seqQueue.size;
         }
         #endregion
     }
     #endregion
 }

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值