脱离ACM队伍已经一年多了,现在还能手写的算法不多,线段树是其中一种。谨以此文纪念逝去的ACM生涯

线段树是一种二叉搜索树,常用于区间求和、区间求极值,其查询和更新时间复杂度是O(logN)。

二叉树


线段树的主要操作包括初始化、更新和查询

1.初始化

初始化过程是一个递归算法,从根节点递归全树。作用是设定子节点区间范围、区间和、子节点索引以及父节点的左右子节点索引

2.查询

查询过程是一个递归算法,从根节点递归全树。首先初始化全局变量res值为0,用以保存结果,然后寻找最浅的被查询区间包含的节点。若节点左右边界均不在查询区间内,直接return;若节点左右边界均在查询区间内,该节点区间值与res相加,return;否则继续递归节点。

3.更新

更新过程与查询比较类似,这里不再赘述,其区别是更新会递归到最深的子节点,而不是查询那样浅尝辄止

话不多说,还是上代码吧

class LineTree {
    private nodes;
    private sum;
    private res;

    constructor(arr: number[]) {
        var res = 0;
        this.sum = [];
        this.nodes = [];
        var n = arr.length;
        for(var i=0 ;i<n; i++) {
            res += arr[i];
            this.sum[i] = res;
        }
        var node = {
            'left' : 0,
            'right' : n - 1,
            'value' : res,
            'index' : 0,
            'lindex' : null,
            'rindex' : null
        };
        this.nodes.push( node );
        this.build(node);
    }

    private build(node) {
        if(node.left == node.right) {
            return;
        }

        var left = node.left;
        var right = Math.floor( (node.left + node.right) / 2 );
        var left_node = {
            'left' : left,
            'right' : right,
            'value' : left == 0 ? this.sum[right] : this.sum[right] - this.sum[left-1],
            'index' : this.nodes.length,
            'lindex' : null,
            'rindex' : null
        };
        this.nodes[node.index].lindex = left_node.index;

        left = Math.floor( (node.left + node.right) / 2 ) + 1;
        right = node.right;
        var right_node = {
            'left' : left,
            'right' : right,
            'value' : left == 0 ? this.sum[right] : this.sum[right] - this.sum[left-1],
            'index' : this.nodes.length + 1,
            'lindex' : null,
            'rindex' : null
        };
        this.nodes[node.index].rindex = right_node.index;

        this.nodes.push(left_node, right_node);
        this.build(left_node);
        this.build(right_node);
    }

    private callQuery(node, left, right) {
        if(node.left > right || node.right < left) {
            return;
        }
        else if(left <= node.left && right >= node.right) {
            this.res += node.value;
            return;
        }
        else {
            this.callQuery( this.nodes[node.lindex], left, right );
            this.callQuery( this.nodes[node.rindex], left, right );
        }
    }

    public query(left: number, right: number): number {
        this.res = 0;
        this.callQuery(this.nodes[0], left, right);
        return this.res;
    }

    private callUpdate(node, index, value) {
        if(node.left > index || node.right < index) {
            return;
        }
        else {
            this.nodes[node.index].value += value;
            if(node.left == node.right) {
                return;
            }
            else {
                this.callUpdate(this.nodes[node.lindex], index, value);
                this.callUpdate(this.nodes[node.rindex], index, value);
            }
        }
    }

    public update(index: number, value: number): void {
        this.callUpdate(this.nodes[0], index, value);
    }
}

codebeast
51 声望0 粉丝

下一篇 »
NodeJS爬虫