队列:栈队和顺序的实现(C语言)

目录

一    了解队列

二    队列的操作实现

1, 基于链表实现的队列

2, 基于数组实现的队列

三    测试效果


一    了解队列

  • 概念: 队列(queue)是一种遵循先入先出(FIFO)规则的线性数据结构。结构和排队类似,队尾不断有新的人加入(入队),而队头不断有人离开(出队)。由于队列的特点:只允许在队头进行删除操作,而在队尾进行插入操作。和栈一样,队列也可以看做是一种操作受限制的数组或链表。
  • 应用:打印机的打印任务;对网站的访问请求等


二    队列的操作实现

(1)定义队列结构;                       (2)队列的初始化/构造;

(3)获取栈的长度;                       (4)判断栈是否为空;

(5)获取队头/尾元素;                  (6)入队;                

(7)出队;                                     (8)销毁队列

1, 基于链表实现的队列

若不是很了解链式存储结构,可参考这篇文章:链表入门:“单链表“的基本操作详解(C语言)icon-default.png?t=N7T8https://blog.csdn.net/Mzyh_c/article/details/133841591?spm=1001.2014.3001.5502

/*定义链队*/
typedef struct LinkQueue
{
	int data;
	struct LinkQueue* next;    
	struct LinkQueue* top;    //记录队头结点
	struct LinkQueue* end;    //记录队尾结点
}LinkQueue;

/*    初始化链队    */
LinkQueue* InitLinkQueue()
{
	LinkQueue* L = (LinkQueue*)malloc(sizeof(LinkQueue));
    if(!L)
        return NULL;    //开辟失败

	L->top = NULL;
	L->end = NULL;
	return L;        //返回开辟成功的链队
}

/*    获取队列长度    */
int GetLength(LinkQueue* L)
{
	int count = 1;
	//为了不改变队列中的指针,创建临时指针进行遍历
	LinkQueue* temp = L->top;
	while (1)
	{
		temp = temp->next;
		count++;
		//当到循环到队尾的时候停止并返回count
		if (temp == L->end)
			return count;
	}
}

/*    判断链队是否为空    */
int IsEmpty(LinkQueue* L)
{
	if (L->top == NULL && L->end == NULL)
		return 1;//链队为空
	else
		return 0;//链队不为空
}

/*    入队    */
void Push(LinkQueue* L, int e)
{	
	//在队尾插入
    //创建一个新结点
	LinkQueue* P = (LinkQueue*)malloc(sizeof(LinkQueue));
	if (!P)
		return;    //p创建失败

	P->data = e;
	if (IsEmpty(L))
	{
		//当队列为空时,头指针和尾指针都指向新结点P
		L->end = P;
		L->top = L->end;
		return;    //结束方法
	}
	//当队列不为空时,新结点插入在队尾,尾指针指向新结点
	L->end->next = P;
	L->end = P;
}

/*    出队    */
int Pop(LinkQueue* L)
{
	if (IsEmpty(L))
	{
		return 0;
	}
	//在队头删除
	LinkQueue* temp = (LinkQueue*)malloc(sizeof(LinkQueue));
	if (!temp)
		return;    //temp创建失败

	//存储将删除结点的元素
	temp = L->top;
	int e = temp->data;
	L->top = L->top->next;	//top指向下一个结点
	free(temp);		//释放空间
	temp = NULL;	//防止产生野指针
	//返回已删除结点的元素
	return e;
}

/*    获取队头元素    */
int GetTop(LinkQueue* L)
{
	if (IsEmpty(L))
	{
		return 0;
	}
	return L->top->data;
}

/*    获取队尾元素    */
int GetEnd(LinkQueue* L)
{
	if (IsEmpty(L))
	{
		return 0;
	}
	return L->end->data;
}

/*    销毁链队    */
void Destroy(LinkQueue* L)
{
	if (IsEmpty(L))
		return;

	while (L->top != L->end)
	{
		LinkQueue*  temp = L->top;
		L->top = L->top->next;
		free(temp);		//释放空间
		temp = NULL;	//防止产生野指针
	}
	//在 L->top == L->end 时并没有执行循环体内容
	free(L->end);
	L->end = NULL;
}

2, 基于数组实现的队列

在数组中删除首元素每次都得向后访问n次找到首元素后再进行删除,时间复杂度为O(n),这样的做法效率很低。所以我们将定义一个头指针 top 在每次删除元素后都进行头指针的自增(top++)指向下一个首元素,入队也会对尾指针 end 进行自增,这样时间复杂度就为O(n)了。而数组的长度是有限的,这样会导致当它们到达数组尾部时就无法继续移动了,所以我们可以使用循环(环形)数组解决这个问题。(见下面代码)

循环队列的示意图

此实现方式的局限性是其长度不可变,读者可以自己尝试将数组改用动态数组,引入扩容机制进行解决。

/*    定义顺序队    */
typedef struct SqQueue
{
	int top;		//队头
	int end;		//队尾
	ElemType* data;	//数据
}SqQueue;

/*    初始化顺序队列    */
SqQueue* InitQuese()
{
	SqQueue* q = (SqQueue*)malloc(sizeof(SqQueue));
	q->data = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);
	if (q->data == NULL)
		return 0;//开辟空间失败

	q->end = q->top = 0;
	return q;	//开辟空间成功返回队列q
}

/*    判断队列是否为空    */
int IsEmpty(SqQueue* q)
{
	if (q->end == q->top)
		return 1;
	else
		return 0;
}

/*    获取队列长度    */
int GetLength(SqQueue* q)
{
	return (q->end - q->top);
}

/*    入队    */
void Push(SqQueue* q, ElemType e)
{
	if ((q->end % MAXSIZE == q->top) && (q->end != 0))
	{
		printf("Push.err: 队列已满!\n");
		return;//队列已满
	}
	//q->data[(q->end)++] = e;
	q->data[(q->end) % MAXSIZE] = e;
	(q->end)++;
	//把队列变成循环的队列
}

/*    出队    */
ElemType Pop(SqQueue* q)
{
	//队列为空则返回
	if (IsEmpty(q))
		return -1;
	//存储要删除的数据并返回
	ElemType e = q->data[q->top];
	(q->top)++;
	return e;
}

/*    获得队尾的元素    */
ElemType GetEnd(SqQueue* q)
{
	return q->data[(q->end - 1) % MAXSIZE];
}


/*    删除队列    */
void DelQuese(SqQueue* q)
{
	if (IsEmpty(q))
		return;
	q->end = q->top = 0;
}

/*    销毁队列    */
void DestoryQuese(SqQueue* q)
{
	free(q->data);//释放空间
	q->data = NULL;//防止产生野指针
	q->end = q->top = 0;
}

 对于循环队列,我们需要让 top 或 end 在越过数组尾部时,直接回到数组头部继续遍历。这种周期性规律可以通过“取余操作”来实现。(即每次指针移动时 % 队列的最大长度,可以防止指针访问越界。)


三    测试效果

链队的测试截图

顺序循环的测试截图

在学习了队列和栈后, 你可以尝试完成以下的练习, 可能会有所收获!

停车场管理系统 - 栈和队列的应用(C语言)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Mzyh_c/article/details/135175641

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值