课程学习
关于并查集和归并排序
归并
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
常用于多个有序的数据文件归并成一个有序的数据文件。
思想方法:
(1)假设已经有两个有序数列,分别存放在两个数组s,r中;并设i,j分别为指向数组的第一个单元的下标;s有n个元素,r有m个元素。
(2)再另设一个数组a,k指向该数组的第一个单元下标。
#include <stdlib.h>
#include <stdio.h>
void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex)
{
int i = startIndex, j=midIndex+1, k = startIndex;
while(i!=midIndex+1 && j!=endIndex+1)
{
if(sourceArr[i] > sourceArr[j])
tempArr[k++] = sourceArr[j++];
else
tempArr[k++] = sourceArr[i++];
}
while(i != midIndex+1)
tempArr[k++] = sourceArr[i++];
while(j != endIndex+1)
tempArr[k++] = sourceArr[j++];
for(i=startIndex; i<=endIndex; i++)
sourceArr[i] = tempArr[i];
}
//内部使用递归
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{
int midIndex;
if(startIndex < endIndex)
{
midIndex = startIndex + (endIndex-startIndex) / 2;//避免溢出int
MergeSort(sourceArr, tempArr, startIndex, midIndex);
MergeSort(sourceArr, tempArr, midIndex+1, endIndex);
Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);
}
}
int main(int argc, char * argv[])
{
int a[8] = {50, 10, 20, 30, 70, 40, 80, 60};
int i, b[8];
MergeSort(a, b, 0, 7);
for(i=0; i<8; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
并查集
重点处理节点的连锁顺序和方向
#include <bits/stdc++.h>
using namespace std;
int fa[105];
int find(int n)
{
if(fa[n]==n) return n;
else return fa[n]=find(fa[n]);
}
struct Node{
int x,y;
}node[105];
bool check(Node a,Node b)
{
if(a.x==b.x) return 1;
else if(a.y==b.y)return 1;
else return 0;
}
set<int> cnt;
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
fa[i]=i;
cin>>node[i].x>>node[i].y;
for(int j=0;j<i;j++)
{
if(check(node[i],node[j])){
fa[find(j)]=find(i);
// cout<<fa[j]<<endl;
}
}
}
for(int i=0;i<n;i++)
{
//cout<<find(fa[i])<<endl;
cnt.insert(find(i));
}
cout<<cnt.size()-1<<endl;
}
以下为引用课程内容
并查集 每个点保存其父节点的信息 合并两个集合、查询两个元素是否在同一个集合 暴力:开数组保存元素属于的集合编号,查询
O(1)O(1),合并 O(n)O(n) 并查集:用树形结构保存集合,根节点编号作为集合编号,保存每个节点的父节点信息 Q:如何判断树根?
Q:如何取得集合编号? Q:如何合并两个集合? 此时的复杂度,查询 O(n)O(n),合并 O(1)O(1),如何优化(即降低树的高度)?
注:合并操作前需要先查询根节点信息,此处的“合并”未计算查询复杂度 加入路径压缩优化,查询
O(logn)O(logn),合并O(1)O(1) 加入按秩合并优化,查询 O(logn)O(logn),合并O(1)O(1)
加入上述两种优化,查询 O(\alpha(n))O(α(n)),合并O(1)O(1) 近似认为其查询操作的时间复杂度
O(\alpha(n)) = O(5) = O(1)O(α(n))=O(5)=O(1)
- 由于阿克曼函数 f (n) = A(n, n)f(n)=A(n,n) 的增加速率非常快,因此其反函数 f^{−1} = \alphaf −1 =α 会以非常慢的速度增加。阿克曼反函数常用 αα 表示。因为 A(4, 4)A(4,4) 的数量级约等于\large
2{{2{{10^{{19729}}}}}}2 2 10 19729
,因此对于一般可能出现的数值 nn,α(n)α(n) 均小于等于 44。 字典树 高效的存储和查找字符串集合 最大异或对 树状数组前缀和
- 每个数组下标代表一个前缀, 值为前缀的和, 然后用数组元素间做减法算出某个区间的和.填例子
- 填题目
差分
- 保存相邻值的差.例子:每个数组下标代表一个前缀和, 上一个数组下标代表的前缀和 与 当前数组下标代表的前缀和 之差, 作为当前数组元素的值.(区间插气球问题)
- 填题目
前缀和和差分是一对逆运算 前缀和 S_i=a_1+a_2+a_3+…S i =a 1 +a 2 +a 3
+… 如何计算 S_iS i 作用:快速求区间和 二维前缀和 差分:构造b数组使满足其前缀和是原数组 b 1 = a 1 b_1=a_1 b1=a1
b 2 = a 2 − a 1 b_2=a_2-a_1 b2=a2−a1 区间增减 假定a数组初始为0,每次长度一区间增减 线段树