算法系列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;
}
然后上一下总的运行代码
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
}
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程