【队列】循环队列,链式队列

队列先进先出(用循环链表就可以实现)
请添加图片描述

循环队列

1.为什么要把队列臆想成一个环?
难点1如何让出队列和入队列时间复杂度都降低为O(1)
不挪动数据,使用队头指针和队尾指针来回走动,得把它臆想成一个环。
2.判空和判满有何区分
问题:front = rear
为了区分判空和判满
判空和判满操作的判断依据出现了冲突,都是rear == front; 所以我们需要浪费一个尾部节点,当做标记位,当尾指针再向后跑一步就遇到了头指针,此时我们就认为队列满了
解决 1.自己加一个标志length
2.浪费一个空间当标记
判空:front == rear
判满:(rear+1)%Maxsize ==front
3.怎么求有效长度
获取其有效元素个数

  • 第一个MAXSIZE是为了防止出现负数
  • 第二个MAXSIZE ,防止没有出现负数的 情况下多加MAXSIZE
int 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<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>

#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);
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值