Data Structures and Algorithms segment tree

(A) the definition of

Segment tree is a balanced binary search tree where the absolute value of the difference of height balanced left and right subtrees of tree line does not exceed the content of each storage node 1. segment tree consists of two parts.?:

  • Interval or segment is: [range left point, right point interval] (expressed as a parameter or attribute node class represents a member)
  • Element stored node interval: determined according to business needs

Balanced binary tree based on the characteristics (absolute value of the difference between the height of the left and right subtrees of not more than 1), we can use NULL completion interval is not present, the final completion of a full binary tree, when the node interval sequentially layer by layer laying out of (output sequence), we can use an array manner represents a complete binary tree (there are n elements with sections, the maximum array size of the memory space is needed to 4n).
Here Insert Picture Description
From the above chart:

  • Left child node of each range interval [l, mid], the right child of [mid + 1, r] wherein l:. A left end point, r: the right end, mid = (l + r) / 2
  • For node i, left child node is 2 * i + 1, the right child of 2 * i + 2.

(Ii) custom segment tree

1. Create a segment tree from the array

public class SegmentTree<E> {

	/**
	 * 源数据数组
	 */
	private E[] data;

	/**
	 * 以数组的形式表现线段树
	 */
	private E[] tree;
	
	/**
	 * 融合器接口: 线段树的区间结点存储的元素根据融合器接口的merger()方法来决定
	 */
	private Merger<E> merger;

	/**
	 * 构造函数: 将传来的数组 构建成 线段树数组
	 * 
	 * @param arr
	 */
	@SuppressWarnings("unchecked")
	public SegmentTree(E[] arr, Merger<E> merger) {
		// 融合器接口初始化 
		this.merger = merger;
		
		// 源数据数组初始化
		data = (E[]) new Object[arr.length];
		for (int i = 0; i < arr.length; i++) {
			data[i] = arr[i];
		}

		// 线段树数组初始化的大小为 数组大小的4倍.
		tree = (E[]) new Object[4 * arr.length];
		// 构建线段树数组
		buildSegmentTree(0, 0, arr.length - 1);
	}

	/**
	 * 在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;
		
		// 先创建以左孩子结点索引处区间[最左端点...中间值]的线段树
		buildSegmentTree(leftTreeIndex, l, mid);
		// 然后创建以右孩子结点索引处区间[中间值+1...最右端点]的线段树
        buildSegmentTree(rightTreeIndex, mid + 1, r);
		
		// 最后区间要维护的数据 根据Merger接口的merge(e1, e2)方法来决定
		tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
	}
	
	/**
	 * 返回完全二叉树的数组表示中, 一个索引所表示的元素的左孩子结点的索引
	 *  
	 * @param index
	 * @return
	 */
	private int leftChild(int index) {
		return index * 2 + 1;
	}
	
	/**
	 * 返回完全二叉树的数组表示中, 一个索引所表示的元素的右孩子结点的索引
	 * 
	 * @param index
	 * @return
	 */
	private int rightChild(int index) {
		return index * 2 + 2;
	}
	
	@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		res.append('[');
		for (int i = 0; i < tree.length; i++) {
			res.append(tree[i]);
			if (i != tree.length - 1) {
				res.append(", ");
			}
		}
		res.append("]");
		return res.toString();
	}
}

test

public static void main(String[] args) {
	Integer[] nums = {1, 2, 3, 4, 5, 6};
	// lambda表达式 (a, b) -> a + b 表示Merger接口的merge(a, b), 求两数之和
	SegmentTree<Integer> segmentTree = new SegmentTree<>(nums, (a, b) -> a + b);
	System.out.println(segmentTree);
}

Here Insert Picture Description

2. segment tree query interval

There are three cases:

  • The children left the tree line interval contains the search range (mid> = right point query interval), recursive children into the left section
  • Segment tree of the right child interval contains the search range (mid <= left endpoint of the query range), recursive child into the right range
  • Search interval where tree line, while intervals around children, get [the left endpoint of the query range, mid] and [mid, right query interval point] value, to operate.
