Estructuras de Datos y Algoritmos segmento de árbol

(A) la definición de

árbol segmento es un árbol binario de búsqueda equilibrado donde el valor absoluto de la diferencia de altura equilibrada izquierda y subárboles derecha de la línea de árbol no excede el contenido de cada nodo de almacenamiento 1. árbol segmento consta de dos partes.?:

  • Intervalo o segmento es: [Punto de izquierda, intervalo de punto right] (expresado como una clase de parámetro o atributo nodo representa un miembro)
  • Elemento intervalo nodo almacenado: determina de acuerdo a las necesidades empresariales

árbol binario equilibrado basado en las características (valor absoluto de la diferencia entre la altura de los subárboles izquierdo y derecho de no más de 1), podemos utilizar intervalo de terminación NULL no está presente, la realización final de un árbol binario completo, cuando el nodo intervalo capa secuencialmente por capa por el que se de (secuencia de salida), podemos utilizar una manera matriz representa un árbol binario completo (hay n elementos con secciones, se necesita el tamaño máximo de la matriz del espacio de memoria a 4n).
Aquí Insertar imagen Descripción
a partir de la tabla anterior:

  • nodo Izquierda niño de cada intervalo de rango de [l, mid], el hijo derecho de [mediados + 1, r] en la que L :. Un punto final, r restante: el extremo derecho, mid = (l + r) / 2
  • Para el nodo i, nodo hijo izquierdo es 2 * i + 1, el hijo derecho de 2 * i + 2.

(Ii) árbol segmento personalizado

1. Crear un árbol segmento de la matriz

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();
	}
}

prueba

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);
}

Aquí Insertar imagen Descripción

2. segmento intervalo de consulta del árbol

Hay tres casos:

  • Los niños salieron de la línea de árboles intervalo contiene el rango de búsqueda (mediados> = punto correcto intervalo de consulta), los niños recursivas en la sección izquierda
  • árbol segmento del intervalo hijo derecho contiene el rango de búsqueda (mediados <= extremo izquierdo de la gama de consulta), hijo recursiva en el rango correcto
  • Buscar intervalo en el que la línea de árboles, mientras que los intervalos alrededor de los niños, conseguir [el extremo izquierdo de la gama de consulta, a mediados] y [mediados, consulta la derecha punto de intervalo] valor, para operar.
/**
 * 返回区间[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. Una operación única actualización del árbol segmento

La actualización del índice de árbol segmento correspondiente al valor del índice, y el valor del mantenimiento de todos sus nodos padre.

/**
 * 将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) el análisis de complejidad tiempo

función complejidad del tiempo análisis
consulta (l, r) O (h) => O (log n) árbol segmento personalizado es un árbol binario completo, sólo una operación de consulta recursiva en la altura del árbol
conjunto (índice, e) O (h) => O (log n) árbol segmento personalizado es un árbol binario completo, justo actualización recursividad en la altura del árbol
Liberadas nueve artículos originales · ganado elogios 0 · Vistas 305

Supongo que te gusta

Origin blog.csdn.net/Admin_Lian/article/details/104795961
Recomendado
Clasificación