【算法与数据结构】队列

文章介绍了队列的概念,强调其先进先出(FIFO)特性,并对比了顺序表和队列的不同。接着详细阐述了循环队列如何解决假溢出问题,以及其在顺序表上的具体实现,包括初始化、入队、出队、查看队首元素等操作。此外,文章还提到了链表实现的队列,这种实现方式无需担心存储空间的问题。最后,提供了主程序进行测试和演示。
摘要由CSDN通过智能技术生成

队列

队列:结构定义

队列是有一篇连续的存储区,其实连续性不重要,而是队列需要保持一个特性:
从队首出元素,从队尾入元素。这一点与顺序表不一样,元素加入的位置不一样
队列:只允许从尾部加入元素,从头部去除元素
顺序表:允许在任意位置加入或者删除元素
队列的属性
size, 记录队列的总大小
head, 指向队首的指针, 包含队首
tail,指向队尾的指针,不包含队尾
先进先出,first in - first out(FIFO)

在这里插入图片描述

队列:出队(Pop)

队列只允许从队首出队
在这里插入图片描述
在这里插入图片描述

队列:入队(Push)

只允许从队尾入队
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

队列:假溢出

最简单的队列是有问题的
假设入队了7, 8, 9三个元素,要入队10号元素
队列已经“满了”,入不了队
实际上只存储了6个元素,但队列的总大小是9
在这里插入图片描述
提出一种新的队列结构:循环队列
当tail超出范围的时候,就让tail指向0号位置
在这里插入图片描述

队列:循环队列

当tail超出范围的时候,就让tail指向0号位置
多了一个属性项:
count: 记录的是队列中当前一共存放了多少个元素
在这里插入图片描述
入队:
在这里插入图片描述
循环队列是为了解决队列的假溢出问题

队列:顺序表的实现

队列结构定义: typedef struct Queue{}Queue;

// 队列
typedef struct Queue{
	Vector * data;
	int size, head, tail, count;  // 循环队列

}Queue;

初始化: int * initQueue(int n) {}, 其中使用了顺序表的初始化函数initVector()

// 初始化
Queue * initQueue(int n)
{
	Queue *q = (Queue *)malloc(sizeof(Queue));
	q->data = initVector(n);    // 用顺序表去初始化
	q->size = n;
	q->head = q->tail = q->count = 0;
	return q;
}

判空操作: int empty(Queue *q) {}

// 判空操作
int empty(Queue *q)
{

	return q->count == 0;
}

查看队首元素: int frontQueue(Queue *q) {}, 其中用到了顺序表的查找操作vectorSeek()函数

// 查看队首元素
int frontQueue(Queue *q)
{
	return vectorSeek(q->data, q->head);    // vectorSeek(data, i), 得到顺序表data的第i个元素
}

压入队列: int push(Queue *q, int val) {}, 其中用到了顺序表的插入操作insertVector()函数

// 压入队列
int push(Queue *q, int val)
{
	if (q->size == q->count) return 0;
	// 把val放到tail所指向的位置
	insertVector(q->data, q->tail, val);
	q->tail += 1;
	// 判断tail是否指向非法位置
	if (q->tail == q->size) q->tail = 0;   // 队满了后让tail重新指向队首
	q->count += 1;

	return 1;
}

弹出队列: int pop(Queue *q) {}

// 弹出队列
int pop(Queue *q)
{
	if (empty(q)) return 0;
	eraseVector(q->data, q->head); // 删掉head指向的元素
	
	q->head += 1;
	//判断head是否越界
	if (q->head == q->size) q->head = 0; // head重新指向队首

	q->count -= 1;
	return 1;
	
}

**销毁:**void clearQueue(Queue *q) {},其中用到了顺序表的销毁函数clearVector()

// 销毁
void clearQueue(Queue *q)
{
	if (q == NULL) return;
	clearVector(q->data);
	free(q);
	return;

}

下面实现顺序表:
顺序表结构定义: typedef struct Vector{}Vector;

// 顺序表
typedef struct Vector{
	int size, count;
	int *data;
} Vector;

初始化顺序表: Vector * initVector(int n) {}

// 初始化
Vector * initVector(int n)
{
	Vector *v = (Vector *)malloc(sizeof(Vector));
	v->data = (int *)malloc(sizeof(int)* n);
	v->size = n;
	v->count = 0;
	return v;
}

查找顺序表: int vectorSeek(Vector *v, int pos) {}

// 查找顺序表
int vectorSeek(Vector *v, int pos)
{
	if (pos < 0 || pos >= v->size) return -1;
	if (v->count > 0) return v->data[pos];
	return 0;
	
}

插入元素: int insertVector(Vector *v, int pos, int val)

// 插入元素
int insertVector(Vector *v, int pos, int val)
{
	if (pos < 0 || pos >= v->size) return 0;
	// 由于pos只能在队尾插入,只要挪一位就行
	v->data[pos] = val;
	v->count += 1;
	return 1;
	
}

删除元素: int eraseVector(Vector *v, int pos) {}


// 删除元素
int eraseVector(Vector *v, int pos)
{
	if (pos < 0 || pos >= v->size) return 0;
	v->data[pos] = 0;
	v->count -= 1;
	return 1;
}

销毁顺序表: void clearVector(Vector *v){}

// 销毁顺序表
void clearVector(Vector *v)
{
	if (v == NULL) return;
	free(v->data);
	free(v);
	return;
}

主程序:

void outputQueue(Queue *q)
{
	printf("Queue:  ");
	for (int i = 0; i < q->count; i++)
	{
		printf("%4d ", vectorSeek(q->data, (q->head + i) % q->size));
	}
	printf("\n\n");
}



