划分树

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

2.查找整序列的第k大值方法:

a.采用快速查找法,分治思想。然而此方法会破坏原序列,并且需要O(n)的时间复杂度。

b.使用二叉平衡树进行维护,此方法每次查找时间复杂度仅为O(logn)。然而此方法丢失了原序列的顺序信息,无法查找出某区间内的第k大值

c.划分树:基本思想就是对于某个区间,把它划分成两个子区间,左边区间的数小于右边区间的数。查找的时候通过记录进入左子树的数的个数,确定下一个查找区间,最后范围缩小到1,就找到了。

3.建树:

使用一个辅助数组对原数组进行排序,找到这个区间的中位数a[mid],小于等于a[mid]的数划入左子树[l,mid],大于则划入右子树[mid+1,r]。同时,对于第i个数,记录在[l,i]区间内有多少数被划入左子树.注意对于被分到同一子树的元素,元素间的相对位置不能改变。然后递归建树。

4.查找:

查找的过程中主要问题就是确定将要查找的区间。查找的定义:查找深度为h,在大区间[st,ed]中找小区间[s,e]中的第k元素。

解决方法:

在大区间[st,ed]中找小区间[s,e]中的第k元素:在区间[st,s-1]中有sum[s-1]进入左子树,记它为l。同理区间[st,e]中有sum[e]个数进去左子树,记它为r。所以,我们知道区间小区间[s,e]中有(r-l)个数进入左子树。那么如果(r-l)>=k,那么就在左子树中继续查找,否则就在右子树中继续查找。

如果接下来要查找的是左子树,即在大区间[st,mid]中找小区间[?,?]中的第k元素:那么小区间应该是[st+l,st+r-1]。显然,这里k不用变。

如果接下来要查找的是右子树,即在大区间[mid+1,ed]中找小区间[?,?]中的第k-(r-l)元素:那么小区间应该是[mid+1++([st,s-1]区间中进入右子树的个数),mid+1+[st,e]区间进入右子树的个数)-1]。显然,这里k要减去区间里已经进入左子树的个数,即k变为k-(r-l)。

 于是递归继续查找直到s=e即可。
代码:
View Code
  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <algorithm>
  4 #include <memory.h>
  5 using namespace std;
  6 //此方法只能对互不相同的元素进行处理
  7 const int maxnum=9;
  8 struct node
  9 {
 10     int array[maxnum];
 11     int sum[maxnum];
 12 }tree[10];   //树有10层
 13 int sorted[maxnum];
 14 
 15 void Build_tree(int cur,int l,int r)
 16 {
 17     if(l==r)
 18         return ;
 19     int m=(l+r)/2;
 20     int i;
 21     
 22     int lsame=m-l+1; //这是往cur+1左子树中一共放入的节点,其中小于中值的一定在左子树中,等于中值的可能在左子树,也可能在右子树
 23     for(i=l;i<=r;i++)
 24         if(tree[cur].array[i]<sorted[m])
 25             lsame--;//与中值相同的放在左子树中的个数
 26 
 27     int lnum=l,rnum=m+1;
 28     for(i=l;i<=r;i++)
 29     {
 30         if(i==l)
 31             tree[cur].sum[i]=0;
 32         else
 33             tree[cur].sum[i]=tree[cur].sum[i-1];
 34         if(tree[cur].array[i]<sorted[m])
 35         {
 36             tree[cur].sum[i]++;
 37             tree[cur+1].array[lnum++]=tree[cur].array[i];
 38         }
 39         else if(tree[cur].array[i]>sorted[m])
 40             tree[cur+1].array[rnum++]=tree[cur].array[i];
 41         else   //if(tree[cur].array[i]==sorted[m])
 42         {
 43             if(lsame>0)
 44             {
 45                 lsame--;
 46                 tree[cur].sum[i]++;
 47                 tree[cur+1].array[lnum++]=tree[cur].array[i];
 48             }
 49             else
 50                 tree[cur+1].array[rnum++]=tree[cur].array[i];
 51         }
 52     }
 53     Build_tree(cur+1,l,m);
 54     Build_tree(cur+1,m+1,r);
 55 }
 56 
 57 int find(int cur,int st,int ed,int l,int r,int k)
 58 {
 59     cout<<cur<<" "<<st<<" "<<ed<<" "<<l<<" "<<r<<" "<<k<<endl;
 60     if(l==r)
 61         return tree[cur].array[l];
 62     int m=(st+ed)/2;
 63     int lnum,rnum;
 64     if(l-1<st)
 65         lnum=0;
 66     else
 67         lnum=tree[cur].sum[l-1];
 68     rnum=tree[cur].sum[r];
 69     if(rnum-lnum>=k)//表示有几个点进入左子树
 70     {
 71         return find(cur+1,st,m,st+lnum,st+rnum-1,k);
 72     }
 73     else
 74     {
 75 //        int rightl,rightr;
 76 //        if(l-1<st)
 77 //            rightl=0;
 78 //        else
 79 //            rightl=l-1-lnum;
 80 //        rightr=(r-l+1)-rnum-1;
 81         return find(cur+1,m+1,ed,m+1+(l-st-lnum),m+1+(r-st-rnum),k-(rnum-lnum));
 82     }
 83 }
 84 
 85 int main()
 86 {
 87     int i,j;
 88     for(i=1;i<maxnum;i++)
 89     {
 90         scanf("%d",&tree[0].array[i]);
 91         sorted[i]=tree[0].array[i];
 92     }
 93     sort(sorted+1,sorted+maxnum);
 94     Build_tree(0,1,maxnum-1);
 95 
 96     for(i=0;i<4;i++)
 97     {
 98         cout<<i<<endl;
 99         for(j=1;j<maxnum;j++)
100             cout<<tree[i].array[j]<<" ";
101         cout<<endl;
102         for(j=1;j<maxnum;j++)
103             cout<<tree[i].sum[j]<<" ";
104         cout<<endl;
105     }
106 
107     int res;
108     res=find(0,1,maxnum-1,2,5,2);
109     cout<<res<<endl;
110     return 0;
111 }
112 
113 
114 /*
115 1 1 1 1 1 1 1 1
116 */
117 
118 
119 //    for(i=0;i<4;i++)
120 //    {
121 //        cout<<i<<endl;
122 //        for(j=1;j<maxnum;j++)
123 //            cout<<tree[i].array[j]<<" ";
124 //        cout<<endl;
125 //        for(j=1;j<maxnum;j++)
126 //            cout<<tree[i].sum[j]<<" ";
127 //        cout<<endl;
128 //    }

问题:

一开始的处理只能针对互不相同的元素,后来引入一个lsame,解决了重复相同元素的问题。

 

 

 

posted @ 2012-08-07 10:57  pushing my way  阅读(226)  评论(0编辑  收藏  举报