数据结构 --- c语言实现普通数组队列 & 循环队列 & 链式队列 & 优先队列

队列的特性

  • 存储数据的方式:一般情况下为 FIFO 先进先出的结构

  • 类似食堂排队打饭,排在前面的先打饭

队列的属性

  • 容量   

  • 队头标记

  • 队尾标记

队列的分类

  • 数组队列(普通队列+循环队列)

  • 链式队列 

  • 优先队列(类似 vip 服务)

 普通数组队列 

  • 用一个数组充当容量

  • 队头标记 & 队尾标记就是数组下标,可以把它们的初始值等于 -1 (保持下标的一致性)或者全部等于 0,无强制性要求,只是在放数据的时候下标的操作不太一样而已

  • 刚开始没有元素,队头和队尾都在同一个位置
  • 放数据到队列中,用的是队尾去操作(队尾往后移动);拿数据用的是队头去操作(队头往后移动)
  • 弊端:一旦做了出队操作,就会存在伪溢出问题:入完数据做出队操作,相对于以往来说容量改变了

  • 原因:做了一个出队操作后(队头往后挪了),队头对应的下标不再是从 0 开始(队头来到下标[1]的位置),下标[0]对应的内存无法使用,假设刚开始创建队列时的最大容量是 6 ,出队一个元素后,最大容量变成了 5

  • 一次性队列,可以把数据都存满,但是不能重复去操作

  • 通过取余 % 让队尾 tail 回到前面去,不存在伪溢出问题(循环队列的方式)

  • 假设 max == 10,一旦队头变化(假设队头到达对应数组下标为1的位置),就存不了10个元素,如果用 curSize 判断满的状态会引发中断

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//抽象结构--->以整型数据为例
typedef struct Queue
{
	int* qMem;              //队列内存
	int front;              //队头标记
	int tail;               //队尾标记
	int curSize;			//当前元素个数
	int maxSize;            //最大容量
}QUEUE,*LPQUEUE;
//创建队列
LPQUEUE createQueue(int max) 
{
	//描述结构的最初状态--->用结构体指针表示一个队列
	LPQUEUE queue = (LPQUEUE)malloc(sizeof(QUEUE)); //动态内存申请
	assert(queue);
    //给队列属性做初始化
	queue->maxSize = max;
	queue->qMem = (int*)malloc(sizeof(int) * max);  //数组的内存二次申请
	assert(queue->qMem);
	queue->curSize = 0;
	queue->front = 0;
	queue->tail = 0;
	return queue;
}
//大小
int  size(LPQUEUE queue)
{
	return queue->curSize;
}
//判断是否为空
int empty(LPQUEUE queue) 
{
	return queue->curSize == 0;
}
//入队 把元素放到队列中队尾做变化 入的队 入的数据
void push(LPQUEUE queue, int data) 
{
	//判断是否为满的状态 用数组 放进去必须考虑满
	if (queue->curSize == queue->maxSize) //一旦队头变化放不了这么多数据 存在伪溢出问题
	{
		printf("满了!\n");
		return;
	}
	queue->qMem[queue->tail++] = data;    //把数据存到队列后队尾++
	queue->curSize++;                    
}
//获取队头元素
int  front(LPQUEUE queue) 
{
	return queue->qMem[queue->front];
}
//出队 队头往后走
void pop(LPQUEUE queue) 
{
    //出队要考虑是否为空的状态
	if (queue->curSize == 0) 
	{
		printf("队列为空,无法出队\n");
		return;
	}
	queue->front++;                      //如果能够出队 队头往后走即可
	queue->curSize--;
}
int main() 
{
	LPQUEUE queue = createQueue(10);     //创建队列 假设存放10个元素
	for (int i = 0; i < 10; i++) 
	{
		push(queue, i);                  //入队
	}
    /*做了一次出队操作后 队头此时移动到1的位置会存在问题*/
	printf("%d\t", front(queue));        //0    
	pop(queue);                        
	push(queue, 100);                    //入队实际上超过了数组的容量
	push(queue, 200);                    //满了!
	while (!empty(queue))                //队列不为空出队
	{
		printf("%d\t", front(queue));    //获取队头元素
		pop(queue);                      //出队
	}
	return 0;                            //0 1 2 3 4 5 6 7 8 9
}
/*输出 一旦做了出队操作不能重复使用*/

0    满了!
1 2 3 4 5 6 7 8 9 100                   //数组下标越界访问 vs编译器测试时没有引发中断

 循环队列

  • 为了避免普通队列做完一次出队后想要重复使用,导致伪溢出问题而存在的

  • 数组下标不变,只是改变队列的 front 和 tail 的值去做数组的遍历

  • 队列永远都是先进先出,虽然数组中的顺序不是按照插入的顺序,但是出来仍然按照插入的顺序:不会影响打印结果,打印仍旧是有序的,按照插入的顺序先插入的先出去 FIFO 先进先出 

