数据结构——队列

1.队列定义

队列时一种受限制的线性表,只能再一端进行插入,再另外一端进行删除。
就像现实中的排队一样在新来的人排在对尾,拍在队伍前面的人先进行服务后离开,期间不允许插队。

简单介绍:

  1. 假如有一个队列,将A,B,C,D插入队列中,对头元素是A,对尾元素是D。也就是说第一个被删除的元素是A,最后一个被删除的元素是D。由此可见,队列具有一个特征,就是先入先出,后入后出
  2. 队列需要两个指针,一个对头指针(front),通常指向队列第一个进来的元素,一个对尾指针(rear),通常指向对尾的最后一个元素,也就是最新插入进来的元素。
  3. 通常插入元素时时在对尾插入(尾插),需要对尾指针发生变化,删除元素,在对头删除(头删),需要对头指针发生变化。

2.队列表现形式

顺序表(数组)

队列较为简单的方法时数组实现,一般情况是对头放数组下标小的位置,对尾放在数组下标大的位置,刚开始时对头指针/对尾指针初始化为-1,当插入元素数时,rear向右移动一格(加1),放入对尾元素。当删除一个元素时,front向右移动一格(加1),删除对头元素。
在这里插入图片描述

随着入队和出队的操作不断进行,会是得队列整体往后偏移,致使队列出现了第三种情况。此时对尾指针已经移动到了数组最后,已经不能再插入元素,
然而对头指针前面还有许多空间可以插入,这样的现象称为假溢出现象。会导致空间的严重浪费。
于是为了解决这种情况,衍生出另外一种形式循环队列:当对尾指针和对头指针到达数组最后时,能够折回到数组开始位置。可以想象为队列的对尾相连。
而实现这一操作可以用“rear(front)%数组长度”取余运算公式实现。

在这里插入图片描述
循环队列很好的解决假溢出的现象,但是再循环队列中,我们发现用front和rear表示队列为空时,rear的值与front的值相等,表示队列满时,rear与front的值也相等,于是,如何表示队列满和空的情况呢?
方法一:队列定义时在增加一变量size来记录队列的长度,这样判断size与数组容量capacity可以直接判断队列是否是空还是满。
方法二:少用一个元素空间。如图(3)所示,此时的情况是rear指针加1的值就与front的值相等了,公式为“(rear+1)%capacity=front”。
我们采用方法二,代码在下面。

链表

链表实现注意的是front指针必须指向链表的头结点,rear指针必须指向链表的尾结点,不能颠倒。因为front指向头结点方便删除操作,如果指向尾结点,上一个结点的位置无法得到,不方便删除操作。
在这里插入图片描述

3.队列主要操作

  1. 生成空队列。初始化。
  2. 判断队列是否满。数组:(rear+1)%capacity=front。链表不需要判断。
  3. 入队。将元素插入对尾,并且让rear指向它。
  4. 判断对空。数组:rear=front。链表:即链表尾空,front,rear=NULL。
  5. 出队。在对尾删除元素,让front指向下一个元素。

4.队列的代码实现

数组:

结构:

typedef int Elementtype;//方便维护
typedef struct Quelist{
	Elementtype *a;
	int front;//对头指针
	int end;//对尾指针
	int capacity;//最大容量
}Que;

初始化:

void Quequeinit(Que *list)
{
	list->a = (Elementtype *)malloc(sizeof(Elementtype)* 12);//为数组申请空间
	list->capacity = 12;//最大容量
	list->end = 0;//对尾对头初始化下标尾0
	list->front = 0;
}

入队和判断满:

void Quelistpush(Que *list, int x)
{
	assert(list);
	if ((list->end + 1) % list->capacity == list->front){//判断对满
		printf("队列已满\n");
		exit(-1);
	}
	//list->end++;做到循环队列,不能是简单加1
	list->end = (list->end + 1) % list->capacity;//循环
	list->a[list->end] = x;
}

出队和判断对空

Elementtype Quelistpop(Que *list)
{
	assert(list);
	if (list->end == list->front){//对尾和对头相等
		printf("队列为空\n");
		exit(-1);
	}
	Elementtype ass = 0;
	list->front = (list->front + 1) % list->capacity;//循环
	ass = list->a[list->front];//元素就是front下一个的元素
	return ass;
}

打印:验证

void Quelistprint(Que *list)
{
	assert(list);
	if (list->end == list->front){
		printf("队列为空\n");
		exit(-1);
	}
	for (int i = list->front; i < list->end; i++){
		printf("%d ", list->a[i]);
	}
	printf("\n");
}

链表

结构:

typedef int Elementtype;
struct Datalist{
	Elementtype data;
	struct Datalist *next;
};

typedef struct Datalist* Dlist;
typedef struct Quelist{
	Dlist front;//头指针用来存放头节点地址
	Dlist end;//尾指针存放尾节点地址
}Que;

初始化:
也可以在主函数中定义两个datalist的指针变量front和rear,直接对front,rear置空,这时传入参数就得传入这两个变量的地址。

void Quequeinit(Que *list)
{
	assert(list);
	list->front= NULL;//直接置空
	list->end = NULL;
	
}

入队:
这里要考虑两种情况:
1.当没有结点的时候,front,rear两指针存放的都是第一个结点的地址,
2.当有结点的时候,front指针内容不变,rear存放新结点地址。

static Dlist Nodelist(int x){//创建节点
	Dlist list = (Dlist)malloc(sizeof(struct Datalist));
	list->data = x;
	list->next = NULL;
	return list;
}

//要考虑没有元素情况
void Quelistpush(Que *list, int x)
{
	assert(list);
	//assert(node);
	Dlist ass = Nodelist(x);//创建结点
	if (list->end == NULL&&list->front == NULL){//没节点
		list->front = ass;//是等于,实际front与end存放ass地址
		list->end = ass;//不是用next指向ass
	}
	else{//有节点
		list->end->next = ass;
		list->end = ass;
	}

}

出队和判断是否为空:
注意这里一开始要判定是否为空。
不为空时,也有两种情况:
1.只有一个结点。删除结点后头尾指针都置空。
2.不止一个结点:只需要变化front指针。

//要考虑只有有一个元素的情况
Elementtype Quelistpop(Que *list)
{
	assert(list);
	Elementtype ass = 0;
	if (list->end == NULL&&list->front == NULL){//判断是否尾空
		printf("Empty\n");
		exit(-1);
	}
	else{//不为空
		Dlist temp;
		if (list->front->next == NULL){//只有一个结点
			ass = list->front->data;
			list->end = NULL;
			list->front = NULL;

		}
		else{
			temp = list->front;
			ass = list->front->data;
			list->front = list->front->next;
			free(temp);
		}
	}
	return ass;


}

打印结点:验证

void Quelistprint(Que *list)
{
	assert(list);
	Dlist temp = list->front;//要用临时变量代替front,不是代替list
	if (list->end == NULL&&list->front == NULL){
		printf("Empty!\n");
		exit(-1);
	}
	else{
		while (temp){
			printf("%d ", temp->data);
			temp = temp->next;
		}
		printf("\n");
	}
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值