数据结构与算法——队列的定义与运算

目录

一、队列的定义

二、队列的抽象数据类型

三、队列的顺序存储结构与操作实现 

四、队列的链接存储结构和操作实现 


一、队列的定义

        队列(Queue)简称队,它同栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一段进行插入,而在另一端进行删除。我们吧进行插入的一段成为队尾(rear),进行删除的一端称为队头(front)。向队列中插入新元素称为入队或进队,新元素入队后又作为队尾;从队列中删除元素称为出队或离队,元素出队后,其后继元素成为新的队头元素。由于队列的插入、删除都是从队的一段进行的,每个元素必须以一定的顺序出队,即先进是元素先出队,所以也称队列为先进先出表(First In First Out,简称FIFO)。

        日常生活中,人们为购物或者等车是所排的队就是一个队列,新来的人接到队尾后面,站在队头的人购完物即刻离开,当最后一个人离队后,整个队列为空。

二、队列的抽象数据类型

        队列的抽象数据类型中的数据部分具有ElemType元素类型的一个队列,它可以采样任一种存储结构来实现:操作部分包括入队、出队、获取队头元素、判断队列是否为空等。下面给出队列抽象数据类型的具体定义:

ADT Queue :

           DATA :Q 

           Operation: //初始化队列                    void InitQueue(Queue& Q)

                               //插入元素至队尾             void EnQueue(Queue& Q,ElemType item)

                               //获取队头元素                 ElemType GetQueue(Queue& Q)

                              //删除队头元素                  void PopQueue(Queue& Q)

                              //判断是否为空队列           bool EmptyQueue(Queue& Q)

                              //清除队列的所有元素       void ClearQueue(Queue& Q)

            End Queue

三、队列的顺序存储结构与操作实现 

        队列的顺序存储结构需要使用一个数组和2至3个整形变量来实现,利用数组来顺序存储队列中的所有元素,利用一个整形变量存储队头的位置(通常存储队头元素的前一个位置),利用另一个整形变量表示队尾的位置,第三个整型变量存储队列中当前的元素个数。我们将指向队头前一个位置的变量称为头指针,由它加1可得到第一个元素的下标位置,指向队尾的变量称为位置,由它可直接得到队尾元素的下标。分别用front、rear表示,存储队列的长度为len,则元素类型为ElemType的队列的顺序存储结构可描述为:

        struct Seq_Queue   //结构体类型

        {

                ElemType queue[MAX_SIZE];    //MAX_SIZE表示队列的最大长度

                int   front,rear;

                int   len;

        } 

若要对存储队列的数组空间采样动态分配,则Seq_Queue结构体类型可定义为: 

        struct Seq_Queue   //结构体类型

        {

                ElemType  *queue; //指向存储队列的数组空间

                int   front,rear;

                int   len;

                int   capacity;  //即queue所指数组空间的大小

        } 

         每次插入一个元素,就要让队头指针后移一位,然后再在该位置上插入新元素,当队尾指针指向数组空间的最后一个位置,即MAX_SIZE - 1时,若队头元素的前面仍存在空闲的位置,则说明未占满整个数组空间,下一个存储位置应该是下标为0的空间位置,即我们可以通过语句rear=(rear+1)%MAX_SIZE使整个数组空间变为首尾相连的环,所以顺序队列又称循环队列。

        每次删除一个元素时,若队列不空,则把队尾指针后移,使之指向队头元素,然后再返回该元素的值,使队头指针后移也必须进行取模运算,即front=(front+1)%MAX_SIZE,这样才能实现收尾衔接。若len=0,则队列为空,若len=MAX_SIZE,则队列已满。

下面来实现一下队列运算的算法:(进行了一定的修改)

#include
#include
#include

#define MAX_SIZE 8
#define INC_ 3

typedef int ElemType;

typedef struct Seq_Queue
{
	ElemType *queue;
	int front;
	int rear;
	int len;
	int capcaity;
}Seq_Queue;


//增配空间
bool Inc_(Seq_Queue *Q)
{
	Q->queue = (ElemType*)realloc(Q->queue, sizeof(ElemType)*(Q->capcaity + INC_));
	assert(Q->queue != NULL);
	Q->capcaity = Q->capcaity + INC_;
	return true;
}

//初始化
void Init_Queue(Seq_Queue *Q)
{
	Q->queue = (ElemType*)malloc(sizeof(ElemType)*MAX_SIZE);
	assert(Q->queue != NULL);
	Q->front = Q->rear = 0;
	Q->len = 0;
	Q->capcaity = MAX_SIZE;
}

//入队
void EnQueue(Seq_Queue *Q, ElemType x)
{
	if (Q->len == Q->capcaity - 1)
	{
		if (!Inc_(Q))
		{
			printf("Memory Failed\n");
			return;
		}
	}
	//
	Q->rear = (Q->rear + 1) % Q->capcaity;
	Q->queue[Q->rear] = x;
	Q->len++;
}

//判断队列是否为空
bool EmptyQueue(Seq_Queue *Q)
{
	return Q->len == 0;
}

//取队列的第一个元素
ElemType GetQueue(Seq_Queue *Q)
{
	if (EmptyQueue(Q))
	{
		printf("empty!\n");
		return -1;
	}
	return Q->queue[(Q->front+1)%Q->capcaity];
}

