面试复习——数据结构(四):队列

本文详细介绍了队列这一数据结构的基本概念,包括其FIFO(先进先出)特性、队头与队尾的概念。文章讨论了链式、顺序和循环三种队列的实现方式,并提供了具体的C语言实现代码。此外,还展示了队列在杨辉三角生成和迷宫寻路等实际问题中的应用。通过对队列的操作,如入队、出队,实现了不同场景下的问题求解。
摘要由CSDN通过智能技术生成

队列

队列的基本概念

队列(Queue) 也是运算受限的线性表

  • 只允许在表的一端进行插入,而在另一端进行删除→先进先出(First In First Out ,FIFO),先进入队列的成员总是先离开队列
  • 队头(front) :允许进行删除的一端
  • 队尾(rear) :允许进行插入的一端
  • 空队列:队列中没有元素
    队列图示

队列的设计

定义:

  • 队头(front):不存数据,只是指向队列队头
  • 队尾(rear)
    在这里插入图片描述

操作:

  • 创建一个空队列
  • 销毁已存在的队列
  • 将队列清为空队列
  • 判断是否为空队列
  • 返回队列的长度
  • 返回队列的队头元素
  • 向队列的队尾插入元素e
  • 删除队头元素,并返回其值
  • 从队头到队尾依次对队列的每个数据元素调用函数visit()

队列的具体实现

链式表示

链队列:用链表表示的队列

  • 数据元素结点,设有头结点
  • 队列的队头指针和队尾指针
typedef struct Node {
	ElemType data;
	struct Node *next;
}QNode;
typedef struct {
	QNode *front;
	QNode *rear;
}LinkedQueue;

// 1. 链队列的初始化,构造一个空队列
bool InitQueue(LinkedQueue *lq){
	lq->front=lq->rear=(QNode *)malloc(sizeof(QNode));
	if(!lq->front) return false;
	lq->front->next=NULL;
	return true;
}

// 2. 取队列的长度
int GetLen(LinkedQueue *lq){
	int count=0;
	QNode *p;
	if(IsQueueEmpty(lq)==0){
		p=lq->front->next;
		while(p!=lq->rear){
			count++;
			p=p->next;
		}
		count++;
	}
	return count;
}

// 3. 判断队列是否为空
int IsQueueEmpty(LinkedQueue *lq){
	if(lq->front == lq->rear) return 1;
	else return 0;
}

// 4. 查看队头元素
bool GetFront(LinkedQueue *lq,ElemType *e){
	if(lq->front == lq->rear)  return false;
	*e=lq->front->next->data;
	return ture; 
}
// 5. 元素入队(尾)
bool Enqueue(LinkedQueue *lq,ElemType e){
	QNode *p;
	p=(QNode *)malloc(sizeof(QNode));
	if(!p) return false;
	p->data =e; p->next=NULL;
	lq->rear->next=p; //修改尾指针
	lq->rear=p;
	return true;
}
//6. (队头)元素出队
bool Dequeue(LinkedQueue *lq,ElemType *e){
	QNode *p;
	if (lq->front == lq->rear) return false; 
			    	       //空队列的话,则出错
	p = lq->front->next;  //p指向第一个结点
	*e = p->data;
	lq->front->next =p->next; //修改头结点的指针
	if(lq->rear==p) //删仅有的一个元素时,需修改尾指针
		lq->rear=lq->front; 
	free(p);
	return true;
}

顺序表示

顺序队列:利用一组连续的存储单元(一维数组) 依次存放从队头到队尾的各个元素
非空队列里,队头指针始终指向队头元素,而队尾指针始终指向队尾元素的下一位置

  • 初始化:front=rear=0
  • 判队列为空:front==rear
  • 判队列满:rear==MAXQUEUESIZE
  • 入队:将新元素插入rear所指的位置,然后rear加1
  • 出队:删去front所指的元素,然后front加1并返回被删元素
#define MAXQUEUESIZE   100
typedef  struct  queue {
	ElemType  Queue_array[MAXQUEUESIZE] ;
	int  front; // 队头指针
	int  rear;  // 队尾指针
	int  queueSize;     //队列空间的大小
}SqQueue;

