数据结构队列之企业级应用--优先队列

1.了解队列的存储方式

1.(1)数组形式存储

2. (2)链式存储(链表形式)

(1)数组形式的存储方式为连续存储(顺序存储),也就是说该队列的地址是连续的。我们看下图:

该存储方式的头指针与尾指针在没有元素时同时指向下标为-1的位置(注意,只是表现形式).当存储元素之后,队头指针指向第一个元素,队尾指针指向最后一个元素的下一位.如下图:

Ps:纯手工画的不好.hh

唉?你说那么多是不是优先队列就是这么做的呢?别急,别急,我只是给小白科普一下队列的存储方式,不至于那么懵逼。顺序存储的队列一般用作少量元素的处理。(有一种顺序存储队列的特殊情况,循环队列。这个在一些应用场景下比单链表存储方式更高效!!这篇文章效果好下波就更新循环队列)。否则效率虽然不低,但是也不高啊。顺序存储留一个印象给你们,接下里我们讲第二个存储方式。

(2)链式存储队列(链表形式),这个就是我们这次要说的重头戏,一般高效的数据结构都是采用链式储存的(有没有没学好链表的同学?),这次我们的优先队列也不例外。除此之外我们的HTTP反向代理服务器Nginx里面的文件缓存处理方式也是使用链式储存结构的(但是稍稍和我今天说的不一样,那个使用了Linux内核链表常用形式)。好了好了,扯太多了。言归正传今天的高逼格应用-优先队列。我们先让小萌新们看看图:

这就是链式存储的队列(有没有懵逼呢,哈哈)。和顺序存储的方式相差不大,队头指针一如既往指向队头,但是队尾指针不是指向下一位了啊。直接指向最后一个结点(其实就是保存了最后一个结点的地址,我怕你们基础太差听不懂还是解释一下吧)。这里还要补充两个规则。如果一开始没有结点。那么队头指针和队尾指针同时指向NULL(xxx->front=NULL;).

另一个我相信大家也听说过,不过没听说过也没关系。就是元素只能从队尾进队头出(就像排队一样,不然为什么叫队列呢?)。好了,到这里我相信大家应该对队列一知半解了。好,我们开始发车。(我估计有人已经等不及了,当然了。肯定还有很多新手不是很了解,但是没关系,你们只需要看我下面的操作就行了。以后会有机会详细讲解其他数据结构)。

2. 优先队列和一般链式存储队列的差别

为了防止大家懵逼,我们一步一步来。先看看普通链式存储队列的形式。废话不多说,直接上代码:

很明显,这里面有我们刚才说的数据和结点。另一个结构体就是我们的队头指针和队尾指针了,这就是普通链式存储队列的结构(这个时候就有同学要问了,你这个怎么还有个length是什么啊?哈哈,我之前忘记说了,队列是有长度的,很多时候就是这样,当你深入理解一个概念的时候是不需要死记硬背的。需要的时候就会想起,这个长度补充一下,就是当前队列长度,用来判断是否满队,是否队空)。

那我们来看看心心念念的优先队列结构是怎么样的呢?我们先来看一张内存图:

我相信大家已经看出来了,优先队列和普通链式存储的队列区别在于多了一个优先级(这个要怎么解释呢?我想了一下,拿现实的东西来类比一下吧。你去银行排了很久的队,银行不允许插队,从队头办业务,队尾排队(队列特性),但是他们的VIP可以优先办理,也就是说,队头和队尾不受影响,窗口优先处理VIP的业务)。说到这里大家都应该有一个感性的认识了。那我们接着往下看看优先队列的数据结构用代码写是什么样的呢。来,请看下图:

我们可以看到,这里面多了一个优先级priority。除此之外没有任何变化(优先级由程序员本身决定,我这里为了贴合实际,采用数字越大优先级越高),停停停,你的MAX_SIZES是干什么的?哈哈,为了照顾一下小萌新,我略微说一下,这个就是队列的最大长度,结点数不能超过这个数(类比一些现实生活特殊情况的排队)。

那么结构上面的差别我们讲完了,算法上面的差别有没有呢?那肯定有啊,不然我写这个博客干什么(hh),但是差别只存在一个地方,我想已经有人猜到了,那就是出队的情况。优先级越高的,出队越快。别急,我给大家讲一个例子,这个例子说明了除了资本家善用优先队列还有什么情况可以使用,请看下图:

现在已经有了一定的理解了吧,这就是为什么出队的算法实现会不同的原因。好了,普通链式队列和优先队列的差别已经介绍完了,接下来是他的算法实现。

3.优先队列的算法实现

好了,终于到我们激动人心的环节了。算法实现!我相信肯定有人等不及了,(我知道你很急,但是你先别急)。接下来的内容可能有点难度,系好安全带,我要发车了。首先,为了节省大家的时间,我默认大家会普通链式队列了(可能不会,但是没关系)。好了,接下来看我操作。先给大家看看我们要做的流程,如图:

1.初始化队列(上图是不是看的清楚些?):

