今天敲得是队列,有两个不同的实现方式:链式队列和循环队列。
队列只允许在一端插入数据操作,称为队尾,在另一端进行删除数据操作,称为队首。队列具有先进先出的特性。
链式队列
结构体定义
#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)。
总结
在理解了这两个算法之后我自己重新敲了这两个代码。我个人认为其实这两段代码都不算很复杂,最主要的是理解整个逻辑,以及注意自己对细节处的处理。
数据结构学习到现在我最大的心得就是要学会像计算机一样去思考。人的逻辑和计算机的逻辑是存在很大的差别的,应该尝试把人的逻辑转化成计算机的逻辑,用代码呈现出来。