PS:存在假溢出问题!
在入队和出队操作中,头、尾指针只增加不减小,致使被删除元素的空间永远无法重新利用在这里插入图片描述

循环队列

循环队列:将为队列分配的向量空间看成为一个首尾相接的圆环

  • 在循环队列中进行出队、入队操作时,队头、队尾指针仍要加1
  • 但当队头、队尾指针到达MAXQUEUESIZE-1时,其加1操作的结果是指向0
  • 进队:在队尾加元素,然后rear = (rear+1) % MAXQUEUESIZE
  • 出队:取队头元素,然后front = (front+1) % MAXQUEUESIZE
  • 判断队空的条件(rear+1) % MAXQUEUESIZE == front(少用一个元素空间,让rear所指的单元始终为空。约定:以“队列头指针在队尾指针的下一个位置上”为队满的标志)←这只是一种判断队列满的方法,也可以增加队列计数器或标志位
#define MAXQUEUESIZE   100
typedef  struct  queue {
	ElemType *base;  // 动态分配的存储空间
	int  front; // 队头指针,若队列不空,指向队头元素
	int  rear;  // 队尾指针,若队列不空,指向队列尾元素的下一个位置
} CircularQueue;

// 队列初始化,构造一个空循环队列 
bool InitQueue(CircularQueue *cq){
	cq->base = (ElemType *)malloc 			(MAXQUEUESIZE*sizeof(ElemType));
	if(!cq->base) return false;
	cq->front=0;cq->rear=0;
	return true;
}

// 返回队列的元素个数,即队列的长度
int GetLen(CircularQueue *cq) {
	return ((cq->rear - cq->front + MAXQUEUESIZE)%MAXQUEUESIZE);
}

// 插入元素e为Q的新的队尾元素
bool Enqueue(CircularQueue *cq,ElemType e) {
	if((cq->rear+1)%MAXQUEUESIZE == cq->front)
		return false; //队列满
	cq->base[cq->rear]=e;
	cq->rear=(cq->rear+1) % MAXQUEUESIZE;
	return true;
}

// 若队列不空,则删除Q的队头元素,
// 用e返回其值,并返回true;  否则返回false
bool Dequeue(CircularQueue *cq,ElemType *e) {
	if(cq->front == cq->rear) return false;
	*e=cq->base[cq->front];
	cq->front=(cq->front +1) % MAXQUEUESIZE;
	return true;
}

队列的应用举例

杨辉三角/二项式系数生成

杨辉三角: C ( n , i ) = C ( n − 1 , i − 1 ) + C ( n − 1 , i ) C(n,i)= C(n-1,i-1) + C(n-1,i) C(n,i)=C(n1,i1)+C(n1,i)
=> 𝑁 𝑒 𝑥 𝑡 𝑅 𝑜 𝑤 𝑖 = 𝐶 𝑢 𝑟 𝑅 𝑜 𝑤 i − 1 + 𝐶 𝑢 𝑟 𝑅 𝑜 𝑤 𝑖 𝑁𝑒𝑥𝑡𝑅𝑜𝑤_𝑖=𝐶𝑢𝑟𝑅𝑜𝑤_{i-1}+ 𝐶𝑢𝑟𝑅𝑜𝑤_𝑖 NextRowi=CurRowi1+CurRowi
想法:用变量存 𝐶 𝑢 𝑟 𝑅 𝑜 𝑤 i − 1 𝐶𝑢𝑟𝑅𝑜𝑤_{i-1} CurRowi1,队头为 𝐶 𝑢 𝑟 𝑅 𝑜 𝑤 𝑖 𝐶𝑢𝑟𝑅𝑜𝑤_𝑖 CurRowi

