线段树学习

线段树是一种平衡二叉搜索树,用于高效地处理区间查询和更新。本文详细介绍了累加值线段树的结构、构造、求和与更新操作,并延伸至最小值线段树的构建,适用于数据结构与算法学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线段树简介

线段树是一种平衡二叉搜索树(完全二叉树),它将一个线段区间划分成一些单元区间。对于线段树中的每一个非叶子节点,表示区间[a, b]的和,它的左孩子表示的区间为[a,(a+b)/2],右孩子表示的区间为[(a+b)/2+1,b],最后的叶子节点数目为N,与数组下标对应。线段树一般包括建立、查询、插入、更新等操作,建立规模为N的时间复杂度是 O(NlogN),其他操作时间复杂度为O(logN) 。

线段树能够解决什么样的问题:

线段树的适用范围很广,可以在线维护修改以及查询区间上的最值,求和。更可以扩充到二维线段树(矩阵树)和三维线段树(空间树)。对于一维线段树来说,每次更新以及查询的时间复杂度为O(logN)。

二、累加值线段树的结构

由于线段树是完全二叉树,线段树可以使用数组保存,对二叉树进行层次遍历。

假设某个节点下标为i, 它的左孩子下标为2i+1, 右孩子下标为2i+2。如数组 nums = [0, 1, 3, 4, 6] 存储为线段树数组values = [14, 4, 10, 1, 3, 4, 6, 0, 1]。
在这里插入图片描述

三、累加值线段树的构造

在这里插入图片描述

public class SegmentTree {
	int[] value, nums;
	int n;
	
	public SegmentTree(int n, int[] nums) {
		value = new int[4 * n];
		this.n = n;
		this.nums = nums;
		build(0, 0, n - 1);
	}
	public void build(int pos, int left, int right) {
	
		if (left == right) {value[pos] = nums[left];return;}
		int mid = (left + right) / 2;
		build(2 * pos + 1, left, mid);
		build(2 * pos + 2, mid + 1, right);
		value[pos] = value[2 * pos + 1] + value[2 * pos + 2];
	}
}

四、累加值线段树的求和

在这里插入图片描述

public class SegmentTree {
	int[] value, nums;
	int n;
	
	public SegmentTree(int n, int[] nums) {
		value = new int[4 * n];
		this.n = n;
		this.nums = nums;
		build(0, 0, n - 1);
	}
	// value[pos] 存储着nums[left,right]的和 比如query(0,0,4,1,4)
	public int query(int pos, int left, int right, int qleft, int qright) {
		
		if (qleft > right || qright < left) return 0;// 查询区间在当前区间之外
		if (qleft <= left && qright >= right) return value[pos];// 查询区间完全包含当前区间
		int mid = (left + right) / 2;
		int leftSum = query_sum(2 * pos + 1, left, mid, qleft, qright);// 查询左子树
		int rightSum = query_sum(2 * pos + 2, mid + 1, right, qleft, qright);// 查询右子树
		return leftSum + rightSum;
	}
}

五、累加值线段树的更新

在这里插入图片描述

public class SegmentTree {
	int[] value, nums;
	int n;
	
	public SegmentTree(int n, int[] nums) {
		value = new int[4 * n];
		this.n = n;
		this.nums = nums;
		build(0, 0, n - 1);
	}
	// 在nums[left,right]范围内更新index的值为newValue
	public void update(int pos, int left, int right, int index, int newValue) {
		if (left == right && left == index) {
			value[pos] = newValue;return;
		}
		int mid = (left + right) / 2;
		if (index <= mid) {
			update(2 * pos + 1, left, mid, index, newValue);
		} else { 
			update(2 * pos + 2, mid + 1, right, index, newValue);
		}
		value[pos] = value[pos * 2 + 1] + value[pos * 2 + 2]
	}
}

六、最小值线段树构建

class SegTree { // 定义线段树,采用数组存储

int[] value, nums;
int n;// 表示nums数组的长度

SegTree(int n, int[] nums) {
	value = new int[4 * n];this.n = n;this.nums = nums;
	build(1, 0, n - 1);//注意bulid pos的初值是1 后序的坐标也要跟着变
}

// pos表示 nums[left,right]最小值坐标在value中存储的位置
public void build(int pos, int left, int right) {
	
	if (right == left) {value[pos] = left;return;}// 注意存储的是坐标
	build(2 * pos, left, (left + right) / 2);// 建立左子树
	build(2 * pos + 1, (right + left) / 2 + 1, right);// 建立右子树
	int leftIndex=value[2 * pos],rightIndex=value[2 * pos + 1];
	value[pos] = nums[leftIndex] > nums[rightIndex] ? rightIndex : leftIndex;
}

// pos 为nums[left,right]最小值的坐标在value中存储的位置
int query(int pos, int qleft, int qright, int left, int right) {// 递归的找到给定区间的最小值
	
	if (left > right) return -1;
	if (qright == right && qleft == left) return value[pos];// 递归出口
	int mid = (qright + qleft) / 2;
	int leftIndex = query(2 * pos, qleft, mid, left, Math.min(mid, right));//左侧区间最小值的坐标
	int rightIndex = query(2 * pos + 1, mid + 1, qright, Math.max(left, mid + 1), right);
	if (leftIndex == -1) return rightIndex;
	if (rightIndex == -1) return leftIndex;
	return nums[leftIndex] > nums[rightIndex] ? rightIndex : leftIndex;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值