線段樹(Segment Tree)

來源:互聯網
上載者:User

標籤:

原文連結:線段樹(Segment Tree)1、概述

線段樹,也叫區間樹,是一個完全二叉樹,它在各個節點儲存一條線段(即“子數組”),因而常用於解決數列維護問題,基本能保證每個操作的複雜度為O(lgN)。

線段樹是一種二叉搜尋樹,與區間樹相似,它將一個區間劃分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。

對於線段樹中的每一個非葉子節點[a,b],它的左兒子表示的區間為[a,(a+b)/2],右兒子表示的區間為[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最後的子節點數目為N,即整個線段區間的長度。

使用線段樹可以快速的尋找某一個節點在若干條線段中出現的次數,時間複雜度為O(logN)。而未最佳化的空間複雜度為2N,因此有時需要離散化讓空間壓縮。

本文地址:http://www.cnblogs.com/archimedes/p/segment-tree.html,轉載請註明源地址。

2、線段樹基本操作

線段樹的基本操作主要包括構造線段樹,區間查詢和區間修改。

(1)線段樹構造

首先介紹構造線段樹的方法:讓根節點表示區間[0,N-1],即所有N個數所組成的一個區間,然後,把區間分成兩半,分別由左右子樹表示。不難證明,這樣的線段樹的節點數只有2N-1個,是O(N)層級的,

節點定義如下:

typedef struct node {    int l;        //線段的左端點    int r;        //線段的左端點    int value;    //線段上的值}node;
線段樹的構建
bulid//以節點v為根建樹、v對應區間為[l,r]{    對節點v初始化     if (l!=r) {          以v的左孩子為根建樹,區間為[l,(l+r)/2]          以v的右孩子為根建樹,區間為[(l+r)/2+1,r]    }}

完整的建樹代碼如下:

#define N 10000node tree[N];void bulid(int l, int r, int v) //對結點v進行建立,區間為l~r{    tree[v].l = l;    tree[v].r = r;    if(l == r) {        //進行結點的初始化        tree[v].value = a[r];        return;    }    int mid = (l + r) / 2;    bulid(v * 2, l, mid);    bulid(v * 2 + 1, mid + 1, r);    //根據左右兒子更新當前結點    tree[v].value = tree[v * 2].value + tree[v * 2 + 1].value;}
更新

當在a[i]~a[j]上的所有的元素都加上一個值c的時候

如果a[i]~a[j]剛還是一個完整段的時候,直接將這個段的value值加上c*(r-l+1)

當更新的區間不是一個完整段的時候,採用一種記錄增量的方法:給每個節點增加一個域:int add,記錄更新操作的增量c,初始的時候add均為0,比如當對2~5區間更新後,給該結點的add加上一個值c,再下次要對2~3結點進行更新或查詢時,再將add傳遞到下面的孩子結點中去

完整的更新樹代碼如下:

typedef struct node {    int l;        //線段的左端點    int r;        //線段的左端點    int value;    //線段上的值    int add;}node;void update(int v, int r, int l, int m)//更新區間l~r加上數m{    if(tree[v].l == l && tree[v].r == r) {  //找到,更新並記錄增量        tree[v].value += m * (r - l + 1);        tree[v].add = m;        return;    }    if(tree[v].add) {        tree[2 * v].add += tree[v].add;        tree[2 * v + 1].add += tree[v].add;        tree[v].add = 0;    }    int mid = (tree[v].l + tree[v].r) / 2;    if(r <= mid) {        update(v * 2, l, r, m);   //只對左兒子更新    } else {        if(l > mid) {            update(v * 2 + 1, l, r, m);  //只對右兒子更新        } else {                        //區間橫跨左右兒子區間,對其兩者均進行更新            update(v * 2, l, mid, m);            update(v * 2 + 1, mid + 1, r, m);        }    }}
查詢

查詢區間l~r上的value值

void query(int v, int l, int r)  //當前查詢結點為v,要查詢的區間為l~r{    if(tree[v].l == l && tree[v].r == r) {        ans += tree[v].value;        return;    }    if(tree[v].add) {        tree[v * 2].add += tree[v].add;        tree[v * 2 + 1].add += tree[v].add;        tree[v].add = 0;    }    int mid = (tree[v].l + tree[v].r) / 2;    if(r <= mid) {        query(v * 2, l, r);   //要查詢的區間都在左兒子    } else {        if(l > mid) {            query(v * 2 + 1, l, r);  //要查詢的區間都在左兒子        } else {                //要查詢的區間橫跨左右孩子            query(v * 2, l, mid);            query(v * 2 + 1, mid + 1, r);        }    }}
編程實踐

poj2182 Lost Cows

DescriptionN (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, they visited the neighborhood ‘watering hole‘ and drank a few too many beers before dinner. When it was time to line up for their evening meal, they did not line up in the required ascending numerical order of their brands. Regrettably, FJ does not have a way to sort them. Furthermore, he‘s not very good at observing problems. Instead of writing down each cow‘s brand, he determined a rather silly statistic: For each cow in line, he knows the number of cows that precede that cow in line that do, in fact, have smaller brands than that cow. Given this data, tell FJ the exact ordering of the cows. Input* Line 1: A single integer, N * Lines 2..N: These N-1 lines describe the number of cows that precede a given cow in line and have brands smaller than that cow. Of course, no cows precede the first cow in line, so she is not listed. Line 2 of the input describes the number of preceding cows whose brands are smaller than the cow in slot #2; line 3 describes the number of preceding cows whose brands are smaller than the cow in slot #3; and so on. Output* Lines 1..N: Each of the N lines of output tells the brand of a cow in line. Line #1 of the output tells the brand of the first cow in line; line 2 tells the brand of the second cow; and so on.Sample Input51210Sample Output24531
#include<stdio.h>#include<stdlib.h>#define N 10000int small[N], ans[N];struct segment {    int lc, rc, len;};struct segment s[3 * N];void bulid(int root, int lc, int rc)  /*建樹*/{    s[root].lc = lc;    s[root].rc = rc;    s[root].len = rc - lc + 1;    if(rc == lc) return;    bulid(2 * root, lc, (lc + rc) / 2);    bulid(2 * root + 1, (lc + rc) / 2 + 1, rc);}int query(int root, int k){    s[root].len--;    if(s[root].lc == s[root].rc) return s[root].lc;    else if(k <= s[2 * root].len) {        return query(2 * root, k);    } else {        return query(2 * root + 1, k - s[2 * root].len);    }}int main(){    int n, i;    scanf("%d", &n);    for(i = 2; i <=n; i++)        scanf("%d", &small[i]);    small[1] = 0;    bulid(1, 1, n);    for(i = n; i >= 1; i--)        ans[i] = query(1, small[i] + 1);    for(i = 1; i <= n; i++)        printf("%d\n", ans[i]);    return 0;}
View Code

 

 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) -> 9update(1, 2)sumRange(0, 2) -> 8Note:The array is only modifiable by the update function.You may assume the number of calls to update and sumRange function is distributed evenly.

 

public class NumArray {        private class SegmentTreeNode{            int start;            int end;            int sum;            SegmentTreeNode left;            SegmentTreeNode right;            public SegmentTreeNode(int start, int end){                this.start = start;                this.end = end;                this.sum = 0;                this.left = null;                this.right = null;            }        }                private SegmentTreeNode buildTree(int[] nums, int s, int e){            if(s>e)                return null;            SegmentTreeNode stn = new SegmentTreeNode(s,e);            if(s==e){                stn.sum = nums[s];            }else{                int mid = s+(e-s)/2;                stn.left = buildTree(nums, s, mid);                stn.right = buildTree(nums, mid+1, e);                stn.sum = stn.left.sum+stn.right.sum;            }            return stn;        }                private void update(SegmentTreeNode root, int i, int val){            if(root.start==root.end){                root.sum = val;            }else{                int mid = root.start + (root.end-root.start)/2;                if(i<=mid)                    update(root.left, i, val);                else if(i>mid)                    update(root.right, i, val);                root.sum = root.left.sum + root.right.sum;            }        }                private int sumRange(SegmentTreeNode root, int s, int e){            if(root.start==s&&root.end==e){                return root.sum;            }else{                int mid = root.start + (root.end-root.start)/2;                if(e<=mid)                    return sumRange(root.left, s, e);                else if(s>mid)                    return sumRange(root.right, s, e);                else                    return sumRange(root.left, s, mid)+sumRange(root.right, mid+1, e);            }        }                SegmentTreeNode root;        public NumArray(int[] nums) {            root = buildTree(nums, 0, nums.length-1);        }        void update(int i, int val) {            update(root, i, val);                   }                public int sumRange(int i, int j) {            return sumRange(root, i, j);        }}// Your NumArray object will be instantiated and called as such:// NumArray numArray = new NumArray(nums);// numArray.sumRange(0, 1);// numArray.update(1, 10);// numArray.sumRange(1, 2);
View Code參考連結:線段樹(segment tree)

線段樹(Segment Tree)(轉)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.