TypeScript版線段樹

脫離ACM隊伍已經一年多了,如今還能手寫的算法很少,線段樹是其中一種。謹以此文記念逝去的ACM生涯node

線段樹是一種二叉搜索樹,經常使用於區間求和、區間求極值,其查詢和更新時間複雜度是O(logN)。算法

二叉樹


線段樹的主要操做包括初始化、更新和查詢ui

1.初始化

初始化過程是一個遞歸算法,從根節點遞歸全樹。做用是設定子節點區間範圍、區間和、子節點索引以及父節點的左右子節點索引this

2.查詢

查詢過程是一個遞歸算法,從根節點遞歸全樹。首先初始化全局變量res值爲0,用以保存結果,而後尋找最淺的被查詢區間包含的節點。若節點左右邊界均不在查詢區間內,直接return;若節點左右邊界均在查詢區間內,該節點區間值與res相加,return;不然繼續遞歸節點。spa

3.更新

更新過程與查詢比較相似,這裏再也不贅述,其區別是更新會遞歸到最深的子節點,而不是查詢那樣淺嘗輒止code

話很少說,仍是上代碼吧blog

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);
    }
}
相關文章
相關標籤/搜索