数据结构笔记-队列

142 阅读5分钟

特点:插入只能在表尾(队尾)a1进行,删除只能在表头(队首)an进行。先进先出。
双端队列:允许同时从前后端添加和删除元素,是队列和栈的结合体。
输出受限双端队列:一端允许插入和删除,另一端只允许删除。
输入受限双端队列:一端允许插入和删除,另一端只允许插入。
超队列:插入只能在表尾进行,删除允许在两端进行。
存储方式:顺序队列,链队

一、顺序队列

建立空队列

#define MAXSIZE 5
typedef struct {
	int data[MAXSIZE];
	int f, r;//队首front和队尾rear指针,指向队首和队尾位置(元素下标)
}SeQueue;
SeQueue q;

//初始化,建立空队列
q.f = 0;
q.r = 0;

入队

void EnQueue(SeQueue q, int x) {
	//第一个元素是data[0],入队后q.r指向data[1],即下一个要入队的地方
	if (q.r < MAXSIZE) {
		q.data[q.r] = x;
		q.r++;
}
	else printf("overflow");
}

出队

void DeQueue(SeQueue q) {
	if (q.f!=q.r)//队不空,或者q.f<q.r
		q.f++;
}

当入队和出队次数相同时,q.r和q.f指向同一个位置,满足判空条件,但此时队列明明还有位置(q.r < MAXSIZE),这种现象称为假溢出

解决办法:循环队列,将顺序队列data[0...MAXSIZE-1]看成头尾相接的循环结构。但此时队空和队满的判断条件都变成了q.r==q.f,此时有两种解决方案。
1.牺牲一个存储位置,当(q.r+1)%MAXSIZE==q.f时,队满,q.r==q.f时队空。
2.设一个计数器,初始化清零,入队时计数器+1,出队时计数器-1。

以下是基本操作

#define MAXSIZE 5
typedef struct {
	int data[MAXSIZE];
	int f, r;//队首front和队尾rear指针,指向队首和队尾位置(元素下标)
}SeQueue;
SeQueue q;

//初始化,建立空队列
q.f = 0;
q.r = 0;

//入队
void EnQueue(SeQueue q, int x) {
	if ((q.r + 1) % MAXSIZE != q.f)//有空间 
	{
		q.data[q.r] = x;
	q.r = (q.r + 1) % MAXSIZE;
}
	else printf("overflow");
}

//出队
void DeQueue(SeQueue q) {
	if (q.r != q.f)//队不空
		q.f = (q.f + 1) % MAXSIZE;
}

//元素个数
int n = (q.r - q.f + MAXSIZE) % MAXSIZE;

二、链队

建立结构体维护队列

#define MAXQSIZE 5//最大队列长度

//声明一个结构体来表示节点
typedef struct Node{
	int data;   //节点数据域
	struct Node* next; //节点指针域
}QueueNode;

//声明一个结构体来定义上面这个结构体的两个指针
typedef struct{
	QueueNode* front, * rear;//分别指向队首和队尾的指针
}SqQueue;

建立空队列

void Init(SqQueue* s){
	QueueNode* p;
	p = (QueueNode*)malloc(sizeof(QueueNode));//为指向节点的指针分配空间
	p->next = NULL;
	s->front = s->rear = p;
}

判断队列是否为空

int Empty(SqQueue s)
{
	if (s.front == s.rear)
	{
		return 1;//队空
	}
	return 0;
}

计算队列长度

int QueueLength(SqQueue Q) {
	return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}

//第二种写法,求队列长度
int GetLength(SqQueue s)
{
    //声明一个节点类型的指针
    QueueNode* p;
    //让p指向队列的头指针
    p = s.front;
    //声明一个变量用来记录队列当前长度
    int length = 0;
    while (p->next)//当指针p所指的节点不为空时执行循环体
    {
        length++;
        p = p->next;
    }
    return length;//返回当前队列的长度
}

入队

void EnQueue(SqQueue* s, int x){
	//声明一个节点类型的指针变量用来存储要入队的元素
	QueueNode* p;
	p = (QueueNode*)malloc(sizeof(QueueNode));
	if (!p) {
		printf("内存分配失败\n\n");
		return;
	}
	p->data = x;        //指针指向的节点的数据域存放x
	p->next = NULL;     //指针指向的节点的指针域置为空
	s->rear->next = p;   //将队列的尾指针的指针域next指向指针p所指的节点
	s->rear = p;         //将队列的尾指针向后移一位,指向刚入队的节点的位置
}

获取队首元素

int GetTop(SqQueue s)
{
	//首先判断队列是否为空
	if (Empty(s))
	{
		printf("队列为空,无法获取队首元素\n\n");
		return 0;
	}
	return s.front->next->data;//不为空的话就返回队首指针指向的第一个元素的数据域
}

出队

int DeQueue(SqQueue* s){
	//先判断队列是否为空
	if (Empty(*s))
	{
		printf("当前队列为空,无法执行出队操作\n\n");
		return -1;
	}
	//用临时变量保存出队的元素
	QueueNode* p;
	p = s->front->next;
	if (p == s->rear)    //如果p指向了队尾节点
	{
		s->front = s->rear;  //那么删除之后队首指针和尾指针指向同一个位置
	}
	int e = p->data;
	s->front->next = p->next;//将队首指针指向第一个节点的下一个节点。
	free(p);
        return e;
}

主函数

{
    int e;
    SqQueue s;
    Init(&s);
    printf("当前队列是否为空,为空返回真1,不为空返回假0:%d\n\n", Empty(s));
    printf("当前队列的长度为:%d\n\n", GetLength(s));
    //入队
    EnQueue(&s, 5);
    EnQueue(&s, 4);
    EnQueue(&s, 6);
    EnQueue(&s, 2);
    printf("当前队列是否为空,为空返回真1,不为空返回假0:%d\n\n", Empty(s));
    printf("当前队列的长度为:%d\n\n", GetLength(s));
    //获取当前队首元素
    e = GetTop(s);
    if (e) {
        printf("当前队列的队首元素为:%d\n\n", e);
    }
    //执行出队操作
    DeQueue(&s);
    printf("出队的元素为:%d,当前队列的长度为:%d\n\n", e, GetLength(s));
    //获取当前队首元素
    e = GetTop(s);
    if (e) {
        printf("当前队列的队首元素为:%d\n\n", e);
    }

    //再执行一次出队操作
    DeQueue(&s);
    printf("出队的元素为:%d,当前队列的长度为:%d\n\n", e, GetLength(s));
    //获取当前队首元素
    e = GetTop(s);
    if (e) {
        printf("当前队列的队首元素为:%d\n\n", e);
    }
    //再执行一次出队操作
    DeQueue(&s);
    printf("出队的元素为:%d,当前队列的长度为:%d\n\n", e, GetLength(s));
    //获取当前队首元素
    e = GetTop(s);
    if (e) {
        printf("当前队列的队首元素为:%d\n\n", e);
    }
    return 0;
}