線段樹用於處理區間數據的更新與查詢問題,不考慮往區間中增加與刪除數據的,主要用於統計數據方面的需求,在更新與查詢的時間複雜度都爲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)); } }