Fork me on GitHub

-------------------siwuxie095

   

   

   

   

   

   

   

   

为什么使用堆

   

   

这里介绍一个全新的数据结构:(Heap)

   

的一个非常典型的应用就是 优先队列(Priority Queue)

   

   

   

相信对于队列这个概念,大家都非常熟悉,如下:

   

1普通队列:先进先出,后进后出

   

在生活中,大家接触的通常是这种普通队列,所谓普通队列,它的关键

其实是由时间的顺序来决定出队的顺序(即 入队顺序决定出队顺序

   

   

2优先队列:出队顺序和入队顺序无关,和优先级相关

   

所谓优先队列,是指对于排队的人、任务、或 等待执行的请求,

它们都是具有一定的优先级的,出队的顺序和入队的顺序无关,

而和优先级相关(即 优先级决定出队顺序

   

   

   

   

   

实际上在生活中,很多情况也是采取优先队列的方式

   

如:在一些医院看病时,就是急诊病人优先,虽然急诊病人到的可能

比普通病人要晚,但由于普通病人的病情没有那么紧急,所以不会先

被接收诊治

   

   

   

优先队列在计算机科学中也被大量的使用,最典型的应用情况就是在操

作系统中执行任务

   

大家都知道操作系统要同时执行多个任务,但实际上操作系统是将 CPU

的执行周期划成了时间片,在每个时间片里只能执行一个任务

   

究竟要执行哪个任务呢?答案就是每个任务都有一个优先级,操作系统

每一次将动态的选择优先级最高的任务进行执行

   

那么要想能够动态选择优先级最高的任务执行,就需要使用优先队列

   

操作系统中的所有任务都进入优先队列,由优先队列来调度操作系统

每次执行哪个任务

   

注意:这里有一个关键词,即 动态

   

不难想象,如果所有任务永远是固定的话(即 就那么几个任务且优先级

没变化),那么完全可以把这些任务就排一次序,然后从优先级最高到

优先级最低依次执行即可

   

   

   

可是当实际使用优先队列时,情况通常都非常复杂,可以用一个模型模拟如下:

   

   

   

在上图中,蓝色大圆圈表示一个任务处理中心,红色小圆圈表示一个个请求

通过任务处理中心来处理所有的请求

   

当任务处理中心选择执行了某一个请求之后,下一步可能不是简单的去执行

其它的请求,因为在执行该请求的同时,有可能又来了很多新的请求,而且

旧的请求的优先级本身也有可能发生改变

   

所以在这种情况下,一次性的将所有请求排序,然后依次执行,是不现实的,

如果使用排序的方式来解决这个问题,很有可能每一次执行完了某一个请求

之后,都要再对剩下的请求进行一次新的排序,但这样做,耗时是巨大的

   

   

   

当然,这种模型不仅仅适用于操作系统,事实上,在现在的生活中处处都存

在着这样的模型,如:在上网时,不同用户会对同一个网络页面产生网络请

求,而服务器端就要依次来回应这些请求,回应的顺序是怎样决定的,通常

就是通过优先队列来决定

   

   

   

在媒体服务、视频播放、音乐播放等领域,全部都需要使用优先队列,而

在人工智能领域也要使用优先队列

   

如:在游戏中,可能需要用智能来决定某一个角色去攻击敌人的顺序,在

这种情况下,通常这个角色会有一个视野,比较专业的说法叫做 AOI,也

就是这个角色所感兴趣的一个范围

   

在这个范围中,可能有若干个敌人,这个角色就需要由电脑控制,在这些

敌人中选择一个进行攻击,这个选择的顺序,通常就是一个优先队列决定

的顺序,如:可能每次选择最强的那个敌人进行攻击,或 血最少的那个敌

人进行攻击

   

显然,优先队列使用的场景主要是处理动态的情况,因为随着游戏的进行,

这个角色会将攻击范围里的敌人逐渐消灭掉,与此同时,还有很多敌人会

逐渐进入这个角色的攻击范围

   

因此,就需要不断向优先队列中入队新的敌人,与此同时,出队根据优先

队列所选择出来的最佳攻击对象(敌人)

   

   

   

   

 

在上面的例子中,我们一直强调优先队列非常适合处理数据是动态的情况,

但实际上在一些静态的问题的求解上,优先队列也是非常有优势的,如下:

   

   

题目:在 1000000 个元素中选出前 100

   

现在有一百万个元素,想在这一百万个元素中选出前一百名,

应该怎么做?

   

把这个问题抽象一下,即:在 N 个元素中选出前 M 个元素

   

   

大家都能想出的一个基本解法是:只要把这 N 个元素进行一次

排序,然后选出它的前 M 个元素即可,这样的解决方案,算法

的复杂度是 N*logN 这个级别的

   

但如果使用了优先队列,就能把算法复杂度降低到 N*logM

个级别

   

   

具体到这个例子中,N 是一百万,M 是一百,这样的一个优化

将使算法快十几倍,当然,如果 N 更大,这个优化的性能优势

将更加明显

   

   

那么优先队列是如何将这样一个问题的时间复杂度从 N*logN

降到 N*logM 呢?这就涉及到优先队列的实现了

   

   

   

   

   

对于队列来说,它的主要操作有两个:一个叫做入队,一个叫做出队

   

优先队列也不例外,从用户的角度来看,优先队列最大的特点就是在

出队时,取出优先级最高的元素

   

   

   

优先队列的实现方式

   

  

入队

出队

普通数组

O(1)

O(n)

顺序数组

O(n)

O(1)

O(lgn)

O(lgn)

   

   

通过数组这样的数据结构就完全可以实现一个优先队列,有两种思路:

   

1)使用普通数组,这样入队就非常简单,使用 O(1) 的时间,直接把一个

元素扔到数组末尾即可,而在出队时,为了取出优先级最高的元素,需要

使用 O(n) 的时间,扫描一遍当前数组,然后拿出优先级最高的元素

   

2)使用顺序数组,也就是要不断的维护这个数组的有序性。这样一来,在

元素入队时,就需要使用 O(n) 的时间,来找到合适的插入位置,而一旦维

护好了数组的有序性,出队就非常简单,使用 O(1) 的时间,直接把队头的

那个优先级最高的元素扔出去即可

   

   

   

但是使用数组进行优先队列的实现是有局限性的,伟大的计算机科学家

发明了一种新的数据结构,可以非常好的平衡入队和出队这两个操作的

时间复杂度

   

这种数据结构 ,使用 可以将入队和出队的时间效率都变成 lgn

这个级别的

   

   

不难发现,对于 lgn 这个级别的时间复杂度来说,它在入队这个时间上

慢于普通数组,在出队这个时间上也慢于顺序数组

   

但平均来讲,使用堆这种数据结构维持一个优先队列,来完成一个系统

任务,它所用的时间要大大低于使用数组来进行实现

   

   

最极端的情况,即 总共有 N 个请求:

   

1)使用普通数组 顺序数组,时间复杂度:O(n^2)

   

2)使用堆,时间复杂度:O(n*lgn)

   

   

n^2n*lgn 这两个级别的算法,它们之间的差异是巨大的

   

   

   

   

   

   

   

   

   

   

【made by siwuxie095】

posted on 2017-06-05 19:44  siwuxie095  阅读(181)  评论(0编辑  收藏  举报

导航