容易记忆、理解的线段树

问题导入

如果有一个数组

int [] arr = {1, 3, 5, 7, 9, 11};

要进行一系列操作:
1、arr[i]加上一个数
2、求区间L-R的和

容易看出,第一个操作复杂度是O(1),第二个是O(n)

有没有更快的呢,答案是有的,利用线段树来实现复杂度为O(logn)

注:这个问题树状数组也可以完成,而且实现更加容易

线段树的构成

线段树
当需要查询区间2 - 5的和时,过程如图
线段树区间求和
当要修改arr[4]的值时,从下往上修改
线段树更新

构造线段树

为了方便用数组构造,我们将树当成是完全二叉树

构造线段树
可以看的出来,
当前节点的左儿子为 2 * node + 1,
右儿子为2 * node + 2
求树的左右儿子节点

代码实现

在代码中,

凡是带node的区间均是树的区间

凡是带node的区间均是树的区间

凡是带node的区间均是树的区间

数组则用start、end、L、R表示

建树

arr是原本的数组,tree是我们要建的线段树,node是指代表当前函数所求区间[start, end]中树的结点的位置,一开始传入build_tree(arr, tree, 0, 0 arr.length - 1)代表整个区间在树中的结点是0,剩余解析看注释

	public static void build_tree(int [] arr, int [] tree, int node, int start, int end) {
		if(start == end) {
			tree[node] = arr[start];
		}else{
			//先求左右结点对应的区间
			int mid = (start + end) / 2;
			int left_node  = 2 * node + 1;
			int right_node = 2 * node + 2;
			
			//分治的思想,先将左右结点建好,按照递归的性质,下面两行代码会一个递归到叶子结点
			//也就是第上面的if条件语句,然后开始由下往上赋值
			build_tree(arr, tree, left_node, start, mid); 
			build_tree(arr, tree, right_node, mid + 1, end);
			
			tree[node] = tree[left_node] + tree[right_node];
		}
	}
更新

可以看到其实参数跟建树类似,只是多了idx,val,分别是在原始数组中将idx位置的值改为val

	public static void update_tree(int [] arr, int [] tree, int node, int start, int end, int idx, int val) {
		if(start == end) {
			arr[idx] = val;
			tree[node] = val;
		}else {
			//同样的先求左右结点对应的区间
			int mid = (start + end) / 2;
			int left_node  = 2 * node + 1;
			int right_node = 2 * node + 2;
			
			//此时如果idx不在我们当前函数所求的区间中,就不需要递归了,其他步骤类似建树
			if(idx >= start && idx <= mid) {
				update_tree(arr, tree, left_node, start, mid, idx, val);
			}else {
				update_tree(arr, tree, right_node, mid + 1, end, idx, val);
			}
			
			tree[node] = tree[left_node] + tree[right_node];
		}
	}
查询

查询稍微复杂一点,参数多了我们所查询的区间L、R

	public static int query_tree(int [] arr, int [] tree,int node, int start, int end, int L, int R) {
		//如果当前函数所查询的区间完全偏离了要求的区间,返回0
		if(R < start || L > end) {
			return 0;
		}
		//类似的,如果包含,则直接放回,当前对应结点的值
		else if(L <= start && end <= R) {
			return tree[node];
		}
		else if(start == end) {
			return tree[node];
		}else {
			//与上面两个函数类似,不加累述
			int mid = (start + end) / 2;
			int left_node  = 2 * node + 1;
			int right_node = 2 * node + 2;
			
			int sum_left  = query_tree(arr, tree, left_node, start, mid, L, R);
			int sum_right = query_tree(arr, tree, right_node, mid + 1, end, L, R);
			
			return sum_left + sum_right;
		}
	}
所有代码
public class RMQ {

	static int MAX_LEN = 1000; //树的节点数,可以开大点
	
	public static void main(String[] args) {
		int arr[] = {1, 3, 5, 7, 9, 11}; //原始数组
		int size = arr.length;
		int [] tree = new int [MAX_LEN];
		
		build_tree(arr, tree, 0, 0, size - 1);
	}
	
	public static int query_tree(int [] arr, int [] tree,int node, int start, int end, int L, int R) {
		if(R < start || L > end) {
			return 0;
		}
		else if(L <= start && end <= R) {
			return tree[node];
		}
		else if(start == end) {
			return tree[node];
		}else {
			int mid = (start + end) / 2;
			int left_node  = 2 * node + 1;
			int right_node = 2 * node + 2;
			
			int sum_left  = query_tree(arr, tree, left_node, start, mid, L, R);
			int sum_right = query_tree(arr, tree, right_node, mid + 1, end, L, R);
			
			return sum_left + sum_right;
		}
	}
	
	public static void update_tree(int [] arr, int [] tree, int node, int start, int end, int idx, int val) {
		if(start == end) {
			arr[idx] = val;
			tree[node] = val;
		}else {
			int mid = (start + end) / 2;
			int left_node  = 2 * node + 1;
			int right_node = 2 * node + 2;
			
			if(idx >= start && idx <= mid) {
				update_tree(arr, tree, left_node, start, mid, idx, val);
			}else {
				update_tree(arr, tree, right_node, mid + 1, end, idx, val);
			}
			
			tree[node] = tree[left_node] + tree[right_node];
		}
	}
	
	public static void build_tree(int [] arr, int [] tree, int node, int start, int end) {
		if(start == end) {
			tree[node] = arr[start];
		}else{
			int mid = (start + end) / 2;
			int left_node  = 2 * node + 1;
			int right_node = 2 * node + 2;
			
			build_tree(arr, tree, left_node, start, mid);
			build_tree(arr, tree, right_node, mid + 1, end);
			
			tree[node] = tree[left_node] + tree[right_node];
		}
	}
}

可以看的出来,无论是构造、更新或者是查询,第一步都是先写

int mid = (start + end) / 2;
int left_node  = 2 * node + 1;
int right_node = 2 * node + 2;

之后都是分治,注意出口条件即可,很好记忆

注:以上均整理自B站正月点灯笼的视频

考虑可再生能源出力不确定性的商业园区用户需求响应策略(Matlab代码实现)内容概要:本文围绕“考虑可再生能源出力不确定性的商业园区用户需求响应策略”展开,结合Matlab代码实现,研究在可再生能源(如风电、光伏)出力具有不确定性的背景下,商业园区如何制定有效的需求响应策略以优化能源调度和提升系统经济性。文中可能涉及不确定性建模(如场景生成与缩减)、优化模型构建(如随机规划、鲁棒优化)以及需求响应机制设计(如价格型、激励型),并通过Matlab仿真验证所提策略的有效性。此外,文档还列举了大量相关的电力系统、综合能源系统优化调度案例与代码资源,涵盖微电网调度、储能配置、负荷预测等多个方向,形成一个完整的科研支持体系。; 适合人群:具备一定电力系统、优化理论和Matlab编程基础的研究生、科研人员及从事能源系统规划与运行的工程技术人员。; 使用场景及目标:①学习如何建模可再生能源的不确定性并应用于需求响应优化;②掌握使用Matlab进行商业园区能源系统仿真与优化调度的方法;③复现论文结果或开展相关课题研究,提升科研效率与创新能力。; 阅读建议:建议结合文中提供的Matlab代码实例,逐步理解模型构建与求解过程,重点关注不确定性处理方法与需求响应机制的设计逻辑,同时可参考文档中列出的其他资源进行扩展学习与交叉验证。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值