//删除第一个元素
void PopQueue(Seq_Queue *Q)
{
	if (EmptyQueue(Q))
	{
		printf("empty!\n");
		return;
	}
	Q->front = (Q->front + 1) % Q->capcaity;
	Q->len--;
}

//清除队列,释放空间
void ClearQueue(Seq_Queue *Q)
{
	free(Q->queue);
	Q->capcaity = 0;
	Q->front = Q->rear = 0;
	Q->len = 0;
}

int main()
{
	Seq_Queue Q;
	Init_Queue(&Q);
	for (int i = 1; i <= 10; i++)
	{
		EnQueue(&Q, i);
	}

	for (int i = 1; i <= 8; i++)
	{
		cout << GetQueue(&Q)<<" ";
		PopQueue(&Q);
	}

	if (EmptyQueue(&Q))
	{
		cout << "hahfahfhah\n";
	}

	ClearQueue(&Q);

	system("pause");
	return 0;
}

在上述队列的顺序存储结构中,若省略len域也是可以的,但此时的长度为MAX_SIZE的数组空间最大只能存储长度为MAX_SIZE的队列,也就是说必须有一个位置空闲着。这是因为,若使用全部位置存储队列,但队头和队尾指针指向同一个位置时,可能为空队,也可能为满队,这就存在二义性,无法进行判断。为了解决这个问题,只能牺牲一个位置的空间,利用队头和队尾指针是否相等作为判断空队的条件,而利用队尾指针加1取模后是否等于队头指针作为满队的条件。对于这种算法,我们可以略加修改就能得到:

        (1)删除原来对长度域len的初始化操作;

        (2)把判断满队的条件Q->len=Q->capcaity-1改为(Q->rear+1)%MAX_SIZE;

        (3)把判断空队的条件Q->len==0改为Q->front=Q->rear;

                。。。。。。。。以上只做分析,并不一定为最终正确的做法,具体修改步骤读者自行解决。

四、队列的链接存储结构和操作实现

        队列的链接存储结构也是通过结点构成的单链表实现的,此时只允许在单链表的表头进行删除,表尾进行插入,因此需要两个指针:队头front、队尾rear。用队头指向表头结点的存储位置,用队尾指向表尾结点的存储位置。用于存储队列的单链表简称链队列。具体定义如下:

结点:

                typedef struct node

                {

                        ElemType data;   //值域

                        struct node *next;

                }node;

链队列:

                typedef struct LinkQueue

                {

                        node *front;   //队头指针

                        node *rear;   //队尾指针

                }LinkQueue;

下面来实现一下链队列结构的算法:

#define _CRT_SECURE_NO_WARNINGS

#include
#include
#include

typedef struct node
{
	int data;
	node* next;
}node;

typedef struct linkQueue
{
	node* front;
	node* rear;
}linkQueue;

typedef linkQueue LQ;

void initQueue(LQ* Q);
void enQueue(LQ* Q, int x);
void popQueue(LQ* Q, int* item);
void show(LQ* Q);
int gethead(LQ* Q);
int QueueEmpty(LQ* Q);


void initQueue(LQ* Q)
{
	Q->front = Q->rear = (node*)malloc(sizeof(node));
	assert(Q->front!=NULL);
	Q->front->next = NULL;
}

void enQueue(LQ* Q, int x)
{
	node* p = (node*)malloc(sizeof(node));
	assert(p!=NULL);
	p->data = x;
	p->next = NULL;

	Q->rear->next = p;
	Q->rear = p;
}

int popQueue(LQ* Q)
{
	node* p = Q->front;
	if (p == Q->rear)
	{
		printf("enpty!!!\n");
		return -1;
	}

	p = p->next;
	free(Q->front);
	Q->front = p;

	return p->data;
}

void show(LQ* Q)
{
	node* p = Q->front;
	if (p == Q->rear)
	{
		printf("empty!!!\n");
		return;
	}
	p = p->next;
	while (p)
	{
		printf("%d->", p->data);
		p = p->next;
	}
	printf("NUL.\n");
}

int gethead(LQ* Q)
{
	node * p = Q->front;
	if (p == Q->rear)
	{
		printf("empty!!!\n");
		return -1;
	}
	else
	{
		p = p->next;
	}
	return p->data;
}


int QueueEmpty(LQ* Q)
	{
		if (Q->front == Q->rear)
		{
			return 1;
		}
		else
		{
			return 0;
		}
}





int main()
{
	LQ Q;
	initQueue(&Q);
	printf("请输入数据(个数为5):");
	for (int i = 0; i < 5; i++)
	{
		int n;
		scanf("%d", &n);
		enQueue(&Q, n);
	}
	int m;
	for (int i = 0; i < 5; i++)
	{
		m = popQueue(&Q);
		printf("%d-》", m);
	}
	printf("nul.\n");
	show(&Q);

	return 0;
}

下一次,我们将会介绍一些队列的应用实例来帮助大家更好的理解队列的存储结构,这次就草草了结吧!

你可能感兴趣的:(数据结构与算法专题,数据结构,算法,c语言)