划分树读书笔记

来源:互联网 发布:查询数据库中重复记录 编辑:程序博客网 时间:2024/04/27 16:51

原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。

由于各种原因,可能存在诸多不足,欢迎斧正!     

     之前一直认为线段树可以解决划分树的所有问题。直到愚昧的我遇到poj2104,用线段树怎么也无从下手,于是下定决心学学划分树。以我的理解,划分树的主要功能是求区间的第k大元素,可能有的题要灵活做些修改。

 

背景

给定一个无序的数据序列,然后给定若干小区间,查询区间第k小的元素。单纯的暴力方法一般无法解决问题;一般的线段树也不能轻松解决问题。因此,此处有必要引进划分树这一数据结构。下面简单介绍一下我关于划分树的读书笔记。

 

划分树的基本思想  

        划分树的基本思想就是对于某个区间,把它划分成左右两个子区间,左边区间的元素小于等于右边区间的元素(不必排序且保证稳定性),在某一深度deptoleft[dep][i]记录[1,i]分到左边的元素个数。查找时通过记录进入区间左子树的元素个数,确定下一个查找区间(此处有两重含义,下面做介绍),最后范围缩小到区间长度为1此时已经找到该元素。

 

 

划分树有两个基本操作

1.构建

        建树的过程比较简单,对于区间[l,r],首先通过对原数组的排序找到这个区间的中间元素a[mid],小于a[mid]元素以及等于a[mid]且靠左的元素(包括a[mid])划入他的左子树[l,mid];其余的划入右子树[mid+1,r],有一约束就是保证正好存满左右子树,不能使哪边的存储元素个数超过它所能存储的上限。同时,对于区间[l,r]第i元素a[i],记录在当前深度dep下[1,i]区间内有多少元素被划入左子树(我这里记为 toleft[dep][i] 这里是为了后面的查询搓澡的方便)。然后,对它的左子树区间[l,mid]和右子树区间[mid+1r]递归的继续建树

        建树的时候要注意对于被分到同一子树的元素,元素间的相对位置不能改变,这就类似快速排序中的稳定性—相同元素间的相对顺序不变。

        此过程时间复杂度为O(N*log(N))

2.查询

        查找时主要问题是确定将要查找的区间。假设在深度为dep,大区间[L,R]查找小区间[l,r]中的第k小元素。

       我们的想法是先确定小区间[l,r]中的第k小元素在大区间[L,R]的左右哪个子树中,然后修改对应的dep,[L,R],[l,r],k……直到找到l=r为止,a[l]即为所求。以上所做的都得根据

toleft[][]的值。

       具体做法为toleft[dep][l-1]记录着当前深度depl左边进入左子树的元素个数,toleft[dep][r]记录着当前深度下r左边进入左子树的元素个数,下面就得讨论,记lsum=

toleft[dep][r]-toleft[dep][l-1]lsum为区间[l,r]内进入左子树的元素个数。

   

       若lsum>=k,说明小区间[l,r]中的第k小元素在大区间[L,R]的左子树上。此时到大区间[L,R]的左子树上继续查找,但小区间[l,r]应作相应的修改,newl=L+ toleft[dep][l-1]-

toleft[dep][L-1]r=newl+lsum-1

       若lsum<k,说明小区间[l,r]中的第k小元素在大区间[L,R]的右子树上。此时到大区间[L,R]的右子树上继续查找,但小区间[l,r]应作相应的修改,newr=r+toleft[dep][R]-

toleft[dep][r],newl=newr-(r-l+1-lsum)+1=newr-(r-l-lsum)k=k-lsum

       若某时刻l=r,此时退出,a[l]即为所求。

       此过程时间复杂度为O(N)

 

 

小结

         关于划分树,构建、查询操作都是比较基础的,大致跟线段树差不多,是线段树的某一特殊形式,采用二分思想,结点维护的是当前深度下从1i这段区间内进入左边的元素个数(依我愚见划分树大都这样)。在实现的时候应该注意一些小细节,比如修改小区间[l,r]、k等,否则就会功败垂成。

   

以上就是我所理解的划分树的基本思想及两个基本操作。鉴于水平有限,错误难免,希望和大家一起学习交流,请斧正!