【线段树 C++】

线段树 C++

C++实现线段树

对于一给定的数组a,若要计算「长度为n的连续元素和:a[i+1] + …+a[i+n] 」时间复杂度为O(n);若要修改某一位上的元素值,时间复杂度为O(1)。
若创建一个新数组b记录「前n位的累加和」,则查询「给定长度的元素和:a[i+1] + …+a[i+n] = b[i+n] - b[i]」的时间复杂度为O(1);修改某一位的元素值的时间复杂度变为O(n)。
当需要频繁进行两种操作时,时间开销较大。因此我们希望有一个平均时间复杂度较低的算法/数据结构来实现。
事实上,线段树的两种操作时间复杂度均为O(log n)

图形解释

下图为一个长度为5的数组 a[61, 3, 35, 5, 50] 生成的线段树,叶节点存储原数据,非叶子结点存储一定范围内节点的和。
如:
154 = a[0]+ a[1] +a[2]+a[3]+a[4]
55 = a[3] + a[4]
64 = a[0] + a[1]
3 = a[1]
sum_segment_tree样例
这是个sum_segment_tree的样例,事实上ST中的求和运算可以替换为min、max等运算。

代码

#include <iostream>
#include <vector>

class segment_tree{
    //计算任意长度的连续元素和get_sum,和修改任意元素update_node 的时间复杂度均为 O(log(n))
public:
    std::vector<int> data;
    std::vector<int> tree;

    //构造函数
    explicit segment_tree(std::vector<int>& input){
        data = input;
        tree = std::vector<int> (4*data.size(),int(0)); //四倍大小
        create_node(0 ,data.size() -1, 0);
    }

    //修改初始第i位元素值为value
    void update_node(int i, int value){
        int delta = value - data[i];
        data[i] = value;
        int left = 0,right = data.size()-1,mid,dex = 0;
        do{
            tree[dex] += delta;
            mid = (left + right)/2;
            if(i<=mid){
                right = mid;
                dex = dex*2 + 1;
            }else{
                left = mid + 1;
                dex = dex*2 +2;
            }
        }while(right != left);
        tree[dex] += delta;
    }

    //获取i到j的累加和 data[i] + data[i+1] + ... + data[j]
    int get_sum(int i, int j){
        int left=0,right=data.size()-1;
        return get_sum(i,j,left,right,0);
    }


private:
    //线段树节点赋值 线段上下坐标 线段树实际索引位置
    void create_node(int min_dex, int max_dex, int tree_dex){
        if(min_dex == max_dex){
            tree[tree_dex] = data[max_dex];
            return;
        }
        int mid = (min_dex + max_dex)/2;
        create_node(min_dex,mid,2*tree_dex +1);
        create_node(mid +1,max_dex,2*tree_dex +2);
        tree[tree_dex] = tree[2*tree_dex+1] + tree[2*tree_dex +2];
    }
    //递归获取累加和,深度优先,递归
    int get_sum(int i,int j, int left, int right,int dex){
        if(i > j)
            return 0;
        if(i == left && j ==right)
            return tree[dex];
        int mid = (left + right)/2;
        return get_sum(i, std::min(j,mid), left, mid,2*dex +1) +
               get_sum(std::max(i,mid+1), j, mid+1, right,2*dex +2);
    }

};

int main() {
    std::vector<int> f = {61,3,35,5,50};
    segment_tree t = segment_tree(f);
    for(int i : t.tree){
        printf("%d\t", i);
    }
    t.update_node(4,10);
    std::cout << std::endl;
    for(int i : t.tree){
        printf("%d\t", i);
    }
    printf("\n%d",t.get_sum(1,3));
    return 0;
}

执行main测试后,结果如下:

154	99	55	64	35	5	50	61	3	0	0	0	0	0	0	0	0	0	0	0	
114	99	15	64	35	5	10	61	3	0	0	0	0	0	0	0	0	0	0	0	
43
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值