5.数据结构与算法:队列

一.队列的介绍

1.1队列

队列是日常生活中最常见的,例如,到食堂买饭要排队,去银行要排队,到车站买票要排队,操作系统的进程需要排队等等。和栈相似,队列也是一种操作受限制的线性结构,我们限制在一端插入(称为:入队),在另一端删除(称为:出队),先进入队的成员总是先离开队列;因此,,队列也称做先进先出的线性表。

1.2队列的基本概念

(1).队列:是一种特殊的线性表,只允许在表的一端进行插人 ,而在另一端进行删除
(2).队首:允许进行删除的一端称为队首,用队首指针front表示。
(3).队尾:允许进行插人的-端称为队尾.用队尾指针rear表示。
说明:队列中没有元素时称为空队列。在空队列中依次加人元素a1,a2,…,an,之后.a1是队首元素,an是队尾元素。人队只能在队尾插人,出队的次序也只能是a1,a2,a3,…,an,即队列的修改是依次的先进先出的原则进行的。

二.顺序队列

2.1顺序队列

顺序队列,采用顺序存储结构存储的队列简称为顺序队列,顺序队列也采用数组来实现,要设定两个整数变量才指示队头和队尾的位置,称为队头指针front 和队尾指针 rear ,顺序队列的类型定义如下:

//创建队列顺序队列的自定义类型
#define maxsize 100
typedef int Elemtype;
typedef struct Snode
{
	Elemtype data[maxsize];
	int front, rear;                                   //队头指针,队尾指针
}SqQueue;

说明:
(1).设置Q是指向定义好的顺序队列的指针,初始Q->rear = Q->front =0;
(2).入队操作时因为初始队首和队尾都是0号的位置,这时,将元素插入在队尾的位置后,Q->rear再加1,所以顺序队列中的队尾指针rear指向队尾元素的下一个位置。出队操作时,需先将队列首元素取出,然后Q->front加1;
(3).Q->data[Q->front]存放队首元素的值,Q->data[Q->rear-1],存放队尾元素的值。
(4).队列空的条件是Q->rear 等于 Q->front ,队列满的条件是Q->rear等于QUEUESIZE(队列最大值)

2.2循环队列

1.假溢出现象
初始化队列Q时,Q->rear==Q- > front=0;入队时,新元素存入队尾指针所指向的单元,队尾指针再往后移动一个;出队运算时 ,取出队首所指向指针所指向单元的元素的值。队首指针再往后移动一个。 也就是说,在非空顺序队列中 .队头指针始终指向当前的队头元素,而队尾指针始终指向队尾元素的下一个单元。

随着人队和出队的不断进行,整个队列不断后移,当Q ->rear== QUEUESIZE时,尾指针无法再移动则表示队满,但却不是真正的队满,因为经过出队操作后,队列前面出现了一些空单元。由于入队只能在队尾进行,队首之前的空存储单元无法使用,我们把这种现象称为“假溢出”,即存储空间未满,可是不能再做入队运算。
解决“假溢出”问题可以有两种方法:
(1)平移元素:把元素平移到队列的首部,但这种方法效率低。
(2)将新元素插人到第一个位置 上,构成循环队列,人队 和出队仍按“先进先出”的原则进行。但这种方法操作效率、空间利用率高。

因此,为了解决“假溢出”问题,我们一般采用循环队列的方法。即把顺序队列想象成一个首尾相接环状结构,即规定最后.个单元的后继为第一一个单元,并称这种环状数组表示的队列为循环队列。采用循环队列存储的队列,当尾指针指向队尾单元时,插入一个元素后 ,尾指针加1不再是指向队列存储空间之外,而是让尾指针指向第一个存储单元是空的,可以继续插入新元素。循环队列的结构体定义与顺序队列一致。将队列的存储空间构造成首尾相接的环状结构,存储在这样结构的队列称为循环队列(Cireular Queue)。在循环队列中进行出队、人队操作时,头尾指针仍要加1.向后移动。只不过当头尾指针指向上界(QUEUESIZE 1)时,其加1操作的结果是应当指向的下界0。

