數據結構--線段樹

  線段樹用於處理區間數據的更新與查詢問題,不考慮往區間中增加與刪除數據的,主要用於統計數據方面的需求,在更新與查詢的時間複雜度都爲logn級別。線段樹不屬於完全二叉樹,但屬於平衡二叉樹。

線段樹事例

  • 數組爲存儲的實現代碼如下:
/**
 * 
 * 功能描述:線段樹
 * 
 * @version 2.0.0
 * @author zhiminchen
 */
public class SegmentTree<E> {

    // 用於存儲線段數的數據
    private E[]       data;
    // 用於存儲原始數據
    private E[]       tree;
    // 用於抽象線段樹的統計操作
    private Merger<E> merger;

    /**
     * 
     * 功能描述:
     * 
     * @version 2.0.0
     * @author zhiminchen
     */
    static interface Merger<E> {
        public E merge(E left,
                       E right);
    }

    /**
     * 線段樹的構造方法,傳入的是一個數組與merger對象
     * @param arr
     * @param merger
     */
    public SegmentTree(E[] arr, Merger<E> merger) {
        this.merger = merger;
        data = (E[]) new Object[arr.length];
        // 將arr傳入的數據複製到data數組中
        for (int i = 0; i < arr.length; i++) {
            data[i] = arr[i];
        }
        // 這裏需要4倍的空間(tree是一個滿二叉樹的空間,需要考慮只有10個節點的情況)
        tree = (E[]) new Object[arr.length * 4];
        buildSegmentTree(0, 0, data.length - 1);
    }

    /**
     * 
     * 功能描述: 在treeIndex的位置創建表示區間【left, right】的線段樹
     * 
     * @param treeIndex
     * @param left
     * @param right void
     * @version 2.0.0
     * @author zhiminchen
     */
    private void buildSegmentTree(int treeIndex,
                                  int left,
                                  int right) {
        // 遞歸終止條件
        if (left == right) {
            tree[treeIndex] = data[left];
            return;
        }
        // treeIndex的左孩子節點的值
        int leftIndex = getLeft(treeIndex);
        // treeIndex的右孩子節點的值
        int rightIndex = getRight(treeIndex);
        // 找到線段樹中間的區間,遞歸構造區間樹的左右孩子節點
        int mid = left + (right - left) / 2;
        //
        buildSegmentTree(leftIndex, left, mid);
        buildSegmentTree(rightIndex, mid + 1, right);
        // 這裏貿給具體的業務邏輯處理
        tree[treeIndex] = merger.merge(tree[leftIndex], tree[rightIndex]);
    }

    /**
     * 
     * 功能描述:對線段樹中的值進行更新
     * 
     * @param index
     * @param e void
     * @version 2.0.0
     * @author zhiminchen
     */
    public void set(int index,
                    E e) {
        if (index < 0 || index > data.length - 1) {
            throw new IllegalArgumentException("index is illegal");
        }
        data[index] = e;
        set(0, 0, data.length - 1, index, e);
    }

    /**
     * 
     * 功能描述: 在以treeIndex爲根的線段樹中 更新index的值爲e
     * 
     * @param treeIndex
     * @param left
     * @param right
     * @param index
     * @param E void
     * @version 2.0.0
     * @author zhiminchen
     */
    private void set(int treeIndex,
                     int left,
                     int right,
                     int index,
                     E e) {
        // 遞歸退出條件
        if (left == right) {
            tree[treeIndex] = e;
            return;
        }
        int mid = left + (right - left) / 2;
        // treeIndex的左孩子節點的值
        int leftIndex = getLeft(treeIndex);
        // treeIndex的右孩子節點的值
        int rightIndex = getRight(treeIndex);

        if (index >= mid + 1) { // 更新index在右子樹的情況
            set(rightIndex, mid + 1, right, index, e);
        } else {
            set(leftIndex, left, mid, index, e);
        }
        // 更新線段樹的值
        tree[treeIndex] = merger.merge(tree[leftIndex], tree[rightIndex]);
    }

    /**
     * 
     * 功能描述: 查詢某個區間的值
     * 
     * @param queryLeft
     * @param queryRight
     * @return E
     * @version 2.0.0
     * @author zhiminchen
     */
    public E query(int queryLeft,
                   int queryRight) {
        return query(0, 0, data.length - 1, queryLeft, queryRight);
    }

