• 第k小元素学习记录


    从第K元素看数据结构>>http://www.cppblog.com/820986942/archive/2011/05/23/146991.html 

    View Code
      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 
      5 using namespace std;
      6 
      7 #define ls rt<<1
      8 #define rs rt<<1|1
      9 #define lson l,m,ls
     10 #define rson m+1,r,rs
     11 #define mid (l+r)>>1
     12 
     13 #define MAXN 200010
     14 
     15 int sum[MAXN<<2];//记录有几个点在节点的范围内
     16 int rank[MAXN],fa[MAXN];//rank表示集合的大小,fa表示根
     17 int n;
     18 
     19 void init(int n)
     20 {
     21     for(int i=0;i<=n;i++)
     22     {
     23         rank[i]=1;
     24         fa[i]=i;
     25     }
     26 }
     27 
     28 int find(int x)
     29 {
     30     if(x != fa[x])
     31         fa[x]=find(fa[x]);
     32     return fa[x];
     33 }
     34 
     35 void build(int l,int r,int rt)
     36 {
     37     if(l==1)//开始时每个集合的大小都为1故都在区间[1,r]内
     38         sum[rt]=n;
     39     else
     40         sum[rt]=0;
     41     if(l==r) return ;
     42     int m=mid;
     43     build(lson);
     44     build(rson);
     45 }
     46 
     47 void update(bool flag,int val,int l,int r,int rt)
     48 {
     49     if(!flag)//flag见main函数
     50         sum[rt]--;
     51     else
     52         sum[rt]++;
     53     int m=mid;
     54     if(l==r) return ;
     55     if(val<=m) //val为集合的大小,小于m表示在左边
     56         update(flag,val,lson);
     57     else
     58         update(flag,val,rson);
     59 }
     60 
     61 void query(int k,int l,int r,int rt)
     62 {
     63     if(l==r)
     64     {
     65         printf("%d\n",l);
     66         return ;
     67     }
     68     int m=mid;
     69     if(k <= sum[rt<<1|1])
     70         query(k,rson);////右子树有大于等于k个数,第k个数肯定在右边
     71     else
     72         query(k-sum[rt<<1|1],lson);
     73 }
     74 
     75 int main()
     76 {
     77     int m;
     78     while(scanf("%d%d",&n,&m) != EOF)
     79     {
     80         init(n);
     81         build(1,n,1);
     82         while(m--)
     83         {
     84             int q,x,y;
     85             scanf("%d",&q);
     86             if(q==0)
     87             {
     88                 scanf("%d%d",&x,&y);
     89                 x=find(x);
     90                 y=find(y);
     91                 if(x==y)
     92                     continue;
     93                 update(false,rank[x],1,n,1);//先把x所在集合大小去掉
     94                 update(false,rank[y],1,n,1);
     95                 update(true,rank[x]+rank[y],1,n,1);//集合合并
     96                 fa[y]=x;
     97                 rank[x]+=rank[y];
     98             }
     99             else
    100             {
    101                 scanf("%d",&x);//第k小
    102                 query(x,1,n,1);
    103             }
    104         }
    105     }
    106     return 0;
    107 }

    一,线段树与树状数组动态更新k小元素:

    +线段树

    poj-2985

    可以与下面的树状数组相比较来理解,其实跟树状数组差不多。 

    View Code
      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 
      5 using namespace std;
      6 
      7 #define ls rt<<1
      8 #define rs rt<<1|1
      9 #define lson l,m,ls
     10 #define rson m+1,r,rs
     11 #define mid (l+r)>>1
     12 
     13 #define MAXN 200010
     14 
     15 int sum[MAXN<<2];//记录有几个点在节点的范围内
     16 int rank[MAXN],fa[MAXN];//rank表示集合的大小,fa表示根
     17 int n;
     18 
     19 void init(int n)
     20 {
     21     for(int i=0;i<=n;i++)
     22     {
     23         rank[i]=1;
     24         fa[i]=i;
     25     }
     26 }
     27 
     28 int find(int x)
     29 {
     30     if(x != fa[x])
     31         fa[x]=find(fa[x]);
     32     return fa[x];
     33 }
     34 
     35 void build(int l,int r,int rt)
     36 {
     37     if(l==1)//开始时每个集合的大小都为1故都在区间[1,r]内
     38         sum[rt]=n;
     39     else
     40         sum[rt]=0;
     41     if(l==r) return ;
     42     int m=mid;
     43     build(lson);
     44     build(rson);
     45 }
     46 
     47 void update(bool flag,int val,int l,int r,int rt)
     48 {
     49     if(!flag)//flag见main函数
     50         sum[rt]--;
     51     else
     52         sum[rt]++;
     53     int m=mid;
     54     if(l==r) return ;
     55     if(val<=m) //val为集合的大小,小于m表示在左边
     56         update(flag,val,lson);
     57     else
     58         update(flag,val,rson);
     59 }
     60 
     61 void query(int k,int l,int r,int rt)
     62 {
     63     if(l==r)
     64     {
     65         printf("%d\n",l);
     66         return ;
     67     }
     68     int m=mid;
     69     if(k <= sum[rt<<1|1])
     70         query(k,rson);////右子树有大于等于k个数,第k个数肯定在右边
     71     else
     72         query(k-sum[rt<<1|1],lson);
     73 }
     74 
     75 int main()
     76 {
     77     int m;
     78     while(scanf("%d%d",&n,&m) != EOF)
     79     {
     80         init(n);
     81         build(1,n,1);
     82         while(m--)
     83         {
     84             int q,x,y;
     85             scanf("%d",&q);
     86             if(q==0)
     87             {
     88                 scanf("%d%d",&x,&y);
     89                 x=find(x);
     90                 y=find(y);
     91                 if(x==y)
     92                     continue;
     93                 update(false,rank[x],1,n,1);//先把x所在集合大小去掉
     94                 update(false,rank[y],1,n,1);
     95                 update(true,rank[x]+rank[y],1,n,1);//集合合并
     96                 fa[y]=x;
     97                 rank[x]+=rank[y];
     98             }
     99             else
    100             {
    101                 scanf("%d",&x);//第k小
    102                 query(x,1,n,1);
    103             }
    104         }
    105     }
    106     return 0;
    107 }

     

    +树状数组

    看这里

    二,划分树与归并树求第k小元素:

    看大牛的博客上说的,划分树可以看成线段树+快排,归并树可以看成线段树+归并排序

    +划分树

    这里看看http://www.notonlysuccess.com/?s=2104

    poj 2104poj-2761都可以用划分树求。 

    题意就是求不同区间第k值:

    给出代码,还不是很理解:

    View Code
      1 #include <iostream>
      2 #include <cstdio>
      3 #include <algorithm>
      4 #include <cstring>
      5 
      6 using namespace std;
      7 
      8 #define ls rt<<1
      9 #define rs rt<<1|1
     10 #define lson l,m,ls
     11 #define rson m+1,r,rs
     12 
     13 #define MAXN 100001
     14 struct Seg_Tree
     15 {
     16     int l,r;
     17 }tt[MAXN<<2];
     18 int len;
     19 int sorted[MAXN];
     20 int toLeft[20][MAXN];
     21 int val[20][MAXN];
     22 
     23 void build(int d,int l,int r,int rt) 
     24 {
     25     tt[rt].l = l;
     26     tt[rt].r = r;
     27     if(tt[rt].l == tt[rt].r)    return ;
     28     int m = (l+r)>>1;
     29     int lsame = m - l + 1;
     30     for(int i = l ; i <= r ; i ++)
     31     {
     32         if(val[d][i] < sorted[m]) 
     33             lsame --;
     34     }
     35     int lpos = l;
     36     int rpos = m+1;
     37     int same = 0;
     38     for(int i = l ; i <= r ; i ++) 
     39     {
     40         if(i == l) 
     41             toLeft[d][i] = 0;
     42         else 
     43             toLeft[d][i] = toLeft[d][i-1];
     44         if(val[d][i] < sorted[m]) 
     45         {
     46             toLeft[d][i] ++;
     47             val[d+1][lpos++] = val[d][i];
     48         } 
     49         else if(val[d][i] > sorted[m]) 
     50             val[d+1][rpos++] = val[d][i];
     51         else 
     52         {
     53             if(same < lsame) 
     54             {
     55                 same ++;
     56                 toLeft[d][i] ++;
     57                 val[d+1][lpos++] = val[d][i];
     58             }
     59             else 
     60                 val[d+1][rpos++] = val[d][i];
     61         }
     62     }
     63     build(d+1,lson);
     64     build(d+1,rson);
     65 }
     66 
     67 int query(int k,int d,int l,int r,int rt) 
     68 {
     69     if(l == r) 
     70         return val[d][l];
     71     int s;
     72     int ss;
     73     if(l == tt[rt].l) 
     74     {
     75         s = toLeft[d][r];
     76         ss = 0;
     77     } 
     78     else 
     79     {
     80         s = toLeft[d][r] - toLeft[d][l-1];
     81         ss = toLeft[d][l-1];
     82     }
     83     if(s >= k)
     84     {
     85         int newl = tt[rt].l + ss;
     86         int newr = tt[rt].l + ss + s - 1; 
     87         return query(k,d+1,newl,newr,ls);
     88     } 
     89     else 
     90     {
     91         int m = (tt[rt].l + tt[rt].r)>>1;
     92         int bb = l - tt[rt].l - ss;
     93         int b = r - l + 1 - s;
     94         int newl = m + bb + 1;
     95         int newr = m + bb + b;
     96         return query(k-s,d+1,newl,newr,rs);
     97     }
     98 }
     99 
    100 int main() 
    101 {
    102     int n , m;
    103     while(scanf("%d%d",&n,&m) != EOF)
    104     {
    105         for(int i=1;i<=n;i++)
    106         {
    107             scanf("%d",&val[0][i]);
    108             sorted[i] = val[0][i];
    109         }
    110         sort(sorted + 1 , sorted + n + 1);
    111         build(0,1,n,1);
    112         while(m --) 
    113         {
    114             int l,r,k;
    115             scanf("%d%d%d",&l,&r,&k);
    116             printf("%d\n",query(k,0,l,r,1));
    117         }
    118     }
    119     return 0;
    120 }

     

    +归并树

    ++++未看

    小结:线段树与树状数组可以用来求区间固定,但是动态查询第K小元素的题,划分树与归并树可以求不同区间的第K小,具体看上面题。

  • 相关阅读:
    jQuery中deferred对象的使用(一)
    css中calc()的使用
    网络协议学习笔记1
    iOS: 类目里动态关联对象
    [转载] 2016 app 上线流程
    iOS:集成环信3.0循环掉坑。(学习笔记一)
    iOS 实现条件选择框
    iOS : 定义项目中接口文档
    iOS:消除项目中的警告⚠️
    iOS 一个简洁的条件筛选界面
  • 原文地址:https://www.cnblogs.com/Missa/p/2628921.html
Copyright © 2020-2023  润新知