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

 更新时间:2013年11月15日 15:00:29   投稿:mdxy-dxy  
可能大家都知道,线性表的变种非常非常多,比如今天讲的“队列”,灰常有意思啊

一:概念

          队列是一个”先进先出“的线性表,牛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;
        }

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

复制代码 代码如下:

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

      改造后代码如下:

复制代码 代码如下:

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
}


 

相关文章

  • 在PB中如何让用户只能修改新增的数据

    在PB中如何让用户只能修改新增的数据

    一些数据库系统或者某些数据表只允许用户添加数据,而不能修改或者删除以往的记录,此时我们就必须在程序进行控制。有些程序员通过不显示以往的数据来保证,下面我们介绍一种既可以看到原始记录,有不容许用户修改这些记录的方法
    2008-11-11
  • windows下gitbash安装教程(小白教程)

    windows下gitbash安装教程(小白教程)

    这篇文章主要介绍了windows下gitbash安装教程(小白教程),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 计算机网络日常练习题,每天进步一点点

    计算机网络日常练习题,每天进步一点点

    下面小编就为大家带来一篇计算机网络的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以帮到你
    2021-09-09
  • 导致爬虫无法使用的原因探讨解决

    导致爬虫无法使用的原因探讨解决

    随着互联网的普及和发展,爬虫技术也越来越多地被应用到各个领域,然而,在实际使用中,爬虫可能会遇到各种问题导致无法正常工作,本文将探讨导致爬虫无法使用的原因,并给出相应的解决方法
    2023-10-10
  • WebStorm 遇到的问题总结

    WebStorm 遇到的问题总结

    这篇文章主要介绍了WebStorm 遇到的问题总结的相关资料,需要的朋友可以参考下
    2017-03-03
  • 永恒之蓝实战教程之Mac通过Metasploit攻击Server2008的详细过程

    永恒之蓝实战教程之Mac通过Metasploit攻击Server2008的详细过程

    这篇文章主要介绍了永恒之蓝实战教程 Mac通过Metasploit攻击Server2008,首先准备一个Server2008,主要功能是使网络上的机器能够共享计算机文件、打印机、串行端口和通讯等资源,需要的朋友可以参考下
    2022-08-08
  • 关于IE11修改User-agent不再支持document.all等

    关于IE11修改User-agent不再支持document.all等

    这篇文章主要介绍了关于IE11修改User-agent不再支持document.all等,需要的朋友可以参考下
    2015-12-12
  • 在ubuntu18.04版本安装vscode(2种)

    在ubuntu18.04版本安装vscode(2种)

    这篇文章主要介绍了在ubuntu18.04版本安装vscode,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • 在github上怎样修改语言设置

    在github上怎样修改语言设置

    这篇文章主要介绍了在github上怎样修改语言设置问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • 使用Postman测试需要授权的接口问题

    使用Postman测试需要授权的接口问题

    Postman是一款免费的http模拟请求工具,常用来测试开发接口。实际场景中,很多接口是需要授权才能使用,这篇文章主要介绍了用Postman测试需要授权的接口,需要的朋友可以参考下
    2022-06-06

最新评论