数据结构-队列的实现

不同于栈的先进后出、后进先出,队列的操作为先进先出、后进后出。而对于队列的实现,主要的问题集中在插入和删除两部分,而插入和删除又分别区分为两种情况。

插入:

情况一:队列中一个节点都没有,只有我创建的两个空指针。这时就可以将这两个指针指向我所要加入的第一个节点(该节点需要用malloc函数向内存中申请一块空间,在申请的同时将该空间的地址赋给我创建的两个指针)。这时就完成了第一个节点的入队。

情况二:队列中已经有了一些节点,这时要继续向队列中插入节点。(需要注意的是,对于单链表结构的队列而言,往往采用尾插头删的方法,因为对于单链表而言,尾插和头删比较方便)此时我之前创建的两个指针head和tail分别指向队列的头节点和尾节点,要尾插,则

tail->next=newnode,tail=newnode(这类结构已经接触过无数次了)

删除:

情况一:队列中还有一些节点的情况下删除一个节点(即将节点出队)。这里的实现要用到一个前面接触很多次的指针拷贝的思想,由于队列中一般是头删,所以势必要改变指针head,头节点被删除后,需要head=head->next,但这时就会出现问题,如果我在head=head->next前free(head),那么可以顺利删除头节点的内容,但是head指针也就失去了和链表的联系,如果我先head=head->next,再free(head),则释放的会是当前第二个节点的空间。所以这里一定缺少了一步,这时就需要我对指针进行拷贝,head->next=next,然后free(head),这时再让head指针指向原先的第二个节点next,head=next(删除了第一个之后,它就变成了第一个)。

情况二:队列中只剩下一个节点,这时仍要删除。此时指针head和tail都指向这个唯一的节点,在利用情况一中的逻辑删除这一点后,head=next,(next即为head->next,链表的尾节点->next为空),此时,head节点就被恰好置空。但是,没有人管tail指针,如果不管它,tail指针就会变成野指针,因此要在情况二出现时,专门将tail指针置为空。

 栈此外,还需要额外注意的是,在程序结束前,一定要销毁所申请的空间,因为由malloc申请的空间在整个程序结束时会被返还操作系统,但在某个函数运行结束时不会,如果整个程序持续运行,多次调用该函数,那么函数每被调用一次,就会在内存中占用一定的空间,长此以往就可能会占用大量的空间,甚至导致计算机没法正常运转。

代码实现:

头文件:头文件中定义的QueueNode结构体和Queue结构体,第一个定义的是实现队列功能的链表中的一个节点,一个是定义维护队列功能的两个指针,一个是头指针,一个是尾指针。

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

typedef int QDataType;//宏定义

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QueueNode;//对于队列而言,

typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
}Queue;

//void QueueInit(QueueNode** pphead, QueueNode** pptail);
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);

源文件:

#include "Queue.h"

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

void QueueDestroy(Queue* pq)//删除队列
{
	assert(pq);
	QueueNode* cur = pq->head;
	while (cur != NULL)
	{
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;//将队列中每一个节点都删除时,head和tail指针也就变成了野指针,因此要将二者置为空
}

void QueuePush(Queue* pq, QDataType x)//队列中加入新元素
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	newnode->data = x;
	newnode->next = NULL;//新节点以尾插的形式加入到队列中

	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}//初始化
	else
	{
		pq->tail->next = newnode;//newnode是新节点的地址,这里代表尾插
		pq->tail = newnode;//尾指针向后移动一位
	}
}

void QueuePop(Queue* pq)//队列中排出一个元素
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//队列增删元素是:头删尾插
	QueueNode* next = pq->head->next;//将整个链表的第二个节点的地址赋给指针next
	free(pq->head);//释放头指针指向的空间
	pq->head = next;//然后立刻尾头指针赋上新的地址(头指针就不会因为其过去指向的内存空间被释放而变成野指针),新的地址是原先的第二个节点,亦即下一个
	//即将被删除的节点
	if (pq->head == NULL)//当整个队列被删完时,即pq->head->next==NULL,上一个被删完的节点后面已经为空了,那么在完成此次删除之前,tail和head指针指向
		//同一个节点,对于pq->head而言,顺势就可以将其赋为空(phead每次都等于其目前指向节点的下一个节点的地址,其后面没有节点时,自然被赋为空),
		//而没人理tail指针,这时tail指针就会变成指向内存已经被释放空间的野指针,为此,在队列中所有节点都被删完时,要额外将tail指针赋为空
	{
		pq->tail = NULL;
	}
}

QDataType QueueFront(Queue* pq)//返回值为顺序表中的数值类型,即返回当前队列的第一个节点的值
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

QDataType QueueBack(Queue* pq)//同上,返回当前队列的尾节点的值
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);

	int n = 0;
	QueueNode* cur = pq->head;
	while (cur)//链表结构中while循环比for循环更加常用,当头节点不为空时,头节点就向后移动一位,并计数,循环结束时返回的就是队列的长度
	{
		++n;
		cur = cur->next;
	}

	return n;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

teat.c

#include "Queue.h"

void TestQueue1()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	QueuePop(&q);
	QueuePop(&q);
	QueuePop(&q);
	QueuePop(&q);
	//QueuePop(&q);

	//printf("%d\n", QueueFront(&q));
	//printf("%d\n", QueueBack(&q));

	QueuePush(&q, 10);
	QueuePush(&q, 20);

	QueueDestroy(&q);
}

void TestQueue2()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QDataType front = QueueFront(&q);
	printf("%d ", front);
	QueuePop(&q);

	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		QDataType front = QueueFront(&q);
		printf("%d ", front);
		QueuePop(&q);
	}
	printf("\n");
}

int main()
{
	TestQueue2();
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值