脫離ACM隊伍已經一年多了,如今還能手寫的算法很少,線段樹是其中一種。謹以此文記念逝去的ACM生涯node
線段樹是一種二叉搜索樹,經常使用於區間求和、區間求極值,其查詢和更新時間複雜度是O(logN)。算法
線段樹的主要操做包括初始化、更新和查詢ui
初始化過程是一個遞歸算法,從根節點遞歸全樹。做用是設定子節點區間範圍、區間和、子節點索引以及父節點的左右子節點索引this
查詢過程是一個遞歸算法,從根節點遞歸全樹。首先初始化全局變量res值爲0,用以保存結果,而後尋找最淺的被查詢區間包含的節點。若節點左右邊界均不在查詢區間內,直接return;若節點左右邊界均在查詢區間內,該節點區間值與res相加,return;不然繼續遞歸節點。spa
更新過程與查詢比較相似,這裏再也不贅述,其區別是更新會遞歸到最深的子節點,而不是查詢那樣淺嘗輒止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); } }