最小堆第k小的元素与给定x的大小关系(编程题)

来源:互联网 发布:盛科网络 营收 编辑:程序博客网 时间:2024/06/04 20:14
1.最小堆第k小的元素与给定x的大小关系:kth>=x是否成立


对一个数组实现的n个元素的最小堆和给定的实数x,判断堆中第k小元素是否大于等于x?(0<k<n)要求算法最坏时间复杂度为O(k)。(提示:没必要找出第k小元素的具体值)


解答:


  最直接的两种解法:从堆中选取最小元素k次,时间复杂度O(klogn);检查堆中前k层所有元素,时间复杂度O(n,2^k)。这两种解法都不符合时间复杂度的要求。


  实际上是从根开始遍历所有值小于x的结点:



//第一次调用时,i=1,count=kint heap_compare(heap *h,int i,int count,int x){    if((count<=0)||(i>h->size))        return count;    if(h->h[i]<x) {        count = heap_compare(h,LEFT(i),count-1,x);        count = heap_compare(h,RIGHT(i),count,x);    }    return count;}



  如果根大于等于x,那么其余所有元素都不可能小于x,第k小的元素必然大于等于x。这时返回值为k>0。(若k=1,那么第1小的元素就是根,结果不变)


  如果根小于x,需要对根的两个后继进行遍历。如果你没有看明白这段程序的含义,下面以几个例子来帮助理解这个函数在递归调用时发生了什么。假定k=3,x=3,即判断第3小的元素是否大于等于3。简单起见,结点以下标代替,根为1(与原书一致)。


  实例1:第k小的元素大于等于x。






  实例2:第k小的元素大于等于x。原图交换顺序。






  实例3:第k小的元素小于x。






 


  从上面三个例子可以看出:


  每找到一个小于x的元素都会消耗一个count,如果消耗完了k个,表示至少前k个都小于x。那么第k个必然小于x,后面不必再进行查找,返回值为0。


  反之,如果遍历所有小于k的元素时未消耗完k个count,表示小于x的元素不足k个,那么第k个必然大于等于x,返回值为正值。


  原代码中令人迷惑的地方在于count的使用,理清上面的两个关系就好理解了。虽然看上去这个函数对于一个结点的两个后继的处理地位不同,但实际上没有影响。


  至于时间复杂度,由于我们只遍历了所有小于x的结点以及它们的后继,而且遍历时不超过k个(用完k个就return 0),因此大约是3k个结点,时间复杂度只有O(k),符合要求。
0 0