/**
 * 返回区间[queryL, queryR]的值
 * 
 * @param queryL
 * @param queryR
 * @return
 */
public E query(int queryL, int queryR) {
	 if (queryL < 0 || queryL >= data.length || queryR < 0 || queryR >= data.length || queryR < queryL) {
		 throw new IllegalArgumentException("Index is illegel.");
	 }
	 return query(0, 0, data.length - 1, queryL, queryR);
}

/**
 * 在以treeIndex为根的线段树中[r...l]的范围里, 搜索区间[queryL...queryR]的值
 * 
 * @param treeIndex
 * @param l
 * @param r
 * @param queryL
 * @param queryR
 * @return
 */
private E query(int treeIndex, int l, int r, int queryL, int queryR) {
	// 递归终止条件: 搜索区间的左端点 等于 以treeIndex为根的线段树的左端点  且 搜索区间的右端点 等于 以treeIndex为根的线段树的右端点
	if (l == queryL && r == queryR) {
		return tree[treeIndex];
	}
	
	int leftTreeIndex = leftChild(treeIndex);
	int rightTreeIndex = rightChild(treeIndex);
	int mid = l + (r - l) / 2; // 3 
	
	if (queryR <= mid) {
		// 第一种情况: 以treeIndex为根的线段树的左子树区间  包含 搜索区间
		return query(leftTreeIndex, l, mid, queryL, queryR);
	} else if (queryL >= mid + 1) {
		// 第二种情况: 以treeIndex为根的线段树的右子树区间  包含 搜索区间
		return query(rightTreeIndex, mid + 1, r, queryL, queryR);
	} else {
		// 第三种情况: 搜索区间 包含在  以treeIndex为根的线段树的左右子树区间
		E leftResult = query(leftTreeIndex, l, mid, queryL, mid);
		E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
		return this.merger.merge(leftResult, rightResult);
	}
}

3. A single update operation of the segment tree

Updating the segment tree index corresponding to the index value, and the value of the maintenance of all its parent nodes.

/**
 * 将index位置的值, 更新为e
 * 
 * @param index
 * @param e
 */
public void set(int index, E e) {
	if (index < 0 || index >= data.length) {
		throw new IllegalArgumentException("Index is Illegal.");
	}
	data[index] = e;
	set(0, 0, data.length - 1, index, e);
}

/**
 * 在以treeIndex为根的线段树中更新index的值为e
 * 
 * @param treeIndex
 * @param l
 * @param r
 * @param index
 * @param e
 */
private void set(int treeIndex, int l, int r, int index, E e) {
	// 递归终止的条件: 搜索区间的左端点 等于 搜索区间的右端点, 找到index索引在线段树的位置
	if (l == r) {
		tree[treeIndex] = e;
		return;
	}
	
	int leftTreeIndex = leftChild(treeIndex);
	int rightTreeIndex = rightChild(treeIndex);
	int mid = l + (r - l) / 2;
	
	if (index <= mid) {
		// 以treeIndex为根的线段树的左子树区间  包含 index
		set(leftTreeIndex, l, mid, index, e);
	} else { // index >= mid + 1
		// 以treeIndex为根的线段树的右子树区间  包含 index
		set(rightTreeIndex, mid + 1, r, index, e);
	}
	
	// 对修改元素结点的父节点重新赋值merge
	tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}

(C) the time complexity analysis

function time complexity analysis
query(l, r) O(h) => O(logn) Custom segment tree is a full binary tree, just a recursive query operation on the height of the tree
set(index, e) O(h) => O(logn) Custom segment tree is a full binary tree, just update recursion on the height of the tree
Released nine original articles · won praise 0 · Views 305

Guess you like

Origin blog.csdn.net/Admin_Lian/article/details/104795961