【算法】并查集,前缀和数组,差分数组(学习记录)

今天主要学习并查集,前缀和数组和差分数组,写题的同时巩固动态规划。

并查集

我看了一篇关于并查集的博客,写的特别好,通过这篇博客理解了并查集,所以借用这篇博客中提到一些概念来做知识的复习总结。

(博客指路:【算法与数据结构】—— 并查集-优快云博客

并查集解题的几个关键点:根节点合并查询

拿一道模版题来解释:

解这道题,我们需要有判断两个数是否在同一个集合的操作,以及合并两个集合的操作。

判断两个数是否在同一个集合的操作

1. 判断是否在同一个集合只需要看是否都拥有同一个根节点。

2. 既然要判断,就必须要找一下两个数的根节点是啥,这里就用到 find( ) 函数。同时要注意为了避免超时,要在 find( ) 函数中做一个路径压缩

路径压缩可以用递归完成,下面给出模版代码(解释在代码注释中):

//这里优化find函数,实现路径压缩
int find(int a) {
	if (pre[a] == a) {
		return a;
	}
	return pre[a] = find(pre[a]);//理解这一步,是将所有路过的节点的直接上级改为根节点。同时返回所需要的根节点
}

合并两个集合

这里就是将两个集合的根节点统一一下。例如 : pre[ x ] = y,pre[ m ] = n (这里的意思可以理解为,x的根节点为y,m的根节点为n)要合并只需要从y和n中选任意一个根节点当做合并后的根节点即可,例如将y当做合并后合集的根节点,就写成 pre[ m ] = y  

给出模版代码:

if (z == 1) {
//中间为具体操作
	int fx = find(x);
	int fy = find(y);
//如果两个集合的根节点不一样的话,就需要合并集合操作
	if (fx != fy) {
		pre[fx] = fy;
	}
}

有了以上两个核心操作之后,代码基本就没问题了。

判断两个是否在同一个集合,只需要用 find( ) 函数找一下根节点,判断两个根节点是否相同就行。

下面给出完整代码:

#include<stdio.h>

//这里优化find函数,实现路径压缩
int find(int a) {
	if (pre[a] == a) {
		return a;
	}
	return pre[a] = find(pre[a]);//理解这一步,是将所有路过的节点的直接上级改为根节点。同时返回所需要的根节点
}

int pre[200000];//这个数组其实就是涵盖了自己和上级的内容
int main() {
	int N = 0;
	int M = 0;
	int x, y, z;
	scanf("%d%d", &N, &M);
	
	//在还没有任何合并操作前,自己就是自己的教主(根节点)
	for (int i = 1; i <= N; i++) {
		pre[i] = i;
	}
	while (M) {
		scanf("%d%d%d", &z, &x, &y);
		//合并操作(找到教主,合并一下)
		if (z == 1) {
			int fx = find(x);
			int fy = find(y);
			if (fx != fy) {
				pre[fx] = fy;
			}
		}
		else if (z == 2) {
			if (find(x) == find(y)) {
				printf("Y\n");
			}
			else {
				printf("N\n");
			}
		}
		M--;
	}

	return 0;
}

前缀和数组,差分数组

这里将两个数组放在一起总结。

前缀和数组

可以在求连续一部分数列的和时节省时间。

prefix[ i ] = prefix[ i-1 ] + num ( 当前项的前缀和 = 前一项的前缀和 + 当前项 )

将每个前缀和求出,得到一个前缀和数组,在之后要取一段连续部分的和的时候就很方便。

直接使用prefix[ R ] - prefix[ L-1 ]即可。

下面给出模版代码:

    for (int i = 1; i <= n; i++) {
        prefix[i] = prefix[i - 1] + flo[i];
      
    }

差分数组

差分数组的出现,可以便于将一个连续区间的所有数进行增减操作。

这边不总结具体原理,可以去推荐博客复习

(前缀和数组和差分数组都在同一篇:【算法与数据结构】—— 前缀和与差分_前缀和差分为什么书上没有-优快云博客

首先需要构建一个原始的差分数组( 差分 = 当前项 - 前一项 ),然后对于一个区间,例如x~y这个区间的所有数进行增减操作,那么就将差分数组中x的位置和y后面的一个位置做相应的增减操作。

当增减操作完毕后,需要将得到新的数组,(新项 = 新项的前一项 + 对应位置的差分)

代码模版:

//这个是差分数组的创建
for(int i=1; i<=n; i++)
{
	scanf("%d",&ary[i]);
	subfix[i]=ary[i]-ary[i-1];
}
// 执行m次操作 
	for(int i=m;i>0;i--){
		scanf("%d%d%d",&l,&r,&v);
		subfix[l] += v;
		subfix[r+1] -= v;
	}
	// 新数组
	for(int i=1;i<=n;i++){
     
      ary[i] = ary[i-1] + subfix[i];

} 

### 前缀和差分算法概述 前缀和差分是两种常用的优化区间操作的技术。通过构建辅助数据结构,可以显著降低查询或更新特定范围内的元素所需的时间复杂度。 #### 一维前缀和数组的构造方法 对于给定的一维数组 `a` ,其对应的前缀和数组 `prefix_sum` 可以按照如下方式定义: \[ \text{prefix\_sum}[i] = a[0] + a[1] + ... + a[i-1] \] 这意味着要获得任意位置 i 的前缀和只需累加至该索引之前的所有数值[^3]。 ```cpp vector<int> compute_prefix_sum(vector<int>& nums) { int n = nums.size(); vector<int> prefix_sum(n + 1, 0); for (int i = 1; i <= n; ++i) { prefix_sum[i] = prefix_sum[i - 1] + nums[i - 1]; } return prefix_sum; } ``` 此代码片段展示了如何创建一个基于输入向量 `nums` 的前缀和数组 `prefix_sum` 。注意这里额外分配了一位空间来简化边界条件处理。 #### 使用前缀和计算子数组之和 有了上述前缀和之后,在求解某一段连续序列(比如从下标 l 到 r)内所有数目的总和时变得非常容易: \[ sum(l,r)=\text{prefix\_sum}[r+1]-\text{prefix\_sum}[l] \] 其中 \(sum(l,r)\) 表达的是原数组中第 l 至 r 项之间的累积值。 #### 构建并应用一维差分数组 当面对频繁修改某些区间的场景时,差分就显得尤为重要了。设有一初始全零的一维差分数组 `diff[]`, 对于每次增加区间 `[L,R]` 上面加上增量 d ,只需要做两次单独的操作 : - 将 diff[L]+=d ; - 如果 R< 数组长度,则将 diff[R+1]-=d ; 最终的结果可以通过对整个差分数组执行一次简单的前缀和运算得到实际的变化情况[^1]。 ```cpp void apply_diff(int L, int R, int d, vector<int>& diff){ diff[L] += d; if(R + 1 < diff.size()){ diff[R + 1] -= d; } } // 应用完所有的差异后再转换回原始数组形式 for (size_t i = 1; i < result.size(); ++i) { result[i] += result[i - 1]; } ``` 这段 C++ 代码实现了对指定范围内增加值的功能,并说明了怎样把经过多次调整后的差分映射还原成真实的变动结果。 #### 多维度扩展 除了基本的一维情形外,这些概念同样适用于更高维度的数据集上。例如二维情况下既存在行方向上的前缀和也存在着列方向上的版本;而相应的多维差分则允许更复杂的区域更新模式[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值