判断队列满的状态的两种方式

  • 当队尾 == 队头 & 队头 != 0 就是满的状态(也有可能是空的状态 队头 == 0)

  • if(queue->front==(queue->tail+1)%Max)也可以判断满的状态

  • 建议用 curSize 判断,否则满的状态不太好判断

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef struct Queue
{
	int* qMem;
	int front;
	int tail;
	int curSize;
	int maxSize;
}QUEUE,*LPQUEUE;
//创建
LPQUEUE createQueue(int maxSize)
{
	LPQUEUE queue = (LPQUEUE)malloc(sizeof(QUEUE));
	assert(queue);
	queue->maxSize = maxSize;
	queue->qMem = (int*)malloc(sizeof(int) * maxSize);
	queue->front = 0;
	queue->tail = 0;
	queue->curSize = 0;
	return queue;
}
//大小
int  size(LPQUEUE queue) 
{
	return queue->curSize;
}
//判断队列是否为空
int empty(LPQUEUE queue) 
{
	return queue->curSize == 0;
}
//入队
void push(LPQUEUE queue, int data)
{
	if (queue->curSize == queue->maxSize) 
	{
		printf("队列满了,无法入队!\n");
		return;
	}
	queue->qMem[queue->tail] = data;                    //队尾存数据
	queue->tail = (queue->tail + 1) % queue->maxSize;   //让它满的时候出队时回到前面去
	queue->curSize++;
}
//获取队头元素
int front(LPQUEUE queue) 
{
	return queue->qMem[queue->front];
}
//出队
void pop(LPQUEUE queue) 
{
	if (queue->curSize == 0)
	{
		printf("队列为空无法出队!\n");
		return;
	}
	queue->front = (queue->front + 1) % queue->maxSize;
	queue->curSize--;
}
int main() 
{
	LPQUEUE queue = createQueue(10);
	for (int i = 0; i < 10; i++)
	{
		push(queue, i);
	}
	pop(queue);		   //0
	push(queue, 100);  //1-9-100
	while (!empty(queue))
	{
		printf("%d\t", front(queue));
		pop(queue);
	}
	return 0;
}
/*输出 能重复使用*/

