数据结构之队列

        队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。队列是一种先进先出(First In Fitst Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为对头。队列的先进先出的结构正好符合日常生活中人们的生活习惯,即排在第一位的优先出栈,最后来的人自然排在队列最后了。

        由于队列是特殊的线性表,所以也可以采用数组来顺序存储。假设一个队列有n个元素,则顺序存储的队列需要建立一个大于n的数组,并把队列的所有元素存储在数组的前n个单元,数组下标为0的一端即是对头,所谓的入队操作,其实就是在队尾追加一个元素,不需要移动任何元素,时间复杂度是O(1)。而出队时,是从下标为0的位置出队。那就意外着当执行出队操作时队列中的所有元素都得向前移动,以保证队列的对头仍然是下标为0的位置,此时时间复杂度是O(n)。其实,如果不去限制队列的元素必须存储在数组的前n个单元这一条件,出队的性能就会大大增加。这样的话,当执行出队操作时,不必所有的元素都向前移动,而只是让对头向后移动一位,指向新的队头。

        为了避免当只有一个元素时,对头和队尾重合使得处理变得麻烦,所以引入两个指针,front指针指向对头元素,rear指针指向队尾元素的下一个位置,这样当front等于rear时,此队列不是还剩一个元素,而是空队列。可是这样做的话仍然有缺陷,假设长度为5的数组,初始状态时,front与rear指针均指向下标为0的位置,当执行多次入队操作后,rear指向了下标为4的位置,front指向着下标为0的位置。然后执行某次出队操作后,rear的位置不变,front指向了下标为2的位置。此时如果再执行入队操作时,rear会指向到下标为5的位置,也就是说rear移动到了数组之外。还有,如果这个队列的末尾位置已经被占用,此时如果再入队的话,就会产生数组越界的错误,可实际上,由于此队列执行过出队操作,其下标为0和下标为1的位置仍然空闲,此现象称为“假溢出”。

        解决假溢出的最好方法就是当数组末尾被占用之后,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。以刚才的例子来说,当rear指向下标为4的位置时,再执行入队操作后,rear指向了下标为0的位置,元素成功插入到下标为4的位置。此时又有问题出现,当某次入队之后rear已经指向了front的位置,即rear和front重合了,此时就很难判断此队列是满队还是空队。解决方法是可以设置一个标识变量来判断,还有就是改变队满的条件,即保留一个元素空间。也就是说,当队列满时,数组中还会有一个空闲单元。这种判断队满的方法就是至今在使用的。此时队满的条件就是:(rear+1)%MAXSIZE==front(MAXSIZE为数组容量,%是为了整合rear与front的问题),通用的计算队列长度的公式为:(rear-front+MAXSIZE)%MAXSIZE。

        采用循环队列来存储队列的一般操作有:入队、出队等。

1.入队。向队列中插入元素。

bool EnterQueue(SqQueue* s, ElemType e)
{
	//如果队列满
	if ((s->rear + 1) % MAXSIZE == s->front)
	{
		return false;
	}
	else
	{
		s->data[s->rear] = e;	//将元素e赋值给队尾
		s->rear = (s->rear + 1) % MAXSIZE;	//rear指针向后移一位,若到最后则转到数组头部
		return true;
	}
}

2.出队。删除队中元素。

bool DeleteQueue(SqQueue* s, ElemType* e)
{
	//队空
	if (s->front == s->rear)
	{
		return false;
	}
	*e = s->data[s->front];	//将队头元素赋值给e
	s->front = (s->front + 1) % MAXSIZE;	//front指针向后移一位,若到最后则转到数组头部
	return true;
}

3.求队列长度。

//求队列长度
int QueueLength(SqQueue* s)
{
	return (s->rear - s->front + MAXSIZE) % MAXSIZE;
}

4.判空。

//判空
void QueueEmpty(SqQueue* s)
{
	if (s->front == s->rear)
	{
		printf("队空!\n");
	}
	else
	{
		if ((s->rear + 1) % MAXSIZE == s->front)
		{
			printf("队满!\n");
		}
		else
		{
			printf("此队不为空!\n");
		}
	}
}

5.打印。

void ShowQueue(SqQueue* s)
{
	for (int i = s->front; i < s->rear; i++)
	{
		printf("%d,", s->data[i]);
	}
	puts("\b;");
}

测试函数和声明:

//声明
typedef int ElemType;	//队列容量
#define MAXSIZE 10
typedef struct
{
	ElemType data[MAXSIZE];
	int front;	//头指针
	int rear;	//尾指针
}SqQueue;

//主函数
int main()
{
	SqQueue s = { { 0 }, 0, 0 };
	QueueEmpty(&s);
	EnterQueue(&s, 0);
	EnterQueue(&s, 1);
	EnterQueue(&s, 2);
	EnterQueue(&s, 3);
	EnterQueue(&s, 4);
	EnterQueue(&s, 5);
	EnterQueue(&s, 6);
	QueueEmpty(&s);
	ShowQueue(&s);
	printf("%d\n", QueueLength(&s));
	EnterQueue(&s, 7);
	EnterQueue(&s, 8);
	QueueEmpty(&s);
	ShowQueue(&s);
	printf("%d\n", QueueLength(&s));
	return 0;
}

以上即是与队列的顺序存储相关的~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值