int main(){
	//生成总共total_row行的杨辉三角值
	CircularQueue q; ElemType e; 
	int r,total_row, cur_row_i_1=0, cur_row_i=0,next_row_i;
	InitQueue(&q); Enqueue(&q,1); Enqueue(&q,1);
	for(r=1; r<total_row;r++){
		Enqueue(&q, 0);
		//用0来作为行的分隔
		for(int c=1;c<=r+2;c++) {
			Dequeue(&q,&e);
			cur_row_i = e;
			if (e) printf("%d ", cur_row_i);
			next_row_i = cur_row_i + cur_row_i_1;
			Enqueue(&q,next_row_i);
			cur_row_i_1=cur_row_i;
		}
		printf("\n");
	}
	return 0;
}

迷宫寻路-搜索问题,寻找从入口到出口的最短路径

题目见这篇文章中的迷宫问题:https://blog.csdn.net/weixin_45770491/article/details/125271327?spm=1001.2014.3001.5501

寻找从入口到出口的最短路径,修改栈版迷宫算法,利用队列针对迷宫进行广度优先搜索
迷宫:用2维数组表示
有待搜索的通道块:用队列表示
轨迹(搜索过的块):trajectories

起始方块入队列Q;
while (队列Q非空) {
	取Q的队头e;
	若e是终点,则 {
		从轨迹中回溯路径,
		打印路径,
		返回True
	}
	否则,将该方块记录到轨迹;
	依次取e的下一步可以走到的方块s;
	如果s是可通的方块 ,则 {
		则在该方块中记录该方块的前一个位置,
		将该方块入队,
		将该点设置为走过// FootPrint(s);
	}
}
返回False
/********************************************/
typedef struct Node {
    ElemType data;
    struct Node *next;
}QNode;

typedef struct {
    QNode *front;
    QNode *rear;
}LinkedQueue;
char maze[10][10];
//迷宫的墙:X;
//没有走过的通道块:空格;
//走过标记:*;路径:+
typedef struct{ //迷宫的坐标
    int r,c; //r 表示行,c表示列
} PosType;

//表示路径中的一通道块
typedef struct{  
	PosType seat;
    //通道块在迷宫中的坐标位置
    PosType former;      //从哪个块走过来的
    int di; //从此通道块走向下一个通道块的方向
} ElemType;
ElemType trajectories[1000]; 
int traP=0; 

运动会日程安排

题目
即——子集划分问题:将n个元素组成的集合A划分成k个互不相交的子集A1,A2,…,Ak (k≤n),使同一子集中的元素均无冲突关系
有同一运动员参加的项目抽象为**“冲突”关系**
项目之间的冲突关系为:
R = {(1,4),(4,8),(1,8),(1,7),(8,3),(1,0),(0,5),(1,5),(3,4),(5,6),(5,2),(6,2),(6,4)}
数据结构:

  • 设集合共有n个元素
  • 用队列sq存放集合元素
  • 用矩阵conflictMatrix[n][n]表示元素之间的冲突关系:
    conflictMatrix[i][j]=1, 如果i,j有冲突
    conflictMatrix[i][j]=0, 如果i,j无冲突
  • 用数组result[n]存放每个元素的子集/分组编号
  • 用工作数组clash[n]记录与第k组已入组元素有冲突的元素情况(当组号为k时)
    -每次新开辟一组时,令clash数组各分量的值均为0,当序号为 head 的元素入组时,将和该元素发生冲突的信息记入clash 数组
    -clash的引入可以减少重复察看conflictMatrix数组的时间

算法思想:
利用循环筛选
从第一个元素开始,无冲突的元素划归为一个子集;
再将剩下的元素重新找出互不冲突的元素,划归第二个子集
依次类推,直到所有元素都进入某个子集为止

初始化队列;全体集合元素入队列;
取队头元素,组号为1,clash的值被设置为队头元素在矩阵中的行值;
While(队列不空) {
  取队头元素x;
  若它与当前组的元素没有冲突,即Clash[x]==0 {
      在result中设置该元素对应的分组号;
      修改clash记录 (即,在原clash记录之上叠加上该元素的
      冲突情况)
      }
  否则,将x再次入队
  判断是否走完一轮 (即,若当前队列的队头的值小于前次
  取的队头的值,那么 走完一轮)
  若走完一轮,则:
	  取队头元素x,组号加1,数组clash初始化为全0
}

演示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值