Structures de données et algorithmes arbre segment

(A) la définition de

arbre segment est un arbre binaire équilibré où la valeur absolue de la différence de hauteur sous-arbres équilibrée gauche et droite de la ligne d'arbre ne dépasse pas le contenu de chaque nœud de stockage 1. arbre segment se compose de deux parties.?:

  • Intervalle ou le segment est: [Point gauche, l'intervalle de point de droite] (exprimé en tant que classe de noeud de paramètre ou attribut représente un membre)
  • Élément intervalle de noeud mémorisé: déterminé en fonction des besoins commerciaux

arbre binaire équilibré basé sur les caractéristiques (valeur absolue de la différence entre la hauteur de l'aile gauche et de droite des sous - arbres de pas plus de 1), on peut utiliser un intervalle de complétion NULL est pas présent, la réalisation finale d'un arbre binaire complet, lorsque la couche intervalle de séquence de noeud par la couche aménagement de (séquence de sortie), on peut utiliser d' une manière matricielle représente un arbre binaire complet (il y a N éléments avec des sections, la taille de la matrice de maximum de l'espace de mémoire est nécessaire pour 4n).
Insérer ici l'image Description
d'après le tableau ci - dessus:

  • noeud enfant gauche de chaque intervalle de distance [l, mi], l'enfant droit de [mi + 1, r] dans laquelle L :. A gauche point final, r: l'extrémité droite, à mi = (l + r) / 2
  • Pour le noeud i, noeud enfant gauche est 2 * i + 1, l'enfant droit de 2 * i + 2.

(Ii) arbre segment personnalisé

1. Création d'un arbre de segment de la matrice

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

Insérer ici l'image Description

2. segment de l'intervalle d'interrogation d'arbre

Il y a trois cas:

  • Les enfants ont quitté l'intervalle de ligne d'arbre contient la plage de recherche (mi> = intervalle d'interrogation de points à droite), les enfants récursifs dans la partie gauche
  • arbre segment de l'intervalle de l'enfant de droite contient la plage de recherche (mi <= extrémité gauche de la plage de requête), l'enfant récursive dans la bonne gamme
  • Intervalle de recherche où la ligne d'arbre, alors que des intervalles autour des enfants, prenez [l'extrémité gauche de la plage de requête, mi] et [mi, bon point de l'intervalle d'interrogation] valeur, pour fonctionner.
/**
 * 返回区间[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. Une opération de mise à jour unique de l'arbre de segment

Mise à jour de l'index d'arbre de segment correspondant à la valeur d'index, et la valeur de la maintenance de tous ses noeuds parents.

/**
 * 将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) le temps d'analyse de la complexité

fonction complexité du temps analyse
requête (l, r) O (h) => O (log n) arbre segment personnalisé est un arbre binaire complet, juste une opération de requête récursive sur la hauteur de l'arbre
ensemble (index, e) O (h) => O (log n) arbre segment personnalisé est un arbre binaire complet, juste mise à jour récursion sur la hauteur de l'arbre
Publié neuf articles originaux · louanges gagnées 0 · Vues 305

Je suppose que tu aimes

Origine blog.csdn.net/Admin_Lian/article/details/104795961
conseillé
Classement