划分树

原创 2018年04月16日 21:05:21

划分树是一种基于线段树的数据结构。主要用于快速求出(在log(n)的时间复杂度内)序列区间的第k大或第k小值 。

树的结构:

int sorted[N];    // 对原来集合中的元素排序后的值。   

struct tree{    int val[N];       // val 记录第 k 层当前位置的元素的值   

 int num[N];      // num 记录元素所在区间的当前位置之前进入左孩子的个数     

    }t[20];  


计算3到9区间的第二小值:

首先[3,9]区间之间有3个在左子树比k值大,所以跳到左子树中(大区间),小区间就变成[2,4],然后递归找到区间只剩一个值为止。

怎么找到的:

首先根据num数组我能知道[3,9]之间有多少个数进入了左子树。这样根据这个数来判断我们要的值是在左还是右。这样大区间就确定了。

然后,小区间的2是根据[1,3)之间有多少个进入左子树(值为1)的加上大区间的左端点(值为1);

小区间的右端点(4)是让小区间左端点(2)+[3,9]之间有多少个数进入左子树(有3个)-1;

现在我们判断如果是右子树的话。

比如是找第4小值:

首先[3,9]之间有3个在左子树比k小,所以跳到右子树。小区间是[7,10]

小区间的7是mid(5)(中间的一个数)+1+小区间的3-大区间的1(这个是计算区间之间右多少个数)-1([1,3)之间进入左子树个数);

小区间的10是7+小区间的右端点9-小区间的左端点3+1(这个计算出小区间有多少数)-3(左子树进入了多少个小区间内的数);最后将k-3(左子树进入了多少个小区间内的数);

  1. #include <cstdio>  
  2. #include <iostream>  
  3. #include <cstring>  
  4. #include <queue>  
  5. #include <cmath>  
  6. #include <algorithm>  
  7. using namespace std;  
  8. const int N=100005;  
  9. int sorted[N];    // 对原来集合中的元素排序后的值。   
  10. int arr[N];  
  11. double Sum=0;  
  12. struct tree{  
  13.     int val[N];       // val 记录第 k 层当前位置的元素的值   
  14.     int num[N];      // num 记录元素所在区间的当前位置之前进入左孩子的个数   
  15. }t[20];  
  16. //划分树构建   
  17. void build(int l,int r,int p){  
  18.     if(l==r) return ;  
  19.     /* same 用来标记和中间值 sorted[mid] 相等的,且分到左孩子的数的个数。  
  20.     初始时,假定当前区间[lft,rht]有 mid-lft+1 个和 sorted[mid] 相等。     
  21.     先踢掉比中间值小的,剩下的就是要插入到左边的 
  22.     例如 1 3 3 3 3 5 5 7   same=4,sorted[mid]=3,分到左孩子且值为3的个数为3  
  23.     */     
  24.     int mid=(l+r)>>1,same=mid-l+1,lp=l,rp=mid+1;  
  25.     for(int i=l;i<=r;i++){  
  26.         if(t[p].val[i]<sorted[mid]) same--;  
  27.     }  
  28.     for(int i=l;i<=r;i++){  
  29.         if(i==l){                                  // 初始一个子树。   
  30.             t[p].num[i]=t[p].sum[i]=0;  
  31.         }  
  32.         else{                                     // 初始区间下一个节点。   
  33.             t[p].num[i]=t[p].num[i-1];  
  34.         }  
  35.         /* 如果大于,肯定进入右孩子,否则,判断是否还有相等的应该进入左孩子的,   
  36.          没有,就直接进入右孩子,否则进入左孩子,同时更新节点的 num 域*/     
  37.         if(t[p].val[i]<sorted[mid]){  
  38.             t[p].num[i]++;  
  39.             t[p+1].val[lp++]=t[p].val[i];  
  40.         }  
  41.         else if(t[p].val[i]>sorted[mid]){  
  42.             t[p+1].val[rp++]=t[p].val[i];  
  43.         }  
  44.         else{  
  45.             if(same){  
  46.                 same--;  
  47.                 t[p].num[i]++;  
  48.                 t[p+1].val[lp++]=t[p].val[i];  
  49.             }  
  50.             else  
  51.                 t[p+1].val[rp++]=t[p].val[i];  
  52.         }  
  53.     }  
  54.     build(l,mid,p+1);  
  55.     build(mid+1,r,p+1);  
  56. }   
  57. //划分树查找   
  58. /* 在区间[a, b]上查找第 k 小元素。*/   
  59. int query(int a,int b,int l,int r,int p,int k){  
  60.     int s;                      //[l, a)内将被划分到左子树的元素数目  
  61.     int ss;                     //[a, b]内将被划分到左子树的元素数目    
  62.     int mid=(l+r)>>1;  
  63.     if(l==r) return t[p].val[a];  
  64.     //区间端点点重合的情况,要单独考虑 !!!!!!  
  65.     if(a==l){  
  66.         s=0;  
  67.         ss=t[p].num[b];  
  68.     }  
  69.     else{  
  70.         s=t[p].num[a-1];  
  71.         ss=t[p].num[b]-s;        
  72.     }  
  73.      // 进入左孩子,同时更新区间端点值   
  74.     if(ss>=k){  
  75.         int la=l+s;  
  76.         int lb=l+s+ss-1;  
  77.         return query(la,lb,l,mid,p+1,k);  
  78.     }  
  79.     else{  
  80.         int la=mid+1+a-l-s;  
  81.         int lb=mid+1+b-l-s-ss;   //lb=la+b-a-num[b]=mid+1+a-l-s+b-a-s-ss  
  82.         return query(la,lb,mid+1,r,p+1,k-ss);  
  83.     }  
  84. }  
  85. int main() {  
  86.   
  87.     #ifndef ONLINE_JUDGE  
  88.     freopen("in.txt","r",stdin);  
  89.     #endif  
  90.     int n,m,k,a,b;  
  91.     while(~scanf("%d%d",&n,&m)){  
  92.         for(int i=1;i<=n;i++){  
  93.             scanf("%d",&arr[i]);  
  94.             t[0].val[i]=sorted[i]=arr[i];  
  95.         }  
  96.         sort(sorted+1,sorted+n+1);  
  97.         build(1,n,0);  
  98.         while(m--){  
  99.             scanf("%d%d%d",&a,&b,&k);  
  100.             printf("%d\n",query(a,b,1,n,0,k));  
  101.         }  
  102.     }  
  103. }              