为了有效地区分队列满还是空,我们有多种方法可以采取,比较典型的有如下三种:
(1)多设一个逻辑变量存放队列满还是空。
(2)多使用一个整型变量做计数器来记录队列中元素的总数(即队列长度)。若相等则认为队满(注意:rear所指的单元始终为空)。
(3)少用一个元素的空间,约定人队前,测试尾指针在循环意义下加1后是否等于头指针,若相等则认为队满(注意:rear所指的单元始终为空)。
为了解决这一问题我们在本教材里采用第三种方法,即规定循环队列中始终留一个空的元素空间,即尾指针rear 所指向的空间永远是没有元素的。我们称这种方法为“队尾加1追上队首即为队列满”。

2.3循环队列的基本运算

基本运算包括:队列的初始化,判队空,判队满,入队,出队等操作;
下面,附代码参考:

在这里插入代码片:
#include <iostream>
#include <stdlib.h>
using namespace std;
//创建队列顺序队列的自定义类型
#define maxsize 100
typedef int Elemtype;
typedef struct Snode
{
	Elemtype data[maxsize];
	int front, rear;                                   //队头指针,队尾指针
}SqQueue;

SqQueue* InitSqQueue()                                 //初始化队列
{
	SqQueue* Q;
	Q = (SqQueue*)malloc(sizeof(SqQueue));             //给队列申请空间
	if (!Q)
	{
		return 0;
	}
	else
	{
		Q->front = Q->rear = 0;                       //初始队首与队尾指针都指向0
		return Q;
	}
}
void AssignQueue(SqQueue* Q)    //顺序栈的输入
{
	int i, lenghth;
	cout << "请输入顺序栈的长度" << endl;                            //输入栈的长度
	cin >> lenghth;
	cout << "请根据设定的长依次输入顺序栈的元素(用空格隔开)" << endl;
	for (int i = 0; i < lenghth; i++)
	{
		cin >> Q->data[i];                                          //输入栈的元素
		Q->rear = (Q->rear+1)%maxsize;                                                   //栈顶不断向后移动
	}
}


//循环队列的入队操作
//算法思路
//1.判断队列是否已满,若已经满了,给出溢出提示,若没有,则进行下一步
//2.将新元素赋值给队尾元素位置
//3.队尾指针加1
int EnQueue(SqQueue* Q, Elemtype x)
{
	if ((Q->rear + 1) % maxsize == Q->front)        //判断队列是否已满
	{
		cout << "队列已经满了" << endl;
		return 0;
	}
	else
	{
		Q->data[Q->rear] = x;                      //将新的值赋值给队列的队尾
		Q->rear = (Q->rear + 1) % maxsize;         //循环队列的队尾始终指向队尾的下一个空间
		return 1;
	}
}

//循环队列的出队运算
//删除循环队列的队首元素,并通过指针变量将队首元素的值带出,即出队运算
//1.判断是否队空,对空则无元素出队,给出提示返回信息,若是非空队列的值出队,接下来下一步
//2.将队首元素的值赋值给指针变量所指的存储空间
//3.队首值加1;
int DeleQueue(SqQueue* Q, Elemtype* x)
{
	if (Q->rear == Q->front)                           //判断是否队空,对空则无元素出队
	{
		return 0;
	}
	else
	{
		*x = Q->data[Q->front];                        //将队首释放
		Q->front = (Q->front + 1) % maxsize;           //队首位置向后移动
		return 1;
	}
}

//顺序栈的输出显示
void OputSqlist(SqQueue* Q)
{
	int i;
	int sum = Q->rear;
	int toal = Q->front;
	cout << "顺序表长度为:" << sum - toal << endl;
	cout << "依次输出顺序表的元素:" << endl;
	for (i = toal; i < sum ; i++)
	{
		cout << Q->data[i] << ' ';     //遍历顺序表,并打印在控制台上
	}
}

int main()
{
	SqQueue* Q = InitSqQueue();    //创建循环队列
	Elemtype k, u;
	if (Q == 0)
	{
		cout << "初始化失败" << endl;
	}
	else
	{
		cout << "初始化成功" << endl;
		AssignQueue(Q);
		OputSqlist(Q);
		cout << endl;
		cout << "请输入需要入队的值:" << endl;
		cin >> k;
		EnQueue(Q,k);
		OputSqlist(Q);
		cout << endl;
		cout << "出队显示为:" << endl;
		DeleQueue(Q, &u);
		OputSqlist(Q);
	}
	return 0;
}

三.链队列

3.1.链队列介绍

队列不仅可以采用顺序的存储结构,也可以采用链表的存储结构,称为链队列;与链表类似,通常为了操作方便会比我们前面链表的程序多设置一个头节点,并设置一个队头指针和一个队尾指针,但与链表的不同之处在于,链队列的插入在队尾进行,删除操作在对头进行。

