来源:互联网 发布:淘宝无线端首页尺寸 编辑:程序博客网 时间:2024/05/16 05:28

堆和优先队列

普通的队列是先进先出,而优先队列指的是出队顺序和入队顺序无关,和优先级相关。优先队列最广泛的应用在于操作系统,操作系统将时间周期划分为时间片,而每个时间片中只能执行一个任务,操作系统每次将动态地执行优先级最高的任务。这里有一个关键词:动态,如果队列中的任务量一定,那么我们完全可以按照优先级给它排一次序,依次执行就好了。而实际情况比这要复杂,新旧任务随着任务的执行优先级可能改变,所以我们需要一个动态的优先级队列解决方案。
优先队列应用的场景很多,举一个栗子,比如说要在1000000个数在中选出前100名,我们首先的想法就是使用快排,复杂度为nlog(n)。但是如果我们使用优先队列,我们可以将算法的复杂度降低到nlog(m)。
优先队列的主要操作有两个,入队和原来是一样的,而出队要取出优先级最高的元素。
那么我们会想到这样几个方案,第一个是使用数组,每次在末尾插入,入队的复杂度是O(1),出队由于要遍历,复杂度是O(n)。第二个方案是使用顺序数组,维护数组的有序性,那么插入的时候由于要遍历数组,所以复杂度为O(n),出队的时候只要直接取第一个元素,复杂度是O(1)。第三个方案是使用堆,入队和出队的复杂度都是log(n)。最极端的情况,当我们有N个元素,那么第一种和第二种方案,出队和入队情况总的是n(n+1),第三种情况就是2nlog(n)。n平方和nlog(n)的差异是巨大的。

具体实现

说到堆的插入和删除都是log(n),我们就会想到堆是一个树形结构。最经典的堆就是一个二叉堆binary heap。binary heap分为两种,最大堆和最小堆。最大堆有这样两种条件,一个是堆中每个节点的值不大于父节点的值(可以大于其他非父上层节点的值),二是堆总是一个完全二叉树(除了最后一层,每层都满,最后一层节点在左侧)。最小堆则相反,堆中每个节点的值不小于父节点的值
那么我们使用一个数组来存储优先队列,相当于我们从上到下,从左到右对这个二叉树进行编号(从1开始),这样每一个二叉数的左节点就是父节点编号的2倍(完全二叉树的作用体现了),右节点是父节点的2n+1。所以我们要找到父节点和子节点非常容易,我们使用一个数组,索引0不存放数据,从1开始按照编号存储堆中的内容

shift up

假设我们原来堆中有10个元素,那么接下来我们要添加一个元素,首先肯定是放在数组的第11索引上。那么原来堆上所有节点都满足堆的定义,新加进来的元素如果不满足(即比父节点的元素大),只需要和父节点交换位置,交换位置之后如果还有问题,再和向上一级的父节点交换即可

   function insert(){           //capicity为数组容量           assert(count + 1 <= capicity)       data[count + 1] = item;       count++;       shiftup(count);   }   function shiftup(k){       //注意索引k的越界问题,k=1为根节点,不需要比较       while(k > 1 && data[k] > data[k/2]){          swap(data[k], data[k/2]);       }       k = k / 2;   }

shift down

我们取出一个元素只能从根元素取,也就是索引为1的元素,这时少了一个元素怎么办,我们取出最后一个元素,放到根元素上,然后count–,之后的操作都以count为界,所以我们原本放在11位置的数可以不变。现在根元素可能不满足比子节点大的条件,所以我们需要和两个子节点进行比较,也就是和2n和2n + 1比较,如果比两者较大的大,就和较大的交换。为什么要和较大的换呢,因为较大的上位之后,正好比两个子节点大。

   //堆数组的数量大于0   assert(count > 0);   swap(data[1], data[count]);   count--;   function shiftdown(k){       //堆中至少有一个左孩子       while(2 * k <= count){          var j = 2 * k;          if(j + 1 <= count && data[j + 1] > data[j]){              j += 1;          }          //如果子孩子中较大还是比父节点大,就跳出结束          if(data[j] <= data[k])               break;          //如果想要优化的话,可以用一个变量来保存最终位置,只swap一次          swap(data[k], data[j]);          k = j;       }   }
0 0
原创粉丝点击