利用划分树求解整数区间内第K大的值

  如何快速求出(在log2n的时间复杂度内)整数区间[x,y]中第k大的值(x<=k<=y)?

  其实我刚开始想的是用快排来查找,但是其实这样是不行的,因为会破坏原序列,就算另外一个数组来存储,多次询问的条件下,同样满足不了。

  划分树主要是针对上述问题而设计的。 划分树是一种基于线段树的数据结构,其基本思想就是将待查找的区间分为两个子区间:不大于数列中间 值的元素被分配到左儿子的子区间,简称左子区间;大于数列中间值的元素被分配到右儿子的子区间,简称右子区间。

  显然,左子区间的数小于右子区间的数。建树 的时候要注意,对于被分到同一子树的元素,元素间的相对位置不能改变。例如构建整数序列[1 5 2 3 6 4 7 3 0 0]的划分树:

  整数序列经排序后得到[0 0 1 2 3 3 4 5 6 7],中间值是3。划分出下一层的左子区间[1 2 3 0 0],中间值为1;下一层的由子区间[5 6 4 7 3],中间值为5;以中间值为界,划分出当前层每个区间的下层左右子区间······以此类推,直至划分出所有子区间含单个整数为止。

  这里可能有人就会提问了,为什么两个子区间是这样的呢??  其实我刚开始也这么想,下面给出解释:

1.如果有和中间值相等的元素,插入进去的前提是  小于中间值的元素不够填满一个区间,差多少就补多少和中间值相等的元素(很好理解,小于肯定优先嘛)。

2.相对位置不能改变。

下面给出一个图,还是以[1 5 2 3 6 4 7 3 0 0]为例:

如图可见,划分树是一颗完全二叉树,树叶为单元素区间。若数列含有n个整数,则对应的划分树有[log 2 n]+1层。

扫描二维码关注公众号,回复: 4104503 查看本文章

查找的时候通过记录进入左子树的数的个数,确定下一个查找区间,直至查找范围缩小到单元素为止。  此时,区间内的第K大值就找到了。

  整个算法分两步:

1.建树-------离线构建整个查询区间的划分树

2.查找-------在划分树中查找某个子区间中的第K大值。

  在查询之前,我们先离线构建整个查询区间的划分树。  建树过程比较简单,对于区间[l,r],首先通过对原数列排序找到这个区间的中间值的位置mid(mid=[(l+r)/2]),不大于中间值的数划入左子树[l,mid], 大于中间值的数划入右子树[mid+1,r] 。同时,对于第i 个数,记录在[l,i] 区间内有多少整数被划入左子树。  最后继续对它的左子树[l,mid]  和右子树[mid+1,r]  递归建树,直至划分出最后一层的叶节点为止,显然,建树过程是自上而下的。

  具体实现办法:

  先将读入的数据排序成sorted[]   (从小到大排序) ,取区间[l,r]的中间值sorted[mid],然后遍历[l,r]区间,依次将每个整数划分到左子区间或者右子区间中去。  注意,被分到每个子树的整数是相对有序的。  注意,这里要记录区间能插入多少个和中间值相等的元素。    另外,在这个过程中,要记录一个类似前缀和的东西,即  l  到  i  这个区间内有多少整数被划分到左子树。设:

tree [p][i] ------第p层第i个位置的整数值,初始序列为tree[0][]。

sorted[]---------排序序列,即存储tree[0][]排序后的结果

toleftp[][]--------toleft[p][i],表示第p层前i

猜你喜欢

转载自www.cnblogs.com/caijiaming/p/9972277.html