首先,我们先用一个二级指针初始化LinkQNode类型结构体变量(队头指针和队尾指针),这里会有同学问了,你怎么用二级指针?是不是闲着没事找事,我用一级指针行不行?或许你会有这样的疑问,但是我先告诉你答案:不行(至于为什么,这个又涉及到指针的中级应用,我的博客只写了初级应用,可惜了),你可以去试一下,当然了,改变我函数内部的写法或许可以。好了,言归正传。我们给这个指针申请一块内存(我知道肯定有人不懂,不要急。以后我可能会讲解,这个的东西讲三天三夜也很难讲完啊)。然后就是老鸟程序员经常使用的防御性编程(如果分配内存失败则返回假,具体处理结果由主函数内部实现)。然后我们将队头指针和队尾指针都指向NULL。队列长队置位0。执行完以后返回真。

2.判断队列是否为满(和普通链式存储队列同理):

这个就很简单了。直接对比当前队列长度和最大队列长度就行了。没到就返回真,否则返回假。那么这个有什么用呢?别急,等等插入元素的时候你就知道了。

3.插入元素(老调重弹):

怎么插入呢(~v~)? 首先我们先判断队列是否为满,你看,这不就显示出了上一个函数的作用了吗(这里要提一下,在实际开发中,常用功能最好写成一个函数)(还有很重要的一点,我上面这个函数的代码风格有问题,同学们不要模仿哦。我是为了截图截完才改成这样的。心好累)队列满了返回假,为真继续执行,先申请一块结点的内存。注意,这里面有一个特殊情况。就是队列为空时,队头指针和队尾指针都是空的,要特殊处理。如果长队为0,则代表队列为空。将我们的数据装载进入结点。(由于我懒得设置优先级了,直接拿数据当优先级来用,达到演示目的)。然后将队头指针指向该结点,对尾指针也指向该结点,将该结点的指针域置空,最后队列长度++。然后返回真(其实这里面我可以做的更好的,但是今天累了,不打算优化了。)如果不是空队列,前面的步骤和上面差不多,但是尾结点的指针域保存该结点的地址,然后队尾指针指向该结点,队列长度++,然后返回真。(我眼睛有点累了)。这样我们入队就完成了。

4.打印队列元素(都一样都一样):

 首先判断队列是否为空(老防御编程了),然后定义一个临时指针指向该队头内容(第一个结点的地址),然后定义一个整形i=0,用来逐一遍历队列。进入循环,当p不为空的时候就一直循环(这是不可能的)。打印完以后就把下一个结点的地址赋值给p。达到遍历效果。

5.出队(重头戏来了,我好累了)

由于代码较长我就不上图了,直接上代码。

bool deQNode(LinkQNode* queue)

{

if (!queue)

{

cout << "队列非法" << endl;

return false;

}

if (queue->length < 1)

{

return false;

}

QNode** twolast = NULL;

QNode* last = NULL;

QNode* tmp = NULL;

QNode* preave = NULL;

twolast = &(queue->foront);

last = queue->foront;

tmp = last->next;

while (tmp)

{

preave = last;

if ((*twolast)->priority < tmp->priority)

{

twolast = &(last->next);

}

last = tmp;

tmp = tmp->next;

}

if (queue->length == 1)

{

delete queue->foront;

queue->foront = NULL;

queue->rear = NULL;

queue->length--;

return true;

}

else if ((*twolast) == queue->rear)

{

delete queue->rear;

queue->rear = preave;

queue->rear->next = NULL;

queue->length--;

return true;

}

*twolast = (*twolast)->next;

delete tmp;

queue->length--;

return true;

}

来了,压轴戏。这里面出队的核心思想就是比较优先级,如果大家优先级一样那就是普通链式存储队列。但是大部分情况总是有VIP的。来,一开始就是防御性编程。这个我就不说了。然后我们使用二级指针来处理这个队列。你怎么老是用二级指针?停停停,这里面如果不用二级指针的话就得用两个一级指针。你也不想用两个指针吧。为什么呢?这就是核心了,使用二级指针其实是保存了当前优先级最高结点的上一个结点的指针域的地址。什么?你没听懂?我也猜得到你没听懂。但是呢,我不打算深入讲解这部分,因为有点麻烦。我简单说说思想,二级指针在删除优先级最高结点的时候,本身是不受影响的。所以可以少用一个指针。这个应该能听懂一点吧。还有,出队的时候有两个特殊情况,什么情况呢?这两个特殊情况就是,万一删掉最后一个结点(只有一个结点的那种)。就要特殊处理,如果删掉的是最后一个呢?那也得特殊处理。具体看我的代码吧。毕竟码字确实不容易啊(学习太多东西都是连续的了,一个队列里面的知识不仅仅是队列)。好了,我们来看看运行结果如何,看下图:

 我们可以看到,成功删除掉了优先级最高的元素。实现成功!

其实里面有很多内容都需要长久的学习,才能深入理解。我希望大家都能学有所得,写的不好多多关照。

我是程序员玉无涯,关注我。带你变成高级程序员。

想深入学习的可以私信我哦

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值