3.2链队列的基本运算

链队列的基本运算包括初始化,判空,入队,出队;
下面附代码,方便理解:

#include<iostream>
#include <stdlib.h>
using namespace std;

typedef int Elemtype;
//结点定义
typedef struct Qnode
{
	Elemtype data;      
	struct Qnode* next;
}Qnode;
//链队列定义
typedef struct
{
	Qnode* front;      //设置队头指针
	Qnode* rear;       //设置队尾指针
	int length;        //计算队的长度
}QLinklist;

int InitQueue(QLinklist* Q)    //队列初始话
{
	Q->front = Q->rear = (Qnode*)malloc(sizeof(Qnode));  //将两个指针申请空间
	if (!Q->front)
	{
		return 0;
	}
	else
	{
		Q->front->next = NULL;       //让队头指针下一个元素空间指向空
		Q->length = 0;
		return 1;
	}
}

int QueueEmpty(QLinklist* Q)
{
	if (Q->front == Q->rear)     //判断队头指针于队尾指针是否为空
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

int EnQueue(QLinklist* Q, Elemtype e)
{
	Qnode* p;
	p = (Qnode*)malloc(sizeof(Qnode));   //设置一个插入指针
	if (!p)
	{
		return 0;
	}
	else
	{
		p->data = e;            //取得需要插入的新值
		p->next = NULL;         //指针指向的值一定是从队尾插入的
		Q->rear->next = p;     
		Q->rear = p;            //赋值给队尾,这样队尾就是空的的了
		Q->length++;
		return 1;
	}
}

int DeQueue(QLinklist* Q, Elemtype* e)
{
	Qnode* p;
	if (QueueEmpty(Q))         //先判断队列是否为空
	{
		return 0;
	}
	p = Q->front->next;        //先让头结点指向下一个有数据的值(即首元素结点)
	*e = p->data;              //再将这个首元素结点用中间变量替换
	Q->front->next = p->next;  //最后让首元素结点的下一个指针,作为头节点的下一个首元素结点

	//如果删除的结点是尾结点,将队尾结点指针指向头节点
	if (p == Q->rear)
	{
		Q->rear = Q->front;
	}
	free(p);
	Q->length--;
	return 1;
}

void OutputQueue(QLinklist* Q)
{
	Qnode* p;
	int i;
	p = Q->front->next;       //从头节点的首元素开始打印,毕竟,先进先出
	while (p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
}


int main()
{
	QLinklist* Q = (QLinklist*)malloc(sizeof(QLinklist));
	Elemtype k, e;
	k = InitQueue(Q);
	if (k == 0)
	{
		cout << "初始化失败" << endl;
	}
	else
	{
		cout << "初始化成功" << endl;
		EnQueue(Q, 5);   //将52022依次入队
		EnQueue(Q, 2);
		EnQueue(Q, 0);
		EnQueue(Q, 2);
		EnQueue(Q, 2);
		cout << "入队显示:" << endl;
		OutputQueue(Q);
		cout << endl;
		cout << "出队显示:" << endl;
		DeQueue(Q, &e);
		OutputQueue(Q);
	}
	return 0;
}

四.例题

4.1字符串回文的判断。

“回文”是指正读和反读均相同的字符串;例如,ababa是“回文”字符串。试设计一个算法,利用顺序栈和循环队列的基本运算,判断一个字符串是否是回文。
分析:所谓“回文”,是指正读和反读均相同的字符串。 顺序栈的特点是“先进后出”,即,出栈是逆序;而循环队列的特点是“先进先出”,即出队是顺序。所以只要先将字符串依次入栈入队,然后依次将出栈的字符和出队的字符进行比较,如果完全一致即是回文;否则不是回文

代码参考:

#include <iostream>
#include <stdlib.h>
using namespace std;
//创建队列顺序队列的自定义类型
#define maxsize 100
typedef int Elemtype;
typedef struct Snode
{
	Elemtype data[maxsize];
	int front, rear;                                   //队头指针,队尾指针
}SqQueue;

SqQueue* InitSqQueue()                                 //初始化队列
{
	SqQueue* Q;
	Q = (SqQueue*)malloc(sizeof(SqQueue));             //给队列申请空间
	if (!Q)
	{
		return 0;
	}
	else
	{
		Q->front = Q->rear = 0;                       //初始队首与队尾指针都指向0
		return Q;
	}
}

typedef struct
{
	Elemtype date[maxsize];
	int top;                                              //top栈顶元素,top实际并不是指针类型,存放的是栈顶元素的下标
}SqStack;

SqStack* InitSqStack()                                   //顺序栈的初始化
{
	SqStack* s;
	s = (SqStack*)malloc(sizeof(SqStack));               //申请顺序栈的空间
	if (!s)
	{
		return NULL;                                     //申请失败返回0
	}
	else
	{
		s->top = -1;                                     //申请成功设置为空栈,这样初始化成功
		return s;
	}
}

//顺序栈的插入
//将一个新的元素加入栈中,通常称为入栈和压栈
//算法思路:
//1.判断栈是否满,如果栈满了,给出提示,出错返回
//2.栈顶加1
//3.新元素赋值给栈顶
//4.完成返回
int Push(SqStack* s, Elemtype x)
{
	if (s->top == maxsize - 1)                          //判断栈是否满了
	{                                                   //可以改改define的值,这样看看程序会不会出错
		return 0;
	}
	else
	{
		s->top++;                                       //栈顶加1   
		s->date[s->top] = x;                            //将新元素的值赋值新栈顶
		return 1;
	}
}

//顺序栈的删除(出栈)
//将栈顶的元素删除
//算法思路:
//1.判断栈是否为空,若是空栈,则没有元素出栈,给出提示出错返回
//2.将栈顶元素赋值给x指针所指向的变量
//3.栈顶指针减1
//4.完成返回
int delepop(SqStack* s, Elemtype* u)
{
	if (s->top == -1)
	{
		cout << "此栈为空栈" << endl;                             //判断是否为空栈(插入的就判断是否已经满了,删除判断是否为空)
		return 0;
	}
	else
	{
		*u = s->date[s->top];                                     //将栈的元素赋值给一个新元素指针存着,之后在栈顶减一
		s->top--;      //其实这个函数还是在这个数组中,但它不在这个栈中,如果你加回来,在输出,还是能看到原来的数
		return 1;
	}
}

//循环队列的入队操作
//算法思路
//1.判断队列是否已满,若已经满了,给出溢出提示,若没有,则进行下一步
//2.将新元素赋值给队尾元素位置
//3.队尾指针加1
int EnQueue(SqQueue* Q, Elemtype x)
{
	if ((Q->rear + 1) % maxsize == Q->front)        //判断队列是否已满
	{
		cout << "队列已经满了" << endl;
		return 0;
	}
	else
	{
		Q->data[Q->rear] = x;                      //将新的值赋值给队列的队尾
		Q->rear = (Q->rear + 1) % maxsize;         //循环队列的队尾始终指向队尾的下一个空间
		return 1;
	}
}

//循环队列的出队运算
//删除循环队列的队首元素,并通过指针变量将队首元素的值带出,即出队运算
//1.判断是否队空,对空则无元素出队,给出提示返回信息,若是非空队列的值出队,接下来下一步
//2.将队首元素的值赋值给指针变量所指的存储空间
//3.队首值加1;
int DeleQueue(SqQueue *Q, Elemtype *x)
{
	if (Q->rear == Q->front)                           //判断是否队空,对空则无元素出队
	{
		return 0;
	}
	else
	{
		*x = Q->data[Q->front];                        //将队首释放
		Q->front = (Q->front + 1) % maxsize;           //队首位置向后移动
		return 1;
	}
}

int main()
{
	SqQueue* Q = InitSqQueue();    //创建循环队列
	SqStack* S = InitSqStack();    //创建顺序栈
	Elemtype ch, x,y;
	if (S == 0 || Q == 0)
	{
		cout << "初始化失败" << endl;
	}
	else
	{
		cout << "请依次输入字符" << endl;
		ch = getchar();
		while (ch != '\n')
		{
			Push(S, ch);       //字符串入栈
			EnQueue(Q, ch);    //字符串入队
			ch = getchar();
		}
		while (S->top != -1)
		{
			delepop(S, &x);
			DeleQueue(Q, &y);
			if (x != y)
			{
				cout << "输入的不是回文数" << endl;
				break;
			}
		}
		if (S->top == -1)
		{
			cout << "输入的字符串是回文数" << endl;
		}
	}
	return 0;
} 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

The endeavor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值