链队实现优先队列

首先,什么是优先队列?

普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征.

简单来说,每次出队,优先级最高的先出,如果优先级相同,按普通队列方式(先进先出)出队.

结构体定义

提示:

  1. 应有存放优先级的变量(本文中,数字越大,优先级越高)
  2. 应有数据域
  3. 应有指针域
    在这里插入图片描述
typedef struct FQNode{
	int priority;//应有存放优先级的变量
	int data;//应有数据域
	FQNode *next;//应有指针域
}FQNode;

typedef struct {
	FQNode *front;
	FQNode *rear;
}FQueue;

初始化

提示:

  1. 指向队头和队尾的指针是否置空?
bool initFQueue(FQueue* &q) {
	q = new FQueue;
	q->front = q->rear = NULL;//指向队头和队尾的指针是否置空?
	return true;
}

判断是否队空

无提示

bool ifEmpty(FQueue *q) {
	if (!q->front && !q->rear) {
		return true;
	}
	return false;
}

入队

提示:

  1. 插入的元素有可能是队列的第一个元素!
void insertFQueue(FQueue *q,int data,int priority) {
	if (!q) {
		return;
	}

	FQNode *e = new FQNode;
	e->data = data;
	e->priority = priority;
	e->next = NULL;

	if (ifEmpty(q)) {//插入的元素有可能是队列的第一个元素!
		q->front = q->rear = e;
		return;
	}

	q->rear->next = e;
	q->rear = e;
}

出队(仅用两个二级指针)

提示:

  1. 遍历队列找到优先级最高的结点
  2. 要能获得优先结点的前一个结点以便出队和释放内存
  3. 出队元素可能是队头元素或队尾元素
void deleteFQueue(FQueue *q) {
	if (ifEmpty(q)) {
		cout << "空队列!" << endl;
		return;
	}

	FQNode **max,**e;
	max = &(q->front);//指向队头
	e = &(q->front->next);//指向第一个结点
	while (*e) {
		if ((*e)->priority>(*max)->priority) {
			max = e;//指向优先结点的前一个节点
		}
		//记住下面这行代码
		e = &((*e)->next);//继续搜索
	}
	if (*max == q->rear) {//出队元素是队尾元素
		q->rear = (FQNode*)max;
	}
	//记住下面这行代码
	*max = (*max)->next;//出队(已包含出队元素是第一个元素的情况)
	delete *e;
}

这里有点难,我会做详细讲解.

首先,为什么要用二级指针?二级指针能同时关联两个结点,在找到优先结点后能方便的回溯到上一个结点.
在这里插入图片描述
在网上查二级指针,你得到的大概是这样的图(如下图左)
在这里插入图片描述
然而实际上的二级指针是这样的(如上图右).
其实不管是二级指针还是三级指针还是一百级指针,都可以用上图右的方式表示,这也是我们遇到指针"套娃"时最好的理解方式.

在上面的代码实现里,我让大家记住了两行代码:

FQNode **max,**e;//定义

e = &((*e)->next);//继续搜索
*max = (*max)->next;//出队

可以看出,我在写这两行代码的注释时,一个写的是"继续搜索",一个写的是"出队",意思是两者功能完全不同.可是,&和*是两个相反的运算,且maxe都属于同一类型的二级指针,按理来说两者操作是等价的啊?
我在刚开始的时候也认为它们是等价的,后面经过不断调试,才发现了问题所在.我们先把它统一一下,再做讲解:

FQNode **p;//定义

p = &((*p)->next);//继续搜索
*p= (*p)->next;//出队

从链表的删除来看

在上面讲为什么要用二级指针的时候,我画过一幅图:
在这里插入图片描述
其实p就指向(图中)第一个结点的next域,也就是说,*p就是(图中)第一个结点的next域.现在如果我们有指向第一个结点的指针,名字叫"one",以及指向第二个结点的指针,名字叫"two",我们可以写出如下的删除结点代码:

one->next = one->next->next;
delete two;

在这里插入图片描述
这里one->next和上图的*p是一样的,我们做代换得以下代码:

*p = (*p)->next;
//delete...

出队我们解释清楚了,接下来解释搜索.

FQNode **p;//定义

p = &((*p)->next);//继续搜索
*p= (*p)->next;//出队

此处省略了数据域,只保留指针域.实际上是很简单的,如果你理解了我之前画的实际上的二级指针
在这里插入图片描述

从内存上看

我猜,看完上面后只有一部分读者懂了,所以我从另一个角度再讲一次.

FQNode **p;//定义

p = &((*p)->next);//继续搜索
*p= (*p)->next;//出队

搜索前:
在这里插入图片描述
搜索后:
在这里插入图片描述
出队前:

在这里插入图片描述
出队后:
在这里插入图片描述
总结:在这里,*p实际上就是某一个结点的next域的别名(这与C++引用有异曲同工之妙),更改*p的值也就更改了next的值(即它的指向),也就导致了表的重新链接.而对p的值的更改只会导致指向的变量不同,不会影响到表.

打印队列元素

无提示,使用自己喜欢的风格

void printFQueue(FQueue *q) {
	FQNode *e=q->front;
	while (e) {
		cout << e->data << "\t" << e->priority << endl;
		e = e->next;
	}
}

全部代码及测试代码

#include <iostream>

using namespace std;

typedef struct FQNode{
	int priority;
	int data;
	FQNode *next;
}FQNode;

typedef struct {
	FQNode *front;
	FQNode *rear;
}FQueue;

bool initFQueue(FQueue* &q) {
	q = new FQueue;
	q->front = q->rear = NULL;
	return true;
}

bool ifEmpty(FQueue *q) {
	if (!q->front && !q->rear) {
		return true;
	}
	return false;
}

void insertFQueue(FQueue *q,int data,int priority) {
	if (!q) {
		return;
	}

	FQNode *e = new FQNode;
	e->data = data;
	e->priority = priority;
	e->next = NULL;

	if (ifEmpty(q)) {
		q->front = q->rear = e;
		return;
	}

	q->rear->next = e;
	q->rear = e;
}

void deleteFQueue(FQueue *q) {
	if (ifEmpty(q)) {
		cout << "空队列!" << endl;
		return;
	}

	FQNode **max,**e;
	max = &(q->front);//指向队头
	e = &(q->front->next);//指向第一个结点
	while (*e) {
		if ((*e)->priority>(*max)->priority) {
			max = e;//指向优先节点的前一个结点
		}
		e = &((*e)->next);//继续搜索
	}
	if (*max == q->rear) {//出队元素是队尾元素
		q->rear = (FQNode*)max;
	}

	*max = (*max)->next;//出队(已包含出队元素是第一个元素的情况)
	delete *e;
}

void printFQueue(FQueue *q) {
	FQNode *e=q->front;
	while (e) {
		cout << e->data << "\t" << e->priority << endl;
		e = e->next;
	}
}

int main(void) {
	int n, data, priority;
	FQueue *q;
	initFQueue(q);

	cout << "请输入插入元素的个数:";
	cin >> n;

	while (n--) {
		cout << "请输入插入元素值:";
		cin >> data;
		cout << "优先级:";
		cin >> priority;

		insertFQueue(q, data, priority);
	}
	printFQueue(q);
	cout << endl;
	deleteFQueue(q);
	printFQueue(q);

	system("pause");
	return 0;
}

在这里插入图片描述

最后

事实上,优先队列通常采用来实现.在之后讲堆的时候我们会重提优先队列.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值