1 2 3 4 5 6 7 8 9 100 //只能放10个数据

 链式队列

  • 一般用无头链表记录头和尾的方式,不需要找表尾,因为 tailNode 永远是指向链表的表尾

  • 入队:链表的尾插法

  • 出队:链表的头删法

  • 空的状态:队列的最初状态,没有节点,两个结构体指针都指向空

  • 只有一个节点的情况(既是队头也是队尾),队头和队尾都是指向同一个节点

  • 两个节点的情况:把原来表尾(原来表尾指向1)的 next指针 指向新节点,再把表尾移到新节点的位置即可

  • 链表使用的是零散内存,无大小限制,相比数组队列不需要传maxSize

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//节点
typedef struct Node 
{
	int data;                 //数据域
	struct Node* next;        //指针域
}NODE,*LPNODE,*LIST;     
//创建节点--->把用户的数据变成一个节点才能插到队列中去 
LPNODE createNode(int data) 
{
	LPNODE newNode = (LPNODE)malloc(sizeof(NODE));
	assert(newNode);
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
//描述链表
typedef struct Queue 
{
	int curSize;             //大小
	LPNODE frontNode;        //头节点
	LPNODE tailNode;         //尾节点
}QUEUE,*LPQUEUE;
//创建队列
LPQUEUE createQueue() 
{
	LPQUEUE queue = (LPQUEUE)malloc(sizeof(QUEUE)); //用指针表示一个队列
	assert(queue);
	queue->curSize = 0;
	queue->frontNode = NULL; //队列刚开始都指向空
	queue->tailNode = NULL;
	return queue;
}
//万金油
int  size(LPQUEUE queue) 
{
	return queue->curSize;
}
int empty(LPQUEUE queue) 
{
	return queue->curSize == 0;
}
//入队 入的队 入的数据
void push(LPQUEUE queue, int data) 
{
	LPNODE newNode = createNode(data);  //入队前需要把用户的数据变成一个节点
	//无头链表尾部插入
	if (queue->curSize == 0)            //第1次插入 插入的链表要成为队头和队尾
	{
		queue->frontNode = newNode;    
		//queue->tailNode = newNode;
		//queue->curSize++;
	}
	else                                 //链表有数据
	{
		queue->tailNode->next = newNode; //把tailNode->next指向新节点 
		//queue->tailNode = newNode;     //把tailNode移到新节点的位置
		//queue->curSize++;
	}
	queue->tailNode = newNode;           //相同代码放在外面
	queue->curSize++;
}
//获取队头元素
int front(LPQUEUE queue)
{
	return queue->frontNode->data;
}
//出队
void pop(LPQUEUE queue) 
{
	//链表头删法
	if (queue == NULL || queue->curSize == 0) 
	{
		printf("队列为空,无法出队!\n");
		return;
	}
	struct Node* nextNode = queue->frontNode->next; //记录下一个节点
	free(queue->frontNode);                         //释放队头
	queue->frontNode = nextNode;                    //把队头移到下一个节点的位置
	queue->curSize--;
}
int main() 
{
	LPQUEUE queue = createQueue(10);
	for (int i = 0; i < 10; i++)
	{
		push(queue, i);
	}
	while (!empty(queue))
	{
		printf("%d\t", front(queue));
		pop(queue);
	}
	return 0;
}

/*测试代码*/

0 1 2 3 4 5 6 7 8 9

 优先队列

  • 优先队列是按照优先权(自己设定的规则)出队的

  • 可以用来描述操作系统的调度过程

  • 可以用数组 | 链表实现

  • 常把比较准则 + 数据本身构成一个数据,数据按照比较准则(优先权)做比较

  • 优先队列出队所有数据都是有序的,从小到大出队(找最小的) | 从大到小出队(找最大的)

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#define MAX 100
//数据--->不会存在固定的比较准则 需要为数据构建一个比较准则
typedef struct Data 
{
	int priority;			//比较准则 按比较准则来出队
	char name[20];			//数据本身 
}DATA,*LPDATA,DARR[MAX];    //定义1个结构体数组
//描述优先队列
typedef struct priQueue 
{
	int curSize;
	DARR qMem;              //数组
}PRIO,*LPPRIO;
//创建优先队列
LPPRIO createPriorityQueue() 
{
	LPPRIO queue = (LPPRIO)malloc(sizeof(PRIO)); //用指针表示一个优先队列做动态内存申请
	assert(queue);
	queue->curSize = 0;
    //数组的初始化--->用memset给一段内存做初始化
	memset(queue->qMem, 0, sizeof(DATA) * MAX);  //把所有的内存初始化为0
	return queue;
}
//万金油
int  size(LPPRIO queue)
{
	return queue->curSize;
}
int empty(LPPRIO queue) 
{
	return queue->curSize == 0;
}
//入队 和普通队列入队一样
void push(LPPRIO queue,DATA data) 
{
	if (queue->curSize == MAX) 
	{
		printf("队列满了,无法入队!\n");
	}
	else 
	{
		queue->qMem[queue->curSize++] = data;    //直接把数据放到数组中即可
	}
}
//出队 优先队列按照优先权去出队 找最大|最小的看你想要实现怎样的优先队列 这里按最小的开始出队
void pop(LPPRIO queue, DATA* data) 
{
	if (queue->curSize == 0) 
	{
		printf("队列为空,无法出队!\n");
	}
	else 
	{
		//最小原则--->描述贪心算法(堆也算一种队列 只是组成形式研究方法不同 用二叉树的形式)
		DATA minData = queue->qMem[0];            //假设最小元素是第1个元素
		int minIndex = 0;                         //最小下标是0
        //数组查找
		for (int i = 1; i < queue->curSize; i++) 
		{
            //如果最小值的元素>队列里的其他元素
			if (minData.priority > queue->qMem[i].priority) 
			{
				minData = queue->qMem[i];         //队列的元素成为最小元素
				minIndex = i;                     //对应下标成为最小下标
			}
		}
		//退出循环--->找到最小值                    //queue->qMem[minIndex]
		*data = minData;                          //把数据丢出去Data* 
		//删除-->数组的伪删除 把后面的往前挪 覆盖掉出队的元素
		for (int i = minIndex; i < queue->curSize; i++) 
		{
			queue->qMem[i] = queue->qMem[i + 1];  //数组的移位操作
		}
		queue->curSize--;                         //curSize--
	}
}
int main() 
{
	LPPRIO queue = createPriorityQueue();         //创建队列
    //放入元素
	DATA array[5] =
	{
		1,"张三",
		9,"李四",
		4,"小芳",
		3,"小丽",
		5,"王五",
	};
	for (int i = 0; i < 5; i++)                      //入队
	{
		push(queue, array[i]);                       //入的队 入的元素是数组
	}
	while (!empty(queue))                            //出队
	{
		DATA temp;                                   //定义传出参数
		pop(queue, &temp);                           //传出
		printf("%d:%s\n", temp.priority, temp.name); //打印传出参数 优先权 数据
	}
	return 0;
}
/*输出*/

1 张三
3 小丽
4 小芳
5 王五
9 李四 //按照优先权的顺序出队
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiuqiuyaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值