zoukankan      html  css  js  c++  java
  • 算法系列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 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)。

       

    代码段如下:

     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 }

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

    View Code
      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 #endregion
    111
    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 #endregion
    127
    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 #endregion
    143
    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 #endregion
    164
    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 #endregion
    186
    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 #endregion
    202
    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 #endregion
    215 }

    三:顺序队列的缺陷

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

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

    四:循环队列

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

    1: 概念

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

           “圈圈”。

    2:循环公式

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

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

    3:对循环的改造

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

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

          改造后代码如下:

    View Code
      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 #endregion
    103
    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 #endregion
    119
    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 #endregion
    135
    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 #endregion
    160
    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 #endregion
    182
    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 #endregion
    198
    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 #endregion
    211 }
    212 #endregion
    213 }

  • 相关阅读:
    java反射
    sql语句
    menu
    亮度
    自定义View
    将多层级xml解析为Map
    Theme.AppCompat无全屏主题解决办法
    Android EditText 限制输入为ip类型
    请不要乱用Kotlin ? 空检查
    配置 jvisualvm 监控Java虚拟机
  • 原文地址:https://www.cnblogs.com/huangxincheng/p/2270932.html
Copyright © 2011-2022 走看看