点树的简单实现(极省空间)


思路:

    先画一棵完全二叉树, 为节省空间,采用数组来实现。对这棵二叉树,叶子用于存放数据,节点用于统计叶子信息。

通过下面的三种方法,进一步节省空间:

1   节点只记录左子树叶子信息,右子树叶子信息通过当前节点和父节点等节点的值计算得出。

     因而需要指定一个点,当作根节点的“父节点”,以便计算根节点右子树信息。

     可以将根节点从1开始编号,对节点i,左孩子编号为2*i,右孩子编号为2*i+1,并用编号0记录整根树所有叶子的信息。

2   对某些应用,叶子信息可以通过节点信息计算得出,因而不保存叶子信息,

3   完全二叉树,边界要求为2^k,为了表示[0, n)这n个点,需要将n增加到2^k,实际上,

    只要第n个叶子的父节点r存在就可以了,编号大于r的节点根本不会被访问到,因而没必要分配空间

 

 

 

 

 

View Code
// www.cnblogs.com/flyinghearts
#include<cstdio> 
#include
<cstdlib> 
#include
<cstring> 
 
template
<int N> struct Round2k {
  
enum { down = Round2k</ 2u>::down * 2
         up 
= down == N ? down : 2 * down };
}; 

template
<> struct Round2k<1> {
  
enum { down = 1, up = 1};
};
 
//若表示的区间为[0, M), 则 N >= (M+1+Extra)/2, 其中Extra为大等于m的最小2^t
//完全二叉树,根节点为1,对节点i,左孩子为2*i,右孩子为2*i+1
//节点编号范围[1, Extra)  叶子编号范围[Extra, 2*Extra), 点n 对应 叶子n+Extra,
//为节省空间,只记录各节点左子树下的叶子的个数,不记录叶子出现的个数
//info[0] 保存所有叶子的总个数, info[i]记录节点i的左子树下的所有叶子的总个数)

template 
<int  M, typename T = int>  //区间[0, M)
class  PointTree {
  
enum { Extra = Round2k<M>::up, N = (M + 1 + Extra) / 2u }; 
  
// T data[M]; 
  T info[N];
  
public:
  PointTree() { clear(); }
  
void clear() { memset(this0sizeof(*this));}
  
int size() { return info[0]; }
  
int capacity() { return N; }
  
  
void add(int n) {
    
++info[0];
    
for (int  i = Extra + n; i > 1; i /= 2u
      
if (i % 2u == 0)  ++info[i / 2u]; 
  }
  
  
void erease(int  n) { 
    
--info[0];
    
for (int i = Extra + n; i > 1; i /= 2u)
      
if (i % 2u == 0)  --info[i / 2u]; 
  }
  
  
void erease_safe(int n) { if (count(n)) return erease(n); }
  
  
int count(int n) {
    
// int sum = 0, i = Extra + n;
    
// while (i % 2u)  sum += info[(i /= 2u)];
    
// return info[i / 2u] - sum;
    int i = Extra + n;
    
if (i % 2u == 0return info[i / 2u];
    
int sum = 0;
    
do { i /= 2u; sum += info[i]; } while (i % 2u);
    
return info[i / 2u- sum;
  }
  
  
int lt(int  n) {
    
int sum = 0 ;
    
for (int i = Extra + n; i > 1; i /= 2u ) 
      
if (i % 2u) sum += info[i / 2u];
    
return sum;
  }
  
  
int lteq(int  n) {
    
//if (n == N - 1) return info[0]; 
    if (N == Extra && n == N - 1return info[0];
    
return lt(n + 1); 
  }
    
  
int gt(int  n)  { return info[0- lteq(n); }
  
int gteq(int  n) { return info[0- lt(n); }
  
  
int operator[](int n) { //第n+1小 
    int i = 1;
    
while (i < Extra) {
      
if (n < info[i]) { i *= 2; }
      
else { n -= info[i]; i = i * 2 + 1; }  
    }
    
return i - Extra;    
  }
  
}; 


int ra(int arr[], int len) //求逆序数
{
  
int sum = 0;
  
for (int i = 0; i < len - 1++i)
    
for (int j = i + 1; j < len; ++j)
      
if (arr[i] > arr[j]) ++sum;
  
return sum;    
}

template
<int N>
int rb(int arr[], int len) //求逆序数 点树实现
{
  PointTree
<N> pt;
  
int sum = 0;
  
for (int i = 0; i < len; ++i) {
    pt.add(arr[i]);
    sum 
+= pt.gt(arr[i]);
  }
  
return sum;  
}

int main()
{
  
const int N = 6;
  
int arr[N] = { 43,2,1,0,5};
  printf(
"%d \n", ra(arr, N));
  printf(
"%d \n", rb<N>(arr, N));
  
}

 

 

posted @ 2011-03-31 23:13  flyinghearts  阅读(1477)  评论(0编辑  收藏  举报