考研数据结构之链队

提示:善不积不足以成名,恶不积不足以灭身


前言

提示:为什么要引用链队?
这个就说来话长了 ,这里我们就长话短说,因为有的时候并不能确定队伍到底有多长,尽管你可能告诉我 不是有动态顺序队列吗,人他总是贪婪的 他嫌弃他个动态顺序队列申请空间,还要拷贝 不够快,你可能会说 这个不是有缺点吗 不方便查找什么的 但是这里是队列呀,不需要进行中间某个位置进行查找什么的 我们只需要定义两个指针一个用来指向头 一个用来指向yi巴就可以了


提示:以下是本篇文章正文内容,下面案例可供参考

一、如何定义链队的结构体

链队列 就如他的名字一样既有链表的性,每一个结点不仅存放数据 也有存放下一个结点的指针域,又有队列的性质只能从头出从yi巴进入 所以可以想象应该要有两个指针 才方便找头尾 要不然每一次都要遍历 来确定尾部 ,链表的头部插入删除都容易 ,链表的尾部有指针的情况下插入容易,因为删除都需要前一个结点将待删除结点的前一个结点的指针域置为NULL,所以这里我们使用链表的头作为队列的头,链表的尾 作为队列的尾 还有就是链表需要不需要选择带头结点的呢 这里个人认为是不需要的,因为 我们在头部插入 尾部删除 这里格式是定下来的 所以也就可以不需要头节点(好像有点跑偏了)你写头结点当然也没有问题

typedef struct QNode
{
	int data;//数据域,存放数据元素
	struct QNode *next;//指针域
}QNode,*QueuePoint;
typedef struct
{
	QNode* front;//头指针
	QNode* rear;//尾指针
}LinkQueue;

这里我们看了一下,大多数书上写的都是带头节点 这里我们也是用带头结点的。

二、代码分块

假如我定义一个链队,LinkQueue Q; 则头指针是Q.front 尾指针是Q.rear ,如上定义 我们将 Q.front是指向头结点的指针所以就是头指针 Q.front ->next就是指向首结点的指针; 而Q.front->next->data 就是首结点的数据域 Q.rear 是指向尾结点的指针 Q.rear->data 就是尾结点的数据很重要!!!! 对于结点不清楚的请看
其中详细说明了几个结点

1.打印

void PrintQueue(LQueue &Q){
	QNode* P=Q.front->next;//此时P就是指向首结点的  注意首结点与头结点的区别 
	cout<<"此时队列中的元素是(左边是队头)";
	for(QNode *i=P;i!=NULL;i=i->next){
		cout<<i->base<<" ";
	} 
	cout<<endl;
}

2、初始化

带头结点的初始化要申请一个结点插入 其他就如链的初始化一样 给头结点的后继赋值为NULL,但是不要忘了初始化两个指针都要指向头结点

bool InitQueue(LQueue &Q){
	//申请一个结点作为头节点 因为我们是带头节点的所以初始化需要申请  初始化的之后 rear和front同时指向这个头节点 
	Q.front=Q.rear=(QNode*)malloc(sizeof(QNode));
	//使用了malloc就需要判断是否空间申请成功
	if(Q.front==NULL){
		exit(1);
	} 
	Q.front->base=-1;
	Q.front->next=NULL;
}

3、进队

尾指针 Q.rear 的后继原本是NULL 但是尾插法之后尾指针的后继应该是P 然后别忘了移动尾指针

//还有不需要判满  相当于链表的尾插法 
bool EnQueue(LQueue &Q,ElemType &x){
	QNode *P=(QNode*)malloc(sizeof(QNode));
	if(P==NULL){
		exit(1);
	} 
	P->base=x;
	P->next=NULL;
	Q.rear->next=P;
	Q.rear=P;//尾指针向后移动 
	PrintQueue(Q); 
	return true; 
}

出队

出队本来是相当于删除首结点 所以要判空Q.front是指向头结点的 所以这里的Q.front 是不变的 但是头结点的后继发生变化了 变成了后继的后继 但是因我们要删除,所以要保存要删除的结点 然后再来释放, 还有若是要只有一个首届点 此是删除就也会改变尾指针

//弹出操作相当于是链表的删除首结点的操作,所以需要释放  考虑一下若是删除的链队中若是只有一个数据元素呢  
//但是若是删除的化 尾指针也是需要移动的 , 他们删除之后同时指向头结点 所以要加一个判断语句 判断是否只有一个结点 
bool DeQueue(LQueue &Q,ElemType &x){
	 QNode* P=Q.front->next;//存放的是首结点的地址 
	 if(Q.front==Q.rear) return false;//还是需要判空操作的 
	 if(Q.front->next==Q.rear){//说明只有一个结点 
	 	Q.rear=Q.front;
		 free(P);
		 return true; 
	 } 
	 Q.front->next=Q.front->next->next;
	 free(P); 
	 PrintQueue(Q);
	 return true; 
}