int main()
{
	srand(time(0));
    #define MAX_OP 10
	Queue *q = initQueue(MAX_OP);
	for (int i = 0; i < MAX_OP; i++)
	{
		int op = rand() % 5, val = rand() % 100;    // 0, 1: pop;  2, 3, 4: push
		switch (op)
		{
		case 0:
		case 1:
			// 出队列之前查看一下队首元素
			printf("front of queue: %d\n", frontQueue(q));
			pop(q);
			break;
		case 2:
		case 3:
		case 4:
			printf("push %d to queue\n", val);
			push(q, val);
			break;


		}
		outputQueue(q);
	}

	clearQueue(q);

	std::cin.get();
	return 0;

}

测试结果

在这里插入图片描述

队列:链表的实现

队列的结构定义: typedef struct Queue{}Queue;
使用链表实现,不在乎存储空间的大小,也就不存在队列假溢出的问题,实际上也不需要头尾指针了,但是还是可以记录一个count

typedef struct Queue{
	LinkList * l;
	int count;

} Queue;

初始化队列: Queue *initQueue() {}

Queue *initQueue()
{
	Queue *q = (Queue*)malloc(sizeof(Queue));
	q->count = 0;
	q->l = initLinkList();
	return q;

}

判空操作: int empty(Queue *q) {}

// 判空操作
int empty(Queue *q)
{
	return q->count == 0;
}

查看队首元素: int frontQueue(Queue *q) {}

// 查看队首元素
int frontQueue(Queue *q)
{
	if (empty(q)) return 0;
	return frontLinkList(q->l);
}

压入队列: int push(Queue *q, int val) {}
不用判断队列是否满了,直接入队操作

// 压入队列
int push(Queue *q, int val)
{
	// 不用判断队列是否满了,直接入队操作
	insertTail(q->l, val);
	q->count += 1;
	return 1;
}

弹出队列: int pop(Queue *q) {}

// 弹出队列
int pop(Queue *q)
{
    if (empty(q)) return 0;
	eraseHead(q->l);
	q->count -= 1;
	return 1;
}

销毁队列: void clearQueue(Queue *q) {}

void clearQueue(Queue *q)
{
	if (q == NULL) return;
	clearLinkList(q->l);
	free(q);
	return;
}

下面实现链表:
链表节点定义: typedef struct Node{ } Node;

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

链表结构定义: typedef struct LinkList{ } LinkList;

// 链表结构定义
typedef struct LinkList{
	Node head, *tail;  // 有头链表

}LinkList;

表节点的初始化: Node *getNewNode(int val) {}

// 链表节点的初始化
Node *getNewNode(int val)
{
	Node *p = (Node *)malloc(sizeof(Node));
	p->data = val;
	p->next = NULL;
	return p;
}

初始化链表: LinkList * initLinkList() {}

// 初始化链表
LinkList * initLinkList()
{
	LinkList *l = (LinkList *)malloc(sizeof(LinkList));
	l->head.next = NULL;
	l->tail = &(l->head);
	return l;
	
}

判空操作: int emptyList(LinkList *l) {}

// 判空操作
int emptyList(LinkList *l)
{
	return l->head.next == NULL;
}

查找队首元素: int frontLinkList(LinkList *l) {}

// 查找队首元素
int frontLinkList(LinkList *l)
{
	if (emptyList(l)) return 0;

	return l->head.next->data;             //返回
}

链表尾部插入元素: int insertTail(LinkList *l, int val) {}

// 在链表尾部插入元素
int insertTail(LinkList *l, int val)
{
	Node *node = getNewNode(val);  // 封装成链表的节点
	// 节点添加到链表尾部
	l->tail->next = node;
	l->tail = node;

	return 1;
}

删掉链表头节点: int eraseHead(LinkList *l) {}

// 删掉链表头节点
int eraseHead(LinkList *l)
{
	if (emptyList(l)) return 0;
	Node *p = l->head.next;
	l->head.next = p->next;

	// 还需要考虑只有一个节点的情况,此时tail指向这个节点,要将tail指向head
	if (p == l->tail) l->tail = &(l->head);
	free(p);
	return 1;
}

销毁链表: void clearLinkList(LinkList *l) {}


// 销毁链表
void clearLinkList(LinkList *l)
{
	Node *p = l->head.next, *q;
	// 先销毁头节点后面的所有节点
	while (p)
	{
		q = p->next;  // 用q记录p的下一个节点
		free(p);
		p = q;
	}

	free(l);
	return;
}

主程序:

// 输出队列
void outputQueue(Queue *q)
{
	printf("Queue:  ");
	// 用一个p指针来遍历链表
	Node *p = q->l->head.next;
	for (int i = 0; i < q->count; i++)
	{
		printf("%4d", p->data);
		p = p->next;
	}
	printf("\n\n");
}



int main()
{
	srand(time(0));
    #define MAX_OP 10
	Queue *q = initQueue();
	for (int i = 0; i < MAX_OP; i++)
	{
		int op = rand() % 5, val = rand() % 100;    // 0, 1: pop;  2, 3, 4: push
		switch (op)
		{
		case 0:
		case 1:
			// 出队列之前查看一下队首元素
			printf("front of queue: %d\n", frontQueue(q));
			pop(q);
			break;
		case 2:
		case 3:
		case 4:
			printf("push %d to queue\n", val);
			push(q, val);
			break;


		}
		outputQueue(q);
	}

	clearQueue(q);

	std::cin.get();
	return 0;

}

测试结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值