1. 栈和循环队列
栈:不管用什么方式去实现,都可以保证入栈和出栈的时间复杂度为O(1)
循环队列:顺序表实现的队列,入队时间复杂度为O(1),出队时间复杂度为O(n)
队列中的三个难点以及解决方案:
① 如何保证顺序表实现的队列的入队和出队时间复杂度为O(1)
答:出队的时候,不再挪动数据向前覆盖,而是数据不动,队头指针向后移动(臆想成一个环)
② 臆想成环之后,判空和判满条件冲突了?
答:1)设计成一个计数器;
2)队尾浪费一个空间,不再存储数据,而是作为一个标记
③ 循环队列的求长度公式:length = (rear - front + MAX_SIZE) %MAX_SIZE
2. 链式队列
链式队列的需求:第一时间可以找到头(首元素);第一时间可以找到尾(尾元素);得有一个链表去存储数据;
解决方法:给头节点加一个尾指针,就可以满足其需求;因此,头结点结构体里成员比有效数据结构体成员多,不在让头结点借助有效数据节点的结构体设计,而是让头结点重新设计自身的结构体设计
如下图:
*入队:在队尾入,相当于尾插;出队:在队头出,头删
3.链式队列的结构体设计
(1) 头结点既然重新设计,完全可以将之前的数据域扔掉,因为链队列的头结点只需要两个指针域即可(队头指针和队尾指针)
typedef int ELEM_TYPE;
//有效数据节点的结构体设计----指针域和数据域
typedef struct Node
{
ELEM_TYPE data;//数据域
struct Node* next;//指针域
}Node,*PNode;
//头节点的结构体设计----两个指针域
typedef struct LQueue
{
struct Node* front;//保存有效数据节点的地址
struct Node* rear;
}LQueue, *PLQueue;
4 可执行函数
注意的两个函数:
① 出队:在队头出,相当于头删,有两种情况
正常情况下出队,收到影响的只有一个指针域——front ;特殊情况下出队,受到影响的指针域有两个——front 和 rear,如图:
② 入队:在队尾入,相当于尾插,有两种情况
正常特殊情况下,里面已经有元素了(不是一个空队列);特殊情况下,这个队列是一个空队列,如果是一个空队列,入队的元素既是队头也是队尾。
5. 代码实现
ist_queue.h文件
#pragma once
//链式队列的结构体设计
typedef int ELEM_TYPE;
//有效数据节点的结构体设计----指针域和数据域
typedef struct Node
{
ELEM_TYPE data;//数据域
struct Node* next;//指针域
}Node,*PNode;
//头节点的结构体设计----两个指针域
typedef struct LQueue
{
struct Node* front;//保存有效数据节点的地址
struct Node* rear;
}LQueue, *PLQueue;
//链式队列可执行函数---增删改查
//初始化
void Init_LQueue(PLQueue lq);
//入队
bool Push(PLQueue lq, ELEM_TYPE val);
//出队(这里需要知道出队的值,通过输出参数rtval带出来)
bool Pop(PLQueue lq, ELEM_TYPE * rtval);//系统提供的是小写
//获取头元素值(这里需要知队头的值,通过输出参数rtval带出来)
bool Top(PLQueue lq, ELEM_TYPE* rtval);
//搜索
struct Node* Search(PLQueue lq, ELEM_TYPE val);
//判空
bool IsEmpty(PLQueue lq);
//判满---链式结构不需要判满
//bool IsFull(PLQueue lq);
//获取有效长度
int Get_length(PLQueue lq);
//清空
void Clear(PLQueue lq);
//销毁
void Destroy(PLQueue lq);
//打印
void Show(PLQueue lq);
ist_queue.cpp文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"list_queue.h"
//初始化
void Init_LQueue(PLQueue lq)//PLQueue头结点
{
assert(lq != NULL);
if (lq == NULL)
{
return;
}
lq->front = NULL;
lq->rear = NULL;
}
//入队
bool Push(PLQueue lq, ELEM_TYPE val)
{
//assert;
//不需要判满
struct Node* pnewnode = (struct Node*)malloc(1 * sizeof(struct Node));
assert(pnewnode != NULL);//申请成功
pnewnode->data = val;
if (IsEmpty(lq))//特殊情况下:空队列
{
pnewnode->next = lq->front;
lq->front = lq->rear = pnewnode;//如果是一个空队列,入队的元素既是队头也是队尾
}
else//正常情况下
{
pnewnode->next = lq->rear->next;
lq->rear->next = pnewnode;
//更新一下队尾指针,重新让它指向新插入的尾结点
lq->rear = pnewnode;
}
return true;
}
//出队(这里需要知道出队的值,通过输出参数rtval带出来)
bool Pop(PLQueue lq, ELEM_TYPE* rtval)
{
//assert;
if (IsEmpty(lq))
{
return false;
}//至少存在一个节点
*rtval = lq->front->data;//第一个元素的数据域带出来
//if (lq->front->next == NULL)//判断剩余元素是否仅剩一个
if (Get_length(lq) == 1)
{
free(lq->front);
lq->front = NULL;
lq->rear = NULL;
}
else//正常情况下,剩余元素不止一个
{
struct Node* p = lq->front;
lq->front = p->next;
free(p);
}
return true;
}
//获取头元素值(这里需要知队头的值,通过输出参数rtval带出来)
bool Top(PLQueue lq, ELEM_TYPE* rtval)
{
//assert
if (IsEmpty(lq))
{
return false;
}
*rtval = lq->front->data;
return true;
}
//搜索---队头到队尾遍历一遍,需要不需要前驱的for循环
struct Node* Search(PLQueue lq, ELEM_TYPE val)//队头到队尾遍历一遍
{
struct Node* p = lq->front;
for (p; p != NULL; p = p->next)
{
if (p->data == val)
{
return p;
}
}
return NULL;
}
//判空
bool IsEmpty(PLQueue lq)
{
return lq->front == NULL;
}
//判满---链式结构不需要判满
//bool IsFull(PLQueue lq);
//获取有效长度
int Get_length(PLQueue lq)
{
int count = 0;
struct Node* p = lq->front;
for (p; p != NULL; p = p->next)
{
count++;
}
return count;
}
//清空
void Clear(PLQueue lq)
{
Destroy(lq);//对于链式结构来说,清空就是销毁
}
//销毁
void Destroy(PLQueue lq)
{
struct Node* p = lq->front;
struct Node* q = lq->front;
while (p != NULL)
{
q = p->next;
free(p);
p = q;
}
lq->front = lq->rear = NULL;
}
//打印
void Show(PLQueue lq)
{
struct Node* p = lq->front;
for (p; p != NULL; p = p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
list_queue.cpp主函数
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"list_queue.h"
//链式队列
int main()
{
struct LQueue head;
Init_LQueue(&head);
for (int i = 0; i < 20; i++)
{
Push(&head, i + 1);
}
Show(&head);
printf("length=%d\n", Get_length(&head));
ELEM_TYPE tmp;
bool tag = Pop(&head, &tmp);//队列,先进先出,这块出1
if (tag)
{
printf("pop = %d\n", tmp);
}
Show(&head);
printf("length=%d\n", Get_length(&head));
ELEM_TYPE flag;
bool tag2 = Top(&head, &flag);//队列,先进先出,这块出1
if (tag2)
{
printf("top = %d\n", flag);
}
Show(&head);
printf("length=%d\n", Get_length(&head));
Destroy(&head);
Show(&head);
printf("length=%d\n", Get_length(&head));
return 0;
}
运行结果:
注意:销毁之后打印出来是空行,图中空白地方