队列:先进先出(用循环链表就可以实现)
1.为什么要把队列臆想成一个环?
难点1:如何让出队列和入队列时间复杂度都降低为O(1)
不挪动数据,使用队头指针和队尾指针来回走动,得把它臆想成一个环。
2.判空和判满有何区分
问题:front = rear
为了区分判空和判满
判空和判满操作的判断依据出现了冲突,都是rear == front; 所以我们需要浪费一个尾部节点,当做标记位,当尾指针再向后跑一步就遇到了头指针,此时我们就认为队列满了
解决 1.自己加一个标志length
2.浪费一个空间当标记
判空:front == rear
判满:(rear+1)%Maxsize ==front
3.怎么求有效长度
获取其有效元素个数
MAXSIZE
是为了防止出现负数MAXSIZE
,防止没有出现负数的 情况下多加MAXSIZEint Get_length(PQueue pq)
{
int length = (pq->rear - pq->front + MAXSIZE) % MAXSIZE;
} 背下来
void Show(PQueue pq)
{
for (int i = pq->front; i != pq->rear; i=(i+1)%MAXSIZE)
{
printf("%d ", pq->base[i]);
}
printf("\n");
}
#include
#include
#include
#include
#define MAXSIZE 10
//循环队列
//一端入队,一端出队
//当队列中没有一个元素时,是空队列
//如果满了,就认为出错了,没有扩容
typedef int ELEM_TYPE;
typedef struct Queue
{
ELEM_TYPE* base;//数据域,用来malloc申请空间
int front;//当队列不为空的时候保存的是队头,指向第一个元素的下标
int rear;//尾指针,当队列不为空的时候,保存的是队尾,指向下一个元素入队的下标。
}Queue,*PQueue;
#include "queue.h"
void Init_Queue(PQueue pq)
{
//assert
pq->base = (ELEM_TYPE*)malloc(sizeof(ELEM_TYPE) * MAXSIZE);
assert(pq->base != nullptr);
pq->front = 0;
pq->rear = 0;
}
//入队 push
bool Push(PQueue pq, ELEM_TYPE val)
{
//assert
if (IsFull(pq))
{
return false;
}
//把它臆想成一个环,所以rear向后跑会有所不同
pq->base[pq->rear] = val;
pq->rear = (pq->rear + 1) % MAXSIZE;//需要取模
//而不是pq->rear++;
return true;
}
//出队 pop 需要删除操作
bool Pop(PQueue pq, ELEM_TYPE* rtval)
{
//assert
if (IsEmpty(pq))
{
return false;
}
*rtval = pq->base[pq->front];
//把它臆想成一个环,所以rear向后跑会有所不同
pq->front = (pq->front+ 1) % MAXSIZE;//需要取模
return true;
}
//top 获取队头元素值, 不需要删除操作
bool Top(PQueue pq, ELEM_TYPE* rtval)
{
if (IsEmpty(pq))
{
return false;
}
*rtval = pq->base[pq->front];
return true;
}
//获取其有效元素个数
int Get_length(PQueue pq)
{
int length = (pq->rear - pq->front + MAXSIZE) % MAXSIZE;
return length;
}
//判空
bool IsEmpty(PQueue pq)
{
return pq->rear == pq->front;
}
//判满
bool IsFull(PQueue pq)
{
return (pq->rear + 1) % MAXSIZE == pq->front;
}
//清空
void Clear(PQueue pq)
{
//assert
pq->front = pq->rear = 0;
}
//销毁
void Destroy(PQueue pq)
{
free(pq->base);
pq->base = nullptr;
pq->front = pq->rear = 0;
}
void Show(PQueue pq)
{
for (int i = pq->front; i != pq->rear; i=(i+1)%MAXSIZE)
{
printf("%d ", pq->base[i]);
}
printf("\n");
}
int main()
{
Queue qu;
Init_Queue(&qu);
for (int i = 0; i < 10; i++)
{
Push(&qu,i);
}
Show(&qu);
int tmp1;
Pop(&qu, &tmp1);
Show(&qu);
int tmp2;
Top(&qu, &tmp2);
printf("%d\n", tmp2);
int len = Get_length(&qu);
printf("%d\n", len);
Destroy(&qu);
len = Get_length(&qu);
printf("%d\n", len);
}
单链表的特点:逻辑上相邻,物理上不一定相邻,所以随机访问时间复杂度为O(n),
我们插入和删除的时候,不需要挪动数据。
头插和头删的时间复杂度均为O(1)
公司一般将plist指向循环链表的尾部,这样在处理头插和尾插就可以让时间复杂度变得最小。
typedef struct ListNode
{
struct ListNode *next;
struct ListNode * rear;
int data;
}
因此需要再定义义个结构体,生成一新的头节点。
typedef struct ListNode
{
struct ListNode *next;
int data;
}
typedef struct LinkList
{
struct ListNode *front;
struct ListNode *rear;
}
如下图
typedef int ELEMTYPE;
typedef struct ListNode
{
struct ListNode* next;
ELEMTYPE data;
}LNode,*PLNode;
typedef struct Head
{
struct ListNode* front; //一直指向第一个结点
struct ListNode* rear; //一直指向最后一个结点
//int length; 如果频繁获取队列的有效长度们可以加上length
}Head,*PHead;
#include "Lqueue.h"
void Init_Iqueue(PHead pq)
{
assert(pq != nullptr);
if (pq == nullptr) return;
pq->front = nullptr;
pq->rear = nullptr;
}
ListNode* BuyNode()
{
ListNode* s = (ListNode*)malloc(sizeof(ListNode));
assert(s != nullptr);
memset(s, 0, sizeof(ListNode));
return s;
}
入队 :当链表中没有数据
//入队 在尾部
bool push(PHead pq,ELEMTYPE val)
{
assert(pq != nullptr);
//申请新结点
ListNode* s = BuyNode();
assert(s != nullptr);
s->data = val;
//插入的时候有没有数据存在
//如果,没有
if (pq->front == nullptr) //需要对头节点进行判断
{
s->next = nullptr;
pq->front = s;
pq->rear = s;
}
else
{
s->next = pq->rear->next;
pq->rear->next = s;
pq->rear = s;//更新尾指针
}
return true;
}
//出队 在头部
bool pop(PHead pq, ELEMTYPE* rtval)
{
assert(pq != nullptr);
if (pq->front == nullptr) return false;
//判断该结点是不是仅剩的唯一节点
if (nullptr == pq->front->next)
{
ListNode* p = pq->front;
*rtval = p->data;
pq->rear = nullptr;
pq->front = nullptr;
free(p);
p = nullptr;
}
ListNode *q = pq->front;
*rtval = q->data;
pq->front = q->next;
free(q);
q = nullptr;
return true;
}
//获取队列顶部元素
bool top(PHead pq, ELEMTYPE* rtval)
{
assert(pq != nullptr);
if (pq->front == nullptr) return false;
//判断该结点是不是仅剩的唯一节点
ListNode* q = pq->front;
*rtval = q->data; //获取顶部元素
return true;
}
//获取有效长度
int GetLength(PHead pq)
{
assert(pq != nullptr);
int len = 0;
ListNode* p = pq->front;
for (; p != nullptr; p = p->next)
{
len++;
}
return len;
}
//判空
bool IsEmpty(PHead pq)
{
return pq->front == nullptr;
}
//判满 单链表没有判满
//清空
void Clear(PHead pq)
{
Destory(pq);
}
//销毁
void Destory(PHead pq)//一个指针
{
if (IsEmpty(pq))
{
ListNode* p = pq->front;
pq->front = p->next;
free(p);
p = nullptr;
}
pq->front = pq->rear = nullptr;
}
void Destory2(PHead pq)//两个指针
{
ListNode* p = pq->front;
ListNode* q = nullptr;
if (IsEmpty(pq))
{
q = p->next;
free(p);
p = q;
}
pq->front = pq->rear = nullptr;
}
void show(PHead pq)
{
assert(pq != nullptr);
ListNode* p = pq->front;
for (; p != nullptr; p = p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
测试
int main()
{
Head pq;
Init_Iqueue(&pq);
for (int i = 0; i < 10; i++)
{
push(&pq, i);
}
show(&pq);
int tmp1;
pop(&pq, &tmp1);
show(&pq);
int tmp2;
bool tag = top(&pq, &tmp2);
if (tag)
{
printf("top->%d\n", tmp2);
}
show(&pq);
int len = GetLength(&pq);
printf("%d\n", len);
Destory(&pq);
len = GetLength(&pq);
printf("%d\n", len);
}