    /**
     * 
     * 功能描述: 在以treeIndex爲根的線段樹中區間爲【left, right】, 搜索【queryLeft, queryRight】區間的值
     * 
     * @param treeIndex
     * @param left
     * @param right
     * @param queryLeft
     * @param queryRight
     * @return E
     * @version 2.0.0
     * @author zhiminchen
     */
    private E query(int treeIndex,
                    int left,
                    int right,
                    int queryLeft,
                    int queryRight) {
        // 如果搜索的區間正好是treeIndex的根節點,則直接返回
        if (left == queryLeft && right == queryRight) {
            return tree[treeIndex];
        }

        int mid = left + (right - left) / 2;
        int leftTreeIndex = getLeft(treeIndex);
        int rightTreeIndex = getRight(treeIndex);
        // 區間的最小值比mid還大, 則在右子樹進行查找。
        if (queryLeft >= mid + 1) {
            return query(rightTreeIndex, mid + 1, right, queryLeft, queryRight);
        } else if (queryRight <= mid) { // 在線段樹的左子樹查找
            return query(leftTreeIndex, left, mid, queryLeft, queryRight);
        } else {
            // 處理在左右子樹都需要查找的情況
            E leftResult = query(leftTreeIndex, left, mid, queryLeft, mid);
            E rightResult = query(rightTreeIndex, mid + 1, right, mid + 1, queryRight);
            return merger.merge(leftResult, rightResult);
        }
    }

    public E get(int index) {
        if (index < 0 || index >= data.length) {
            throw new IllegalArgumentException("index is illegal");
        }
        return data[index];
    }

    /**
     * 
     * 功能描述: (完全二叉樹的數據表示)得到左孩子節點的索引
     * 
     * @param index
     * @return int
     * @version 2.0.0
     * @author zhiminchen
     */
    public int getLeft(int index) {
        return 2 * index + 1;
    }

