JAVA学习之路04——数据结构—>线段树

数据结构学了一段时间,初次接触线段树,放一点代码上来。
用数组实现的线段树,可以尝试用链表实现,节约空间。
优先队列可以传入一个比较器,而线段树则需要一个融合器:

public interface Merger<E> {
	public E merge(E left,E right);
}

线段树的调用过程:

public class Test {
//随手写的测试用例
	public static void main(String[] args) {
		Integer [] arr={0,1,2,3,4,5,6,7,8,9};
		//用拉姆达表达式书写融合过程,融合过程为两者相加
		SegmentTree<Integer> st=new SegmentTree<>(arr,(a,b)->a+b);
		System.out.println(st);
		//查某一段
		System.out.println(st.qurey(3,5));
		//更新某一个节点
		st.set(4, 61);
		System.out.println(st.qurey(3,5));
	}
}

线段树主主题函数:

public class SegmentTree <E> {
	//基于数组构造线段树
	private E []tree;
	//用于接收数组
	private E []array;
	//融合器
	private Merger<E> merger;
	//构造方法
	//arr 传入一个数组;merger 传入一个融合器;
	public SegmentTree(E[] arr ,Merger<E> merger) {
		this.merger=merger;
		array=(E [])new Object[arr.length];
		for(int i=0;i<arr.length;i++) 
			array[i]=arr[i];
		tree=(E [])new Object[4*arr.length];
		//调用辅助函数构造线段树
		tree[0]=buildTree(0,0,array.length-1);
	}
	
	//函数语义:返回构造完成后的线段树的根
	//treeRoot 当前根对应的下标;
	//left 根中线段的左边界;right 根中线段的右边界
	private E buildTree(int TreeRoot,int left,int right) {
		//递归到底的情况
		if(left==right )
			return array[left];	
		//对于非叶子节点,处理方式类似于后续遍历
		//取当前线段的中点
		int mid=left+(right-left)/2;
		//左孩子和右孩子分别调用函数生成对应的线段树
		tree[leftChild(TreeRoot)]=buildTree(leftChild(TreeRoot),left,mid);
		tree[rightChild(TreeRoot)]=buildTree(rightChild(TreeRoot),mid+1,right);
		//用户设计融合器,将左右节点融合。
		tree[TreeRoot]=(E) merger.merge(tree[leftChild(TreeRoot)], tree[rightChild(TreeRoot)]);
		//返回根节点
		return tree[TreeRoot];
	}
	//查询某一段线段树
	public E qurey(int left,int right) {
		return qurey(0,left,right,0,array.length-1);
	}
	//辅助查询函数。语义:返回本段的查询结果
	//root 当前根节点;qureyL查询的左边界;right查询的右边界;left当前的左边界;right当前的右边界
	private E qurey(int root,int qureyL,int qureyR,int left,int right) {
		//递归到底的情况
		if(left==qureyL && right==qureyR) 
			return tree[root];
		//当前区间左右边界的中间值
		int mid=left+(right-left)/2;
		//1.待查询的区间完全落在左子树
		if(qureyR<=mid)
			return qurey(leftChild(root),qureyL,qureyR,left,mid);
		//2.待查询的区间完全落在右子树
		if(qureyL>=mid+1)
			return qurey(rightChild(root),qureyL,qureyR,mid+1,right);
		//3.当待查询的区间在左右区间有交集的时候
		E leftResult=qurey(leftChild(root),qureyL,mid,left,mid);
		E rightResult=qurey(rightChild(root),mid+1,qureyR,mid+1,right);
		return merger.merge(leftResult, rightResult);		
	}
	
	//更新某个叶子节点。index:被更新的数组下标;e:更新为e
	public void set(int index,E e) {
		if(index>=array.length||index<0) 
			throw new IndexOutOfBoundsException();
		//先更新数组中的
		array[index]=e;
		//再更新树中的
		tree[0]=set(0,0,array.length-1,index,e);
	}
	//辅助更新函数。语义:返回本段更新的结果
	// root:根节点;left:左边界;right:右边界;index:被更新的数组下标;e:更新为e
	private E set(int root, int left,int right,int index,E e) {
		//递归终止条件
		if(left==right)
			return e;
		//取中点下标、根节点的左右孩子下标
		int mid=left+(right-left)/2;
		int leftChild=leftChild(root);
		int rightChild=rightChild(root);
		//叶子节点在左子树
		if(index<=mid)
			tree[leftChild]=set(leftChild,left,mid,index,e);
		//叶子节点在右子树
		else if(index>=mid+1)
			tree[rightChild]=set(rightChild,mid+1,right,index,e);
		//左右子树融合
		return merger.merge(tree[leftChild], tree[rightChild]);
	}
	
	public int getSize() {
		return array.length;
	}
	public E get(int index) {
		return array[index];
	}
	public boolean isEmpty() {
		return array.length==0;
	}
	private int leftChild(int index) {
		return 2*index+1;
	}
	private int rightChild(int index) {
		return 2*index+2;
	}
	//打印输出函数
	@Override
	public String toString() {
		// TODO 自动生成的方法存根
		StringBuilder strBd=new StringBuilder();
		strBd.append("[");
		for(E e:tree) {
			strBd.append(e+",");
		}
		strBd.replace(strBd.length()-1, strBd.length()-1, "]");
		return strBd.toString();
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值