#数据结构# 栈与队列的实现1 (C语言)

目录

队列(Queue)

1.队列概念:

2.代码实现:

① 队列的主体选择:

②基本结构:(结构体 QueueNode) 

③头尾标点初始化(QueueInit)

④元素入列(QueuePush)(尾插)

⑤元素出列(QueuePop)(头删)

⑥判断队列是否为空(QueueEmpty)

⑦队列长度(QueueSize)

⑧取队列首位(QueueFront)

⑨取队列末位(QueueBack)

⑩打印队列(Queue print)

销毁队列(QueueDestory)

完整代码:

Queue.h

Queue.c


队列(Queue)

1.队列概念:

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出的(First In First Out)的线性表,简称FIFO。
允许插入(Push)的一端为队尾,允许删除(Pop)的一端为队头。队列不允许在中间部位进行操作!假设队列是Q=(a1,a2,……,an),那么a1就是队头元素,而an是队尾元素。

所以我们进行插入数据时,新插入的数据总是位于队列尾端;进行删除时,删除的数据总是位于队列开头的数据。这跟我们排队打饭的道理有点类似:队伍最前的人先打到饭,并且离开队伍。
———————————————————————————————————————————

2.代码实现:

① 队列的主体选择:

队列可以选择用顺序表或链表进行实现,但是如果使用顺序表实现队列的情况下,对队列首位元素进行删除时,可能需要对整体向前一位迁移,这样的时间复杂度是O(N);或者不进行整体迁移,而采用扩容及改变下标的方法实现队列的头删功能,有点繁杂,所以这里我采用单链表实现队列。

②基本结构:(结构体 QueueNode) 

QueueNode:队列结点

typedef int QDataType;
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;

这也是链表的基本结构,到这里我们可以考虑到:队列两个主要功能:头删和尾插。

!如果用链表实现队列的话,队列就可以看成一个只能头删和尾插的链表!

在头插方面,链表的效率比比顺序表要好;

在尾插方面,顺序表的效率常规情况下会比链表要好,因为链表进行尾删时,要先进行一次遍历找到尾结点,时间复杂度来到了O(N),顺序表只需要通过记录的下标找到尾端下标,进行删除。

有没有一种办法使得链表的尾删变得有效率?

标记尾结点。(或改写双链表实现队列)

(!双链表的哨兵结点前一位就是链表尾结点,哨兵结点后一位就是链表头结点!)

typedef struct Queue
{
	QNode* head;
	QNode* tail;

}Queue;

我们再加一个结构体Queue,负责记录队列中的头结点和尾结点:

 ——————————————————————————————————————————

③头尾标点初始化(QueueInit)

void QueueInit(Queue* pq)//队列初始化
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

首先检查传入用于保存head和tail指针的Queue结构体地址是否为空,避免错误使用。

其次,初始化时,队列没有元素,所以Queue结构体中的head和tail都应指为NULL。

———————————————————————————————————————————

④元素入列(QueuePush)(尾插)

void QueuePush(Queue* pq, QDataType x);//入列

稳妥点,检查传入用于保存head和tail指针的Queue结构体地址是否为空,避免错误使用。

assert(pq);

然后就是为队列中结点开辟空间,再然后就是传入数据。

关于入列的结点,需要注意的点:

1. 第一次入列时,即队列只有一个元素,head还处于指向NULL的状态,需要将入列的结点地址 给予 head,且tail此时与head同位置。

2.不是第一次入列,即队列中已经有其他元素时,此时head已经具体,就指向队列的首位元素,不需要进行改动,此时需要改动的是tail,每有一个元素入列,tail位置就需要更新。

完整函数:

void QueuePush(Queue* pq, QDataType x)//入列
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	assert(newnode);

	newnode->data = x;
	newnode->next = NULL;

	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

———————————————————————————————————————————

⑤元素出列(QueuePop)(头删)

本质是链表尾删,细节是关注head与tail位置的更新,小细节是每次断尾结点后,要更新tail的位置,且将断出去的原来的尾结点释放内存。

