LeetCode 307 Range Sum Query - Mutable(树状数组 || 线段树)

Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.
The update(i, val) function modifies nums by updating the element at index i to val.

Example:
Given nums = [1, 3, 5]

sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8

Note:
The array is only modifiable by the update function.
You may assume the number of calls to update and sumRange function is distributed evenly.
题目大意:给出一个数组,在数组上有两种操作。一个是sumRange(i, j),即求数组中下标从i到j的元素之和;另一个操作是update(i, val),即将数组中下标为i的元素的值更新为val。要求编程实现这两种操作。
解题思路:单点更新,区间查询,树状数组和线段树的模板题。使用树状数组时要注意更新操作的实现,因为树状数组的add(i, val)是将val加到arr[i]中,而不是将val赋值给arr[i]。
代码如下:

//线段树实现
typedef struct {
    int *tree;
    int size;
} NumArray;

int merge(int x, int y) {
    return x + y;
}

//构造线段树
void buildSegTree(NumArray *obj, int *arr, int treeIndex, int lo, int hi) {

    if (lo >= hi) {
        obj->tree[treeIndex] = arr[lo];
        return;
    }
    int mid = lo + (hi - lo) / 2;
    buildSegTree(obj, arr, 2 * treeIndex + 1, lo, mid);
    buildSegTree(obj, arr, 2 * treeIndex + 2, mid + 1, hi);
    obj->tree[treeIndex] = merge(obj->tree[2 * treeIndex + 1], obj->tree[2 * treeIndex + 2]);
}

//查询区间[i, j]内元素的和
int querySegTree(NumArray *obj, int treeIndex, int lo, int hi, int i, int j) {
    if (lo > j || hi < i) return 0;
    if (i <= lo && j >= hi) return obj->tree[treeIndex];

    int mid = lo + (hi - lo) / 2;
    if (i > mid) return querySegTree(obj, 2 * treeIndex + 2, mid + 1, hi, i, j);
    else if (j <= mid) return querySegTree(obj, 2 * treeIndex + 1, lo, mid, i, j);

    int leftQuery = querySegTree(obj, 2 * treeIndex + 1, lo, mid, i, mid);
    int rightQuery = querySegTree(obj, 2 * treeIndex + 2, mid + 1, hi, mid + 1, j);

    return merge(leftQuery, rightQuery);
}

//线段树更新
void updateValSegTree(NumArray *obj, int treeIndex, int lo, int hi, int arrIndex, int val) {
    if (lo >= hi) {
        obj->tree[treeIndex] = val;
        return;
    }
    int mid = lo + (hi - lo) / 2;
    if (arrIndex > mid) updateValSegTree(obj, 2 * treeIndex + 2, mid + 1, hi, arrIndex, val);
    else if (arrIndex <= mid) updateValSegTree(obj, 2 * treeIndex + 1, lo, mid, arrIndex, val);
    obj->tree[treeIndex] = merge(obj->tree[2 * treeIndex + 1], obj->tree[2 * treeIndex + 2]);
}

NumArray *numArrayCreate(int *nums, int numsSize) {
    NumArray *obj = (NumArray *) malloc(sizeof *obj);
    obj->tree = (int *) malloc(sizeof(int) * (4 * numsSize + 1));
    obj->size = numsSize;
    buildSegTree(obj, nums, 0, 0, obj->size - 1);
    return obj;
}

void numArrayUpdate(NumArray *obj, int i, int val) {
    updateValSegTree(obj, 0, 0, obj->size - 1, i, val);
}

int numArraySumRange(NumArray *obj, int i, int j) {
    return querySegTree(obj, 0, 0, obj->size - 1, i, j);
}

void numArrayFree(NumArray *obj) {

    free(obj->tree);
    free(obj);
}
//树状数组实现
typedef struct {
    int* origin;
    int* arr;
    int size;
} NumArray;

int sum(NumArray* obj, int pos){
    int sum = 0;
    while(pos > 0) {
        sum += obj->arr[pos];
        pos -= (pos & -pos);
    }
    return sum;
}

void add(NumArray* obj, int pos, int val){
    while(pos <= obj->size){
        obj->arr[pos] += val;
        pos += (pos & -pos);
    }
}

NumArray* numArrayCreate(int* nums, int numsSize) {
    NumArray* obj = malloc(sizeof *obj);
    obj->origin = malloc(sizeof(int));
    obj->origin = nums;

    obj->arr = malloc(sizeof(int) * (numsSize + 1));
    memset(obj->arr,0,sizeof(int)*(numsSize+1));

    obj->size = numsSize;
    for(int i = 1;i <= numsSize;i++){
        add(obj, i, nums[i-1]);
    }
    return obj;
}

void numArrayUpdate(NumArray* obj, int i, int val) {
    int diff = val - obj->origin[i];
    obj->origin[i++] = val;
    add(obj, i, diff);
}

int numArraySumRange(NumArray* obj, int i, int j) {
    return sum(obj, j + 1) - sum(obj, i);
}

void numArrayFree(NumArray* obj) {
    free(obj->arr);
    free(obj->origin);
    free(obj);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值