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


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

    循环队列

    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
    #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);
    }
    
  • 相关阅读:
    14015.xilinx-芯片手册阅读笔记
    CentOS7上安装MySQL 5.7.32(超详细)
    牛客小白月赛61 B.柜台结账(模拟+字符串)
    Linux V4L2编程和驱动底层分析
    linux下磁盘分区和逻辑卷管理
    ES是什么?ES的使用场景有哪些?分词器??
    吴恩达deeplearning.ai:决策树模型
    在 kubernetes 环境中实现 gRPC 负载均衡
    后台模仿promise.all实现异步,提升代码速度
    创建vue项目的安装汇总
  • 原文地址:https://blog.csdn.net/weixin_52958292/article/details/127096988