取头

别忘了判空就行

bool GetHead(LQueue Q,ElemType &x){
	if(Q.front==Q.rear) return false;
	 else{
	 	x=Q.front->next->base;
	 }
	 PrintQueue(Q);
	 return true; 
}

代码汇总

// InitQueue(&Q):初始化队列 构建一个空队列Q
// QueueEmpty(Q):判断队列是否为空若是队列为空 返回true  否则返回false
// EnQueue(&Q,x) 若是队列未满 则加入使之称为新的那个队尾
// DeQueue(&Q,&x) 出队 若是队列非空 删除队头元素,并用x 返回
// GetHead(Q,&x):读队头元素 若是未空,则将队头元素赋值给x
#include<bits/stdc++.h>
#define ElemType int
#define OK 1
#define ERROR 0 
#define MAX 3 
using namespace std;
typedef struct QNode{
	ElemType base;
	struct QNode *next;
}QNode;
typedef struct LQueue{
	QNode *front;//是让他指向的是头结点 而不是首结点 
	QNode *rear;//指向的是尾结点 
}LQueue;
void PrintQueue(LQueue &Q){
	QNode* P=Q.front->next;//此时P就是首结点的位置  注意首结点与头结点的区别 
	cout<<"此时队列中的元素是(左边是队头)";
	for(QNode *i=P;i!=NULL;i=i->next){
		cout<<i->base<<" ";
	} 
	cout<<endl;
}
bool InitQueue(LQueue &Q){
	//申请一个结点作为头节点 因为我们是带头节点的所以初始化需要申请  初始化的之后 rear和front同时指向这个头节点 
	Q.front=Q.rear=(QNode*)malloc(sizeof(QNode));
	//使用了malloc就需要判断是否空间申请成功
	if(Q.front==NULL){
		exit(1);
	} 
	Q.front->base=-1;
	Q.front->next=NULL;
}
bool QueueEmpty(LQueue Q){
	if(Q.front==Q.rear) return true;
	else return false;
}
//还有不需要判满  相当于链表的尾插法 
bool EnQueue(LQueue &Q,ElemType &x){
	QNode *P=(QNode*)malloc(sizeof(QNode));
	if(P==NULL){
		exit(1);
	} 
	P->base=x;
	P->next=NULL;
	Q.rear->next=P;
	Q.rear=P;//尾指针向后移动 
	PrintQueue(Q); 
	return true; 
}
//弹出操作相当于是链表的删除首结点的操作  所以需要释放  考虑一下若是删除的链队中若是只有一个数据元素呢  
//但是若是删除的化 尾指针也是需要移动的 , 他们删除之后同时指向头结点 所以要加一个判断语句 判断是否只有一个结点 
bool DeQueue(LQueue &Q,ElemType &x){
	 QNode* P=Q.front->next;//存放的是首结点的地址 
	 if(Q.front==Q.rear) return false;//还是需要判空操作的 
	 if(Q.front->next==Q.rear){//说明只有一个结点 
	 	Q.rear=Q.front;
		 free(P);
		 return true; 
	 } 
	 Q.front->next=Q.front->next->next;
	 free(P); 
	 PrintQueue(Q);
	 return true; 
}
bool GetHead(LQueue Q,ElemType &x){
	if(Q.front==Q.rear) return false;
	 else{
	 	x=Q.front->next->base;
	 }
	 PrintQueue(Q);
	 return true; 
}
void menu(){
	cout<<"请选择你要进行的操作"<<endl;
	cout<<"1、判断队列是否为空 2、进队列 3、出队列 "<<endl;
	cout<<"4、获得队列顶的值  5、展示队列中的内容 6、退出"<<endl;
} 
int main(){
	LQueue Q;ElemType x;
	InitQueue(Q);
	int choice;
	while(1){
		menu();	
		cin>>choice;
		if(6==choice) break;
		switch(choice){
			case 1:{
				if(QueueEmpty(Q)){
					cout<<"队列为空"<<endl;
				}
				else{
					cout<<"队列不为空"<<endl;
				} 
				break;
			}
			case 2:{
				cout<<"请输入你要入队列的值"<<endl; 
				cin>>x; 
				EnQueue(Q,x);
				break;
			}
			case 3:{
				DeQueue(Q,x);
				break;
			}
			case 4:{
				GetHead(Q,x);
				cout<<"此时队列顶元素是"<<x;
				break;
			}
			case 5:{
				PrintQueue(Q);
				break;
			} 
			default:break;
		}
	} 
	return 0;	
}


提示 大家可以自己尝试一下 其实这个是不难的

总结

若是文章对你的提升由哪怕一点帮助的话 请答应我 不要吝啬你的点赞评论 你的鼓励对作者是一种莫大的鼓励

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值