数据结构——队列(循环队列)

队列:也是一种受到限制的线性表,一端进行入队,另一端进行出队,我们将入队的一端一般称作队尾,另一端称作队头。

特点:先进先出。

那么顺序表实现的队列为什么叫做循环队列?

关于循环队列队列的三个难点:

第一个难点:怎么让队列也可以实现入队和出队的时间复杂度都是O(1)?

 

最开始这是由顺序表实现的一个队列,里面的数据为1到5。

我们将6入队,会发现入队的时间复杂度为O(1),只需将6放在5号下标的格子位置。

如果将1出队,那么我们需要将0号下标位置里的1删除,还要将后边的数据依次向前移一个格子,这个时候时间复杂度为O(n),因为需要移动数据。

那应该怎么办呢?这个时候就需要一个队头指针和一个队尾指针,队尾指针指向的是下一个存放数据的位置。

解决办法:想象成环形,让数据不动,指针动。

比如将1出队,我们只需要将0号下标位置里的元素删除,然后再将队头指针指向1号下标的位置。

同时我们还要将它想象成一个环状,如果2到9号下标都有元素,0号和1号下标位置为空,此时,队头指针在2号下标位置,但该队列并没有满(因为0号和1号下标),我们把它想象成环状,这个时候就可以将队尾指针指向0号下标,又可以入队。

注意:这个循环只是一个概念,真是并不存在!

 第二个难点:在第一个解决问题的基础上,会发现一个BUG,判空和判满条件一致(队头指针==队尾指针),怎么将其区分开?

队列为空时队头指针和队尾指针都指向同一个位置,队列满的时候也是相同情况(队头指针==队尾指针)

这里给出两种解决方案:

第一种:加上一个计数器cout(保存有效值个数)
队头指针==队尾指针&&cout==0(判空)
队头指针==队尾指针&&cout!=0(判满)
第二种:在队尾处:浪费一个空间不用,这个浪费的空间不在存储有效值,而是当做一个标记位

一般来说我们用第二种办法,这里的代码也是第二种办法。

第三个难点:怎么求有效长度?

这里记住公式:length=(rear-front+MAX_SIZE)%MAX_SIZE

rear(队尾指针),front(队头指针),MAX_SIZE(可以理解为格子数),此指针非彼指针。

循环队列结构体设计:

// 循环队列结构体设计:
typedef int ELEM_TYPE;
typedef struct Queue
{
	ELEM_TYPE* data;//这个指针用来接收malloc从堆里申请的数组
	int front;//队头指针(实际上保存的是数组下标,int)
	int rear;//队尾指针 (实际上保存的是数组下标,int)
}Queue,*PQueue;

循环队列可执行函数声明:

//初始化
void Init_queue(PQueue pq);

//入队
bool Push(PQueue pq, ELEM_TYPE val);

//出队(还要获取出队的值,需要借助一个输出参数rtval)
bool Pop(PQueue pq, ELEM_TYPE* rtval);

//获取队头值
bool Top(PQueue pq, ELEM_TYPE* rtval);

//判空
bool IsEmpty(PQueue pq);

//判满
bool IsFull(PQueue pq);

//获取有效长度
int Get_length(PQueue pq);

//查找
int Search(PQueue pq, ELEM_TYPE val);

//清空
void Clear(PQueue pq);

//销毁
void Destroy(PQueue pq);

//打印
void Show(PQueue pq);

代码:

先设立一个宏

#define MAX_SIZE 100

初始化:

//初始化
void Init_queue(PQueue pq)
{
	assert(pq!= NULL);
	pq->data = (ELEM_TYPE*)malloc(sizeof(ELEM_TYPE) * MAX_SIZE);
	assert(pq->data != NULL);
	pq->front = pq->rear = 0;
}

入队:

bool Push(PQueue pq, ELEM_TYPE val)
{
	assert(pq != NULL);
	if (IsFull(pq))//判满
	{
		return false;
	}

	pq->data[pq->rear] = val;
	//pq->rear++;//error
	pq->rear = (pq->rear + 1) % MAX_SIZE;

	return true;
}

注意,这里的队尾指针不可以通过++方式向后移,而是pq->rear = (pq->rear + 1) % MAX_SIZE;

出队:

//出队(还要获取出队的值,需要借助一个输出参数rtval)
bool Pop(PQueue pq, ELEM_TYPE* rtval)
{
	assert(pq != NULL);
	assert(rtval != NULL);
	if (IsEmpty(pq))//判空,如果空,返回假
	{
		return false;
	}
	*rtval = pq->data[pq->front];
	//pq->front++;//error
	pq->front = (pq->front + 1) % MAX_SIZE;
	return true;
}

获取队头值

//获取队头值
bool Top(PQueue pq, ELEM_TYPE* rtval)
{
	assert(pq != NULL);
	assert(rtval != NULL);
	*rtval = pq->data[pq->front];
	return true;
}

判空,判满,获取有效长度,查找

//判空
bool IsEmpty(PQueue pq)
{
	assert(pq != NULL);
	return pq->rear  == pq->front;
}

//判满
bool IsFull(PQueue pq)
{
	assert(pq != NULL);
	return pq->front == (pq->rear + 1) % MAX_SIZE;
}

//获取有效长度
int Get_length(PQueue pq)
{
	assert(pq != NULL);
	return (pq->rear - pq->front + MAX_SIZE) % MAX_SIZE;
}

//查找
int Search(PQueue pq, ELEM_TYPE val)
{
	assert(pq != NULL);
	for (int i = pq->front; i != pq->rear; i = (i + 1) % MAX_SIZE)
	{
		if (pq->data[i] == val)
		{
			return i;
		}
	}

	return -1;
}

清空,销毁,打印

//清空
void Clear(PQueue pq)
{
	assert(pq != NULL);
	pq->front = pq->rear = 0;
}

//销毁
void Destroy(PQueue pq)
{
	assert(pq != NULL);
	free(pq->data);
	pq->data = NULL;
	pq->front = pq->rear = 0;
}

//打印
void Show(PQueue pq)
{
	assert(pq != NULL);
	for (int i = pq->front; i != pq->rear; i=(i + 1) % MAX_SIZE)
	{
		printf("%d ", pq->data[i]);
	}
	printf("\n");
}

测试:

int main()
{
	Queue head;//建立一个循环队列
	Init_queue(&head);//初始化

	for(int i=0; i<20; i++)//将1到20进队
	{
		Push(&head, i+1);
	}
	Show(&head);//打印

	ELEM_TYPE tmp;
	Pop(&head, &tmp);//出队
	printf("pop = %d\n", tmp);//打印出队的值
	Show(&head);//打印出队后的队列
	printf("length = %d\n", Get_length(&head));//打印队列有效长度

	ELEM_TYPE flag;
	Top(&head, &flag);//获取队头元素
	printf("top = %d\n", flag);//打印队头元素
	Show(&head);//打印队列,没有出队
	printf("length = %d\n", Get_length(&head));//打印有效长度

	Clear(&head);//清空
	Show(&head);//打印
	printf("length = %d\n", Get_length(&head));//打印有效长度

	Destroy(&head);//销毁
	return 0;
}

结果:没有内存泄漏!

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值