实现出列的大概逻辑:

QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;

我们要考虑的情况是:

当我们一直使队列元素出列,tail的位置一直在更新,直到删到只剩一个元素,此时head与tail的位置是一样的:

 继续删除,只剩一个元素:

 此前的出列操作是不需要对tail位置进行更新的,但是目前的这个情况,如果我们继续出列,到队列置空时,head与tail指向同一个位置,这时再 free(head),则tail跟head指向同一个指针的内存被释放,如果不及时更新tail位置,使得tail置空,就会出现tail变成野指针的情况,因此进行判断,当head的下一个结点为NULL时,即队列只有一个元素时,释放head指向的结点,head与tail同时指向NULL。

完整函数:

void QueuePop(Queue* pq)//出列
{
	assert(pq);
	assert(pq->head && pq->tail);

	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

———————————————————————————————————————————

⑥判断队列是否为空(QueueEmpty)

如果队列为空,pq->head==NULL

bool QueueEmpty(Queue* pq)//判断是否为空
{
	assert(pq);

	return pq->head == NULL;
}

———————————————————————————————————————————

队列长度(QueueSize)

实现一个简单的count,对队列进行遍历。

size_t QueueSize(Queue* pq)//队列长度
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}

	return size;
}

———————————————————————————————————————————

⑧取队列首位(QueueFront)

QDataType QueueFront(Queue* pq)//取队列首位
{
	assert(pq);
	assert(pq->head);

	return pq->head->data;
}

———————————————————————————————————————————

⑨取队列末位(QueueBack)

QDataType QueueBack(Queue* pq)//取队列末位
{
	assert(pq);
	assert(pq->tail);

	return pq->tail->data;
}

———————————————————————————————————————————

⑩打印队列(Queue print)

void QueuePrint(Queue* pq)//打印队列
{
	assert(pq);
	if (pq->head != NULL)
	{
		QNode* cur = pq->head;
		while (cur)
		{
			printf("%d  ", cur->data);
			cur = cur->next;
		}
	}
}

———————————————————————————————————————————

销毁队列(QueueDestory)

就是销毁链表的道理:

void QueueDestory(Queue* pq)//销毁队列
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
}

完整代码:

Queue.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;

	//size_t size;
}Queue;

void QueueInit(Queue* pq);//队列初始化

void QueueDestory(Queue* pq);//销毁队列

void QueuePush(Queue* pq, QDataType x);//入列

void QueuePop(Queue* pq);//出列

bool QueueEmpty(Queue* pq);//判断是否为空

size_t QueueSize(Queue* pq);//队列长度

QDataType QueueFront(Queue* pq);//取队列首位

QDataType QueueBack(Queue* pq);//取队列末位

void QueuePrint(Queue* pq);//打印队列

Queue.c

#include "Queue.h"

void QueueInit(Queue* pq)//队列初始化
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

void QueueDestory(Queue* pq)//销毁队列
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QDataType x)//入列
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	assert(newnode);

	newnode->data = x;
	newnode->next = NULL;

	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

void QueuePop(Queue* pq)//出列
{
	assert(pq);
	assert(pq->head && pq->tail);

	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

bool QueueEmpty(Queue* pq)//判断是否为空
{
	assert(pq);

	return pq->head == NULL;
}

size_t QueueSize(Queue* pq)//队列长度
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}

	return size;
}

QDataType QueueFront(Queue* pq)//取队列首位
{
	assert(pq);
	assert(pq->head);

	return pq->head->data;
}

QDataType QueueBack(Queue* pq)//取队列末位
{
	assert(pq);
	assert(pq->tail);

	return pq->tail->data;
}

void QueuePrint(Queue* pq)//打印队列
{
	assert(pq);
	if (pq->head != NULL)
	{
		QNode* cur = pq->head;
		while (cur)
		{
			printf("%d  ", cur->data);
			cur = cur->next;
		}
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值