数据结构--2.4 队列


今天敲得是队列,有两个不同的实现方式:链式队列和循环队列。
队列只允许在一端插入数据操作,称为队尾,在另一端进行删除数据操作,称为队首。队列具有先进先出的特性。

链式队列

结构体定义

#include <stdio.h>
#include <malloc.h>

typedef struct Node{
	int data;
	struct Node *next;
}NODE,*PNODE;

typedef struct Queue{
	PNODE front;
	PNODE rear;
}*QUEUE;

创建

QUEUE initLinkQueue()
{
	QUEUE resultPtr = (QUEUE)malloc(sizeof(Queue));
	PNODE headPtr = (PNODE)malloc(sizeof(Node));
	
	headPtr->next = NULL;
	
	resultPtr->front = headPtr;
	resultPtr->rear = headPtr;
	
	return resultPtr;
}

这里出了创建函数之后headPtr就不存在了,因为是临时变量。但是resultPtr依然存在,因为我们把它返回了。

入队

void enQueue(QUEUE paraQueue,int paraVal)
{
	PNODE p;
	p=(PNODE)malloc(sizeof(Node));
	
	p->data = paraVal;
	p->next = NULL;
	
	paraQueue->rear->next = p;
	paraQueue->rear = p;
}

出队

int deQueue(QUEUE paraQueue)
{
	int val;
	PNODE p;
	
	if(paraQueue->front == paraQueue->rear )
	{
		printf("the queue if empty.\r\n");
		return -1;
	}
	
	p = paraQueue->front->next;
	val = p->data;
	paraQueue->front->next = paraQueue->front->next->next;
	
	if(paraQueue->rear == p)
	{
		paraQueue->front = paraQueue->rear;
	}
	free(p);
	
	return val;
}

运行结果

1 2 3
deQueue is 1
2 3

链式队列的时间复杂度为O(1)。
链式队列的逻辑比较简单,所以没有过多的阐述,相比之下循环队列会稍显复杂。

循环队列

循环队列利用数组来实现。我觉得写出这个代码主要解决两个问题,一个是如何去判断满,而二是如何让整个队列循环起来。(好废话)

结构体定义

#include <stdio.h>
#include <malloc.h>

#define MAX_SIZE 5

typedef struct Circle{
	int data[MAX_SIZE];
	int head;
	int tail;
}*CIRCLE;

创建

CIRCLE initCircleQueue()
{
	CIRCLE resultPtr = (CIRCLE)malloc(sizeof(Circle));
	
	resultPtr->head = 0;
	resultPtr->tail = 0;
	
	return resultPtr;
}

判断空和满

先看代码

bool is_full(CIRCLE paraQueue)
{
	if((paraQueue->tail+1)%MAX_SIZE == paraQueue->head )
	{
		return true;
	}else
	{
		return false;
	}
}

bool is_empty(CIRCLE paraQueue)
{
	if(paraQueue->tail == paraQueue->head )
	{
		return true;
	}else
	{
		return false;
	}
}

判断空其实很简单,当我们的队首和队尾标记都相同时这个队列就空了。比较复杂的是判断满,我们先看图:
在这里插入图片描述
一开始感觉当我的队尾追上队首的时候就满了,但是队尾等于队尾的时候也有可能是空,所以这种判断方法就失效了。那我们可不可以少放一个元素,让rear+1等于队首的时候为满呢,就像这样:
在这里插入图片描述
那答案是可以的。所以判断满的方式就变成了:(paraQueue->tail+1)%MAX_SIZE == paraQueue->head。

入队

那我们又要想了,在入队的时候,我们能够一直让tail++吗?我们的数组是有大小的,当tail超过这个大小的时候我们应该如何让数组循环起来呢。
我们选择对tail取余。我们直接让tail%MAX_SIZE,这样得到的数值就会始终在MAX_SIZE的范围之内了

bool enCircleQueue(CIRCLE paraQueue,int data)
{
	if(is_full(paraQueue))
	{
		printf("the queue is full.\r\n");
		return false;
	}
	
	paraQueue->data[paraQueue->tail%MAX_SIZE] = data;
	paraQueue->tail ++ ;
	
	return true ;
}

出队

int deCiecleQueue(CIRCLE paraQueue)
{
	int val;
	
	if(is_empty(paraQueue))
	{
		printf("the queue is empty.\r\n");
		return -1;
	}
	
	val = paraQueue->data[paraQueue->head%MAX_SIZE];
	paraQueue->head ++ ;
	
	return val;
}

运行结果

the queue is full.
1 2 3 4
delete is 1
delete is 2
3 4
delete is 3
delete is 4
the queue is empty.
delete is -1

循环队列的时间复杂度也是O(1)。

总结

在理解了这两个算法之后我自己重新敲了这两个代码。我个人认为其实这两段代码都不算很复杂,最主要的是理解整个逻辑,以及注意自己对细节处的处理。
数据结构学习到现在我最大的心得就是要学会像计算机一样去思考。人的逻辑和计算机的逻辑是存在很大的差别的,应该尝试把人的逻辑转化成计算机的逻辑,用代码呈现出来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值