數據結構-線段樹 初始化、區間查詢和區間更新(待完善)

一. 線段樹的特點

  • 平衡二叉樹:最大的深度 和最小的深度 差最大爲1。

  • 一種二叉搜索樹。它將一段區間劃分爲若干單位區間,每一個節點都儲存着一個區間。它功能強大,支持區間求和,區間最大值,區間修改,單點修改等操作。
    處理問題: 區間查詢 ,區間更新

  • 線段樹 是以空間換時間的處理,開啓4N的空間
    如果區間有n個元素,數組表示需要有多少節點? 4n的空間,我們的線段樹不考慮添加元素,即空間固定,使用4n的靜態空間即可。可能有浪費,最壞情況,浪費2n-1的空間,空間換時間。

    0 層 : 1
    1 層 : 2
    2 層 : 4
    3 層 : 8
    。。。
    h 層 : 2^h -1
    對滿二叉樹: h層,一共·2^h -1 個節點(大約2 ^ h)
    最後一層 (h-1)層,有2^(h-1) -1 個節點
    最後一層的節點數大致等於前面所有層節點之和。
    如果 n = 2^k ,只需要2n區間
    最壞情況,如果n = 2^k + 1 ,需要4n的空間。
    如果使用鏈表,可以避免浪費空間。

二. 線段樹的初始化

在這裏插入圖片描述
僞代碼如下:

 /**
 * 在treeIndex的位置創建表示區間[l...r]的線段樹
 * 遞歸實現 這裏有些不好理解,需要手動畫畫
 *
 * @param treeIndex
 * @param l
 * @param r
 */
private void buildSegmentTree(int treeIndex, int l, int r) {
    if (l == r) {
        tree[treeIndex] = data[l];
        return;
    }
    int leftTreeIndex = leftChild(treeIndex);
    int rightTreeIndex = rightChild(treeIndex);
    //創建左右子樹
    int mid = l + (r - l) / 2;
    //l,mid; mid+1,r;遞歸
    buildSegmentTree(leftTreeIndex, l, mid);
    buildSegmentTree(rightTreeIndex, mid + 1, r);
    tree[treeIndex] = merge.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}

三. 區間查詢

在這裏插入圖片描述
僞代碼如下:

/**
     * 在以treeIndex爲根的線段樹中【l,r】的範圍裏,搜索區間【queryL,queryR】的值
     *
     * @param treeIndex  初始爲0
     * @param l     初始爲0 
     * @param r     初始爲數組最大索引
     * @param queryL  左區間
     * @param queryR   右區間
     * @return
     */
    private E query(int treeIndex, int l, int r, int queryL, int queryR) {
        if (l == queryL && r == queryR) {
            return tree[treeIndex];
        }
        int mid = l + (r - l) / 2;
        int leftTreeIndex = leftChild(treeIndex);
        int rightTreeIndex = rightChild(treeIndex);
        if (queryL >= mid + 1) {
            return query(rightTreeIndex, mid + 1, r, queryL, queryR);
        } else if (queryR <= mid) {
            return query(leftTreeIndex, l, mid, queryL, queryR);
        } else {
            //一部分左孩子,一部分右孩子上
            E leftResult = query(leftTreeIndex, l, mid, queryL, mid);
            E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
            return merge.merge(leftResult, rightResult);
        }
    }

四. 區間更新

都是採用遞歸的方式更新

//在以treeIndex爲跟的線段樹種更新index的值爲e 類似二分搜索樹中的更新
public void set(int treeIndex, int l, int r, int index, E e) {
    if (l == r) {
        tree[treeIndex] = e;
        return;
    }
    int mid = l + (r - l) / 2;
    int leftTreeIndex = leftChild(treeIndex);
    int rightTreeIndex = rightChild(treeIndex);
    if (index >= mid + 1) {
        set(rightTreeIndex, mid + 1, r, index, e);
    } else {
        set(leftTreeIndex, l, mid, index, e);
    }
    tree[treeIndex] = merge.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章