線段樹入門理解

_Phoenix發表於2015-09-18

這個連結上面對線段樹描述得很清楚【線段樹】線段樹入門之入門

下面談談自己的理解:

線段樹對多次查詢極有優勢,因為一般情況下並不需要掃描到樹的最底端就能得出結果。

個人感覺線段樹也是一種打表(樹形的表?)=。=比如哦,求區間的最大值,用線段樹的話,方法抽象點的說就是區間打表(個人猜測).....分別對每個區間記錄下其最大值,當掃描到這個區間的時候,直接取這個區間存放的最大值,避免再次深入搜尋。

線段樹更新的話,從根節點向父節點遞迴,凡是與節點數值變動有影響的節點,均需要更新一下以免出現父節點最大值並不等於所有子節點中最大值這種情況。但是與變動節點沒有影響的其餘節點,那麼則不需要改變。

貼上個人寫的程式碼(註釋寫得還是很清楚的....):

/*
線段樹入門
該例為查詢區間的最大值
*/
#include <iostream>
#include <cmath>
using namespace std;
const int MAXNODE = 2000006;
const int MAX = 1000003;
struct Node
{
    int value;
    int left, right;
};
Node node[MAXNODE];
int root[MAX]; //對線段樹的根節點進行打表,記錄下每個根節點所對應的樹的編號
void BuildTree(int i, int left, int right) //建立一棵樹,i代表起始點,l,r代表線段樹長度
{
    node[i].left = left;
    node[i].right = right;
    node[i].value = 0;
    if (left == right)
    {
        root[left] = i; //記錄葉子節點所對應的樹的序號
        return;
    }
    BuildTree(i << 1, left, (int)((left + right) / 2));
    BuildTree((i << 1) + 1, (int)((left + right) / 2) + 1, right);
}

void UpdateTree(int i) //從下到上更新節點,在函式外部更改根節點,i代表變動節點
{
    if (i == 1)
        return;
    int x = i / 2; //樹節點標號向上更改
    int a = node[x << 1].value;  //記錄左子節點值
    int b = node[(x << 1) + 1].value; //記錄右子節點值
    node[x].value = (a > b)? a : b;
    UpdateTree(x);
}
int Max = -1;
void Query(int i, int l, int r) //查詢節點,i代表查詢起始節點編號,l,r代表要查詢的線段區間
{
    if (node[i].left == l && node[i].right == r) //找到匹配區間段,記錄下最大值並退出
    {
        Max = (Max < node[i].value)? node[i].value : Max;
        return;
    }
    //如果不滿足整體匹配,那麼對查詢段進行拆分
    i = i << 1;  //首先進入左子樹查詢
    if (l <= node[i].right) //如果滿足查詢段中存在從起點數一段屬於左子樹
    {
        if (r <= node[i].right) //如果查詢段全部屬於左子樹
            Query(i, l, r); //遞迴進入左子樹再次查詢
        else
            Query(i, l, node[i].right); //否則部分進入左子樹查詢
    }
    i += 1; //接下來查詢右子樹
    if (r >= node[i].left)
    {
        if (l >= node[i].left) //如果全部屬於右子樹那麼遞迴進入右子樹繼續查詢
            Query(i, l, r);
        else
            Query(i, node[i].left, r); //否則屬於右子樹的線段遞迴右子樹查詢
    }
}
int main()
{
    BuildTree(1, 1, 5);
    node[root[1]].value = 6;
    node[root[5]].value = 4;
    UpdateTree(root[1]);
    UpdateTree(root[5]);
    Query(1, 1, 5);
    cout << Max;
    return 0;
}