    /**
     * 
     * 功能描述:(完全二叉樹的數據表示) 得到右孩子節點
     * 
     * @param index
     * @return int
     * @version 2.0.0
     * @author zhiminchen
     */
    public int getRight(int index) {
        return 2 * index + 2;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < tree.length; i++) {
            if (tree[i] != null) {
                sb.append(tree[i]);
            } else {
                sb.append("null");
            }

            if (i != tree.length - 1) {
                sb.append(", ");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    public static void main(String[] args) {
        Integer[] data = new Integer[] {
                                         1,
                                         2,
                                         3,
                                         5,
                                         -8,
                                         9,
                                         11,
                                         2,
                                         33,
                                         45,
                                         12
        };

        SegmentTree<Integer> segTree = new SegmentTree<Integer>(data, new Merger<Integer>() {

            public Integer merge(Integer left,
                                 Integer right) {
                return left + right;
            }

        });
        System.out.println(segTree.query(1, 8));
        segTree.set(5, 15);
        System.out.println(segTree.query(1, 8));

    }

}
  • 以樹結構存儲的實現如下:
/**
 * 
 * 功能描述:採用樹的方式實現線段樹
 * 
 * @version 2.0.0
 * @author zhiminchen
 */
public class SegmentTree2<E> {

    // 用於存儲原始數據
    private E[]       data;

    // 用於抽象線段樹的統計操作
    private Merger<E> merger;

    // 樹的根結點
    private Node<E>   root;

    /**
     * 
     * 功能描述: node結點,用於存儲數據
     * 
     * @version 2.0.0
     * @author zhiminchen
     */
    private static class Node<E> {
        E       e;
        int     left;     // 左區間起始位置
        int     right;    // 右區間終止位置
        Node<E> leftNode;
        Node<E> rightNode;

        Node(E e, int left, int right) {
            this(e, left, right, null, null);
        }

        Node(E e, int left, int right, Node<E> leftNode, Node<E> rightNode) {
            this.e = e;
            this.left = left;
            this.right = right;
            this.leftNode = leftNode;
            this.rightNode = rightNode;
        }
    }

    public SegmentTree2(E[] arr, Merger<E> merger) {
        this.merger = merger;
        data = (E[]) new Object[arr.length];
        // 將arr傳入的數據複製到data數組中
        for (int i = 0; i < arr.length; i++) {
            data[i] = arr[i];
        }
        // 調用構造樹的方法
        root = buildSegmentTree(0, data.length - 1);
    }

    /**
     * 
     * 功能描述: 構區間[left,right]的線段樹
     * 
     * @param left
     * @param right
     * @return Node<E>
     * @version 2.0.0
     * @author zhiminchen
     */
    private Node<E> buildSegmentTree(int left,
                                     int right) {
        // 遞歸終止條件
        if (left == right) {
            return new Node<E>(data[left], left, right);
        }
        // 找到線段樹中間的區間,遞歸構造區間樹的左右孩子節點
        int mid = left + (right - left) / 2;
        // 構建樹的左結點
        Node<E> leftNode = buildSegmentTree(left, mid);
        // 構建樹的右結點
        Node<E> rightNode = buildSegmentTree(mid + 1, right);
        // 求結點的值
        E e = merger.merge(leftNode.e, rightNode.e);
        // 返回結點
        Node<E> node = new Node<E>(e, left, right, leftNode, rightNode);
        return node;
    }

    /**
     * 功能描述:對線段樹中的值進行更新
     * 
     * @param index
     * @param e void
     * @version 2.0.0
     * @author zhiminchen
     */
    public void set(int index,
                    E e) {
        if (index < 0 || index > data.length - 1) {
            throw new IllegalArgumentException("index is illegal");
        }
        data[index] = e;
        set(root, 0, data.length - 1, index, e);
    }

    /**
     * 
     * 功能描述: 在以treeIndex爲根的線段樹中 更新index的值爲e
     * 
     * @param left
     * @param right
     * @param index
     * @param E void
     * @version 2.0.0
     * @author zhiminchen
     */
    private void set(Node<E> node,
                     int left,
                     int right,
                     int index,
                     E e) {
        // 遞歸退出條件
        if (left == right && node.left == left && node.right == right) {
            node.e = e;
            return;
        }

        int mid = left + (right - left) / 2;

        if (index >= mid + 1) { // 更新index在右子樹的情況
            set(node.rightNode, mid + 1, right, index, e);
        } else { //更新左子樹的情況
            set(node.leftNode, left, mid, index, e);
        }
        // 更新線段樹的值
        node.e = merger.merge(node.leftNode.e, node.rightNode.e);
    }

    /**
     * 
     * 功能描述:查詢【queryLeft, queryRight】區間的值 
     * 
     * @param queryLeft
     * @param queryRight
     * @return E
     * @version 2.0.0
     * @author zhiminchen
     */
    public E query(int queryLeft,
                   int queryRight) {
        return query(root, queryLeft, queryRight);
    }

    /**
     * 
     * 功能描述: 在以treeIndex爲根的線段樹中區間爲【left, right】, 搜索【queryLeft, queryRight】區間的值
     * 
     * @param treeIndex
     * @param left
     * @param right
     * @param queryLeft
     * @param queryRight
     * @return E
     * @version 2.0.0
     * @author zhiminchen
     */
    private E query(Node<E> node,
                    int queryLeft,
                    int queryRight) {
        // 如果搜索的區間正好是treeIndex的根節點,則直接返回
        if (node.left == queryLeft && node.right == queryRight) {
            return node.e;
        }
        int mid = node.left + (node.right - node.left) / 2;
        // 區間的最小值比mid還大, 則在右子樹進行查找。
        if (queryLeft >= mid + 1) {
            return query(node.rightNode, queryLeft, queryRight);
        } else if (queryRight <= mid) { // 在線段樹的左子樹查找
            return query(node.leftNode, queryLeft, queryRight);
        } else {
            // 處理在左右子樹都需要查找的情況
            E leftResult = query(node.leftNode, queryLeft, mid);
            E rightResult = query(node.rightNode, mid + 1, queryRight);
            return merger.merge(leftResult, rightResult);
        }
    }

    /**
     * 
     * 功能描述:
     * 
     * @version 2.0.0
     * @author zhiminchen
     */
    static interface Merger<E> {
        public E merge(E left,
                       E right);
    }

    public static void main(String[] args) {
        Integer[] data = new Integer[] {
                                         1,
                                         2,
                                         3,
                                         5,
                                         -8,
                                         9,
                                         11,
                                         2,
                                         33,
                                         45,
                                         12
        };

        SegmentTree2<Integer> segTree = new SegmentTree2<Integer>(data, new Merger<Integer>() {
            // 用於求合統計
            public Integer merge(Integer left,
                                 Integer right) {
                return left + right;
            }
        });

        System.out.println(segTree.query(1, 8));
        segTree.set(5, 15);
        System.out.println(segTree.query(1, 8));
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章