题目:poj2104


划分树and主席树

//hdu 2665 这里就做为划分树模板了; #include #include using namespace std; #define N 100100 int data[N]; stru...
  • laziercs
  • laziercs
  • 2012年08月29日 13:17
  • 1204

数据结构----划分树

今晚又学了另外一种树----划分树。看了一晚上了,也是大概明白一些而已,对于一些细节还是不太理解。 划分树是一种基于线段树的数据结构。主要用于快速求出(在log(n)的时间复杂度内)序列区间的第k...
  • Lin_disguiser
  • Lin_disguiser
  • 2016年08月02日 00:34
  • 1170

【划分树】

划分树和归并树都是用线段树作为辅助的,原理是基于快排 和归并排序 的。 划分树的建树过程基本就是模拟快排过程,取一个已经排过序的区间中值,然后把小于中值的点放左边,大于的放右边。并且记录d层...
  • leolin_
  • leolin_
  • 2011年08月18日 02:08
  • 906

划分树算法 模板

转:http://www.cnblogs.com/pony1993/archive/2012/07/17/2594544.html划分树是一种基于线段树的数据结构。主要用于快速求出(在log(n)的时...
  • Littlewhite520
  • Littlewhite520
  • 2017年04月20日 00:22
  • 334

个人对主席树算法的理解

首先借主席树发明人的一段话: ..这个东西是当初我弱不会划分树的时候写出来替代的一个玩意..被一小撮别有用心的人取了很奇怪的名字> < 想法是对原序列的每一个前缀[1..i]建立出一颗线段树维护...
  • SprintfWater
  • SprintfWater
  • 2013年06月24日 15:17
  • 13893

划分树求一个数列任意区间的第k小值

#include #include #include using namespace std; #define N 100500 #define MID ((l+r)>...
  • womeidagong
  • womeidagong
  • 2015年04月30日 16:04
  • 217

HDU 4251 The Famous ICPC Team Again 划分树

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4251 题意:给定一个数组,求区间中位数,保证给定区间长度是奇数 思路:划分树求区间中位...
  • discreeter
  • discreeter
  • 2016年05月17日 16:09
  • 348

划分树读书笔记

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

BZOJ 2654 [整体二分][MST]

Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 2019  Solved: 825[Submit][Status][Discuss]Descriptio...
  • lemonoil
  • lemonoil
  • 2017年07月07日 16:05
  • 165

poj 2104 K-th Number - 经典划分树

Description You are working for Macrohard company in data structures department. After failing yo...
  • u012183589
  • u012183589
  • 2015年07月08日 18:40
  • 606
收藏助手
不良信息举报
您举报文章:划分树
举报原因:
原因补充:

(最多只允许输入30个字)