1.队列存在的实现方式及其存在意义
1.1为什么队列使用单链表实现更好
- 动态内存分配:链表在C语言中通常使用动态内存分配,这意味着可以在运行时根据需要动态地添加或删除节点。这对于实现一个动态大小的队列非常有用,因为队列的大小可以在运行时变化。相比之下,数组的大小通常是固定的,需要在编译时确定,这可能会限制队列的灵活性。
- 插入和删除效率:在链表中,插入和删除操作的时间复杂度为O(1)(在已知位置的情况下)。这意味着在链表的头部或尾部添加或删除节点非常高效。由于队列是一种先入先出(FIFO)的数据结构,我们通常在队列的尾部添加元素(入队),并在头部删除元素(出队)。因此,使用链表实现队列可以充分利用其高效的插入和删除操作。
- 空间效率:链表只存储节点本身的数据和指向下一个节点的指针,不需要像数组那样预留一定的空间。这使得链表在存储大量数据时更为空间高效.
1.2 队列存在的意义
无论是栈,队列,抑或是树,它们基本都是由顺序表,链表这些基本的元素构成的,并且人们将栈,队列等一些数据结构发明出来也是为了根据人类的需求解决人类的一些问题,举一个例子,医院叫号排队,为了防止有些人乱插队从而引起的不必要的纠纷,于是以数据结构队列为基本原理开发出有关排队就医的系统
2.有关队列的一些基本操作如何使用c语言代码将其具现化
2.1如何写一个队列的结点
typedef int QDataType;
typedef struct QueueNode
{
int val;
struct QueueNode* next;
}QNode;typedef struct Queue
{
QNode* phead; // 指向队列头部的指针
QNode* ptail; // 指向队列尾部的指针
int size;
} Queue;
如果只是写一个队列的结点
然后在之后的对队列的操作每次都去再设置一个头指针和尾指针如果我们需要去找队列的尾结点,那么就需要尾指针每次都从头开始去遍历整个链表的结点,但是这样做的话时间复杂度便可以来到O(n),是不合情的
所以我们在最初设置队列的结点相关基础信息的时候就连带着将它的头指针和尾指针一并设置好.
设置两个结点指针phead和ptail,例如我们每次进行一次尾插操作,就让ptail指向新的尾结点,如此一来,ptail便一直指向尾结点,每当我们需要取去寻找尾结点是,可以直接使用ptail,就不需要再去从头开始遍历了.
2.2队列的初始化操作
void QueueInit(Queue* pq)
{
assert(pq);//pq是指向结构体的指针
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
2.3队列的销毁操作
//销毁操作
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;//从头开始销毁,和出队一样
while (cur != NULL)
{
QNode* nexttemp = cur->next;
free(cur);
cur = nexttemp;
}pq->phead = pq->ptail = NULL;
pq->size = 0;
}
QNode* nexttemp = cur->next;
这一步的意思是为了保证在将cur目前所指向的结点删除以后还可以定位到原被删除结点的下一个结点,所以事先将下一个结点cur->next用创建的临时变量nexttemp保存起来,待目前结点被删除以后,cur又快速指向下一个结点cur->next结点.
2.4入队操作
//入队
void QueuePush(Queue* pq,QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->val = x;
newnode->next = NULL;
if (pq->ptail = NULL)//啥都没有
{
pq->ptail = pq->phead = newnode;}
else//本来就有结点
{
pq->phead->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
2.5出队操作
//出队
void QueuePop(Queue* pq)
{
assert(pq);
//暴力检查
//至少要有一个结点才可以进行销毁操作
assert(pq->phead!=NULL);
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
QNode* nexttemp = pq->phead->next;
free(pq->phead);
pq->phead = nexttemp;
}
pq->size--;
}
2.6取头操作
//取头
QDataType QueueFront(Queue* pq)
{
assert(pq);
//要有首结点才可以进行取头操作
assert(pq->phead != NULL);
return pq->phead->val;
}
2.7取尾操作
//取尾
QDataType QueueBack(Queue* pq)
{
assert(pq);
//要有尾结点才可以进行取尾操作
assert(pq->ptail != NULL);
return pq->ptail->val;
}