思路:
先画一棵完全二叉树, 为节省空间,采用数组来实现。对这棵二叉树,叶子用于存放数据,节点用于统计叶子信息。
通过下面的三种方法,进一步节省空间:
1 节点只记录左子树叶子信息,右子树叶子信息通过当前节点和父节点等节点的值计算得出。
因而需要指定一个点,当作根节点的“父节点”,以便计算根节点右子树信息。
可以将根节点从1开始编号,对节点i,左孩子编号为2*i,右孩子编号为2*i+1,并用编号0记录整根树所有叶子的信息。
2 对某些应用,叶子信息可以通过节点信息计算得出,因而不保存叶子信息,
3 完全二叉树,边界要求为2^k,为了表示[0, n)这n个点,需要将n增加到2^k,实际上,
只要第n个叶子的父节点r存在就可以了,编号大于r的节点根本不会被访问到,因而没必要分配空间
点树的实现
// www.cnblogs.com/flyinghearts
#include<cstdio>
#include<cstdlib>
#include<cstring>
template<int N> struct Round2k {
enum { down = Round2k<N / 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(this, 0, sizeof(*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 == 0) return 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 - 1) return 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] = { 4, 3,2,1,0,5};
printf("%d \n", ra(arr, N));
printf("%d \n", rb<N>(arr, N));
}
作者: flyinghearts
出处: http://www.cnblogs.com/flyinghearts/
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。