2022暑初二信息竞赛学习笔记集锦

Day 3:新知——并查集

学习笔记
一、概念
  1. 概念:用来表示不相交集合的数据结构,处理不相交集合的合并查询问题。每个集合通过代表来区分。
  2. 操作:
    (1) FindSet (x)
    用来查找元素 x 属于哪个集合,返回集合的代表。
    (2) UnionSet (x, y)
    如果 x 、 y 属于不同集合,则将 x 、 y 所在集合进行合并,否则不进行任何操作/
  3. 实现方法:有根树表示集合。
二、基本操作
  1. 初始化 MakeSet
    father[i]表示i的父结点。
void MakeSet () {
   
	for (int i = 1; i <= n; i ++) {
   
		father[i] = i;//若父节点与自己相同,则为根结点。
	}
}
  1. 查询 FindSet
int FindSet (int x) {
   
	if (father[x] == x) {
   //同上注释
		return x;
	} else {
   
		return FindSet (father[x]);
	}
}
  1. 合并 UnionSet
void UnionSet () {
   
	if (FindSet (x) == FindSet (y)) {
   //如果x、y在同一集合,不用合并。
		return;
	}
	father[FindSet (x)] = FindSet[y];//接在y的根结点上。
}
三、并查集优化1——路径压缩

int FindSet (int x) {
   
	if (father[x] == x) {
   //同上注释
		return x;
	} else {
   
		return father[x] = FindSet (father[x]);//直接连到根结点。
	}
}
四、并查集优化2——按秩合并(启发式合并)

rank[i]维护以i结点为根的子树的深度。

void UnionSet () {
   
	int a = FindSet (x), b = FindSet (y);
	if (a == b) {
   //如果x、y在同一集合,不用合并。
		return;
	}
	if (rank[a] <= rank[b]) {
   //如果b树比a树深,把a接在b后
		father[a] = b;
	} else {
   //否则把b接在a后
		father[b] = a;
	}
	if (rank[a] == rank[b]) {
   //如果a和b一样深,根据刚才的语句是接在b后的,则b的深度要加1
		rank[b] ++;
	}
}
五、带权并查集(边带权并查集)
六、种类并查集(扩展域并查集)

Day 5:新知——树状数组

学习笔记
一、概念

树状数组(Binary Indexed Tree,简称BIT),是一个区间查询和单点修改时间复杂度降为 Θ ( log ⁡ n ) \Theta (\log n) Θ(logn)的数据结构,主要用于查询任意两点之间所有元素之和。

二、问题的提出

一个一维数组,长度为 n n n,接下来要对这个数组进行两种操作:

  • 修改,对 i i i~ j j j之间的元素增加 x x x
  • 求和,求第 i i i个元素到第 j j j个元素的和。
三、解决办法

办法一:前缀和

优点:求和: Θ ( 1 ) \Theta (1) Θ(1)
缺点:修改: Θ ( n ) \Theta (n) Θ(n)

办法二:树状数组

重点:lowbit (x)函数

求把 x x x转化成二进制后,取末尾的 1 1 1和后面的 0 0 0,再转化成十进制的值。

写法:

int lowbit (int x) {
   
	return x & -x;
}


bit[i]数组就是上图中的C[i]数组,按照上图规律:

bit[1] = a[1]
bit[2] = bit[1] + a[2]
bit[3] = a[3]
bit[4] = bit[2] + bit[3] + a[4]
...

重点:update (k, x)函数:将第k个元素的值加x。

void update (int k, int x) {
   
	for (int i = k; i <= n; i += lowbit (i)) {
   //由上图易得,第i个元素+lowbit (i)即为它的上级元素
		bit[i] += x;
	}
}

重点:sum (k)函数:求第k个元素的值。

int sum (int k) {
   
	int ans = 0;
	for (int i = k; i > 0; i -= lowbit (i)) {
   //累加差分(bit)数组即为原数
		ans += bit[i];
	}
	return ans;
}
四、离散化

Q:为什么要离散化?
A:在某些时刻,数据较大时且只需知道元素的位置而元素的值无关紧要时,可以使用离散化来简化数据的强度。

离散化方法一:用数组进行离散化

struct node {
   
	int val, id;
	bool operator < (const node x) const {
   
		return val < x.val;
	}
}
......
for (int i = 1; i <= n; i ++) {
   
	scanf ("%d", &a[i].val);
	a[i].id = i;
}
sort (a + 1, a + n + 1);
b[a[i].id] = i;

离散化方法二:用STL+二分离散化

#include <algorithm>
using namespace std;

int a[MAXN], lsh[MAXN], cnt, n;
......
for (int i = 1; i <= n; i ++) {
   
	scanf ("%d", &a[i]);
	lsh[i] = a[i];
}
sort (lsh + 1, lsh + n + 1);//排序
cnt = unique (lsh + 1, lsh + n + 1) - lsh - 1;//去重
for (int i = 1; i <= n; i ++) {
   
	a[i] = lower_bound (lsh + 1, lsh + cnt + 1, a[i]) - lsh;//返回坐标
}

Day 8:新知——哈希Hash

学习笔记
一、Hash函数

指可以根据关键字直接计算出元素所在位置的函数。

二、哈希表

根据设定的哈希函数 Hash(key) 和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的 “象” 作为记录在表中的存储位置,这种表便称为哈希表,这一映象过程称为哈希造表散列,所得存储位置称为哈希地址散列地址

三、冲突
  1. 定义:不同的元素占用同一个地址的情况叫做冲突。
  2. 发生冲突的因素
    (1) 装填因子 α \alpha α
    装填因子是指哈希表中己存入的元素个数 n n n 与哈希表的大小 m m m 的比值,即 α = n m α=\frac{n}{m} α=mn α α α越小,发生冲突的可能性越小,反之,发生冲突的可能性就越大。但是, α α α太小又会造成大量存贮空间的浪费,因此必须兼顾存储空间和冲突两个方面。
    (2)所构造的哈希函数
    构造好的哈希函数,使冲突尽可能的少。
    (3)解决冲突的方法
    设计有效解决冲突的方法 。.
四、Hash函数的构造方法
  1. 直接定址法
    取关键字或关键字的某个线性函数值为散列地址,即Hash(K)=KHash(K)=a * K + b(其中 a a a b b b为常数)。
    优点:以关键码 key 的某个线性函数值为哈希地址,不会产生冲突。
    缺点:要占用连续地址空间,空间效率低。

  2. 除后余数法 (常用)
    取关键字被不大于散列表表长 m m m 的数 p p p 除后所得的余数为哈希函数。即
    H a s h ( K ) = K m o d    p ( p ≤ m ) Hash(K) = K \mod p (p≤m) Hash(K)=Kmodp(pm)

    ps:经验得知,一般可选 p p p为质数 或 不包含小于 20 20 20的质因子的合数。例如:131, 1331, 13331, ...

  3. 平方取中法
    取关键字平方后的中间几位为哈希函数。因为中间几位与数据的每一位都相关。
    例: 2589 2589 2589的平方值为 6702921 6702921 6702921,可以取中间的 029 029 029为地址。

  4. 数字分析法
    选用关键字的某几位组合成哈希地址。
    选用原则应当是:各种符号在该位上出现的频率大致相同。

  5. 折叠法
    是将关键字按要求的长度分成位数相等的几段,最后一段如不够长可以短些,然后把各段重叠在一起相加并去掉进位,以所得的和作为地址。
    适用于:每一位上各符号出现概率大致相同的情况。
    具体方法:
    移位法:将各部分的最后一位对齐相加(右对齐)。
    间接叠加法:从一端向另一端沿分割界来回折叠后,最后一位对齐相加。
    例:元素 42751896 42751896 42751896,
    移位法: 427 + 518 + 96 = 1041 427+518+96=1041 42751896=1041
    间接叠加法: 42751896 − > 724 + 518 + 69 = 1311 427 518 96 -> 724+518+69 =1311 42751896>724+518+69=1311

  6. 随机数法
    选择一个随机函数,取关键字的随机函数值为它的哈希地址,即Hash (key) = random (key) 其中random为随机函数(random是C语言函数)。
    通常,当关键字长度不等时采用此法构造哈希函数较恰当。
    rand (): 取随机数,以默认种子1来生成,只要种子一样,无论何时何地生成的随机数都一样。
    srand (x): 将随机数的种子改为 x x x
    time (0): 获取当前时间,因为时间一直在变化,所以随机数的值也在变化。
    参考代码:

#include <cstdio>
#include <cstdlib>
#include <ctime>
using namespace std;

int main () {
   
	srand (time (0));
	printf ("%d\n", rand ());
	return 0;
}
  1. 建立Hash ()函数通常考虑的因素
    (1)计算哈希函数所需时间(包括硬件指令的因素);
    (2)关键字的长度;
    (3)哈希表的大小;
    (4)关键字的分布情况;
    (5)记录的查找频率。
五、处理冲突的办法
  1. 开放地址法
    开放地址就是表中尚未被占用的地址,当新插入的记录所选地址已被占用时,即转而寻找其它尚开放的地址。
    (1) 线性探测法
    设散列函数 Hash (K) = K mod m m m m为表长),若发生冲突,则沿着一个探查序列逐个探查(也就是加上一个增量),那么,第i次计算冲突的散列地址为:
    H i = ( H ( K ) + d i ) m o d    m ( d i = 1 , 2 , … , m − 1 ) H_i = (H(K)+d_i) \mod m (d_i=1,2,…,m-1) Hi=(H(K)+di)modm(di=1,2,,m1)
    优点:只要哈希表未被填满,保证能找到一个空地址单元存放有冲突的元素;
    缺点:可能使第 i i i个哈希地址的同义词存入第 i + 1 i+1 i+1 个哈希地址,这样本应存入第 i + 1 i+1 i+1个哈希地
    址的元素变成了第 i + 2 i+2 i+2个哈希地址的同义词,……,因此,可能出现很多元素在相邻的哈希
    地址上“堆积”起来,大大降低了查找效率。
    (2) 二次探测法
    二次探测法对应的探查地址序列的计算公式为:
    H i = ( H ( k ) + d i ) m o d    m H_i = ( H(k) + d_i ) \mod m Hi=(H(k)+di)modm
    其中 d i = 1 2 , − 1 2 , 2 2 , − 2 2 , … , j 2 , − j 2 ( j ≤ m / 2 ) d_i =1^2,-1^2,2^2,-2^2,…,j^2,-j^2 (j≤m/2) di=12,12,22,22,,j2,j2(jm/2)

  2. 链地址法
    基本思想:
    将具有相同哈希地址的记录链成一个单链表,m个哈希地址就设 m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。
    优点:插入、删除方便。
    缺点:占用存储空间多。

  3. 再哈希法
    基本思想:

H i = R H i ( k e y ) ( i = 1 , 2 , 3 , … … , k ) 。 H_i= RH_i(key) (i=1,2,3,……,k)。 Hi=RHi(key)(i=1,2,3,……,k)

其中, R H i ( ) RH_i() RHi() 均是不同的哈希函数,即在同义词产生地址冲突时计算另一个哈希函数地址,直到冲突不再发生。
优点:不易产生“聚集”。
缺点:增加了计算时间。

  1. 建立一个公共溢出区
    基本思想:
    假设哈希函数的值域为 [ 0 , m − 1 ] [0,m-1] [0,m1],则设向量 H a s h T a b l e [ 0 , m − 1 ] HashTable[0,m-1] HashTable[0,m1]为基本表。在此基础上,再建立一个溢出表,在之后的哈希操作中,无论关键字的同义词生成怎样的哈希地址,一旦发生冲突,就将其放入溢出表中。

Day 10:新知——图的概念、结构和遍历

学习笔记
一、定义

图(graph),用来存储某些具体事物和这些事物中的联系。
图由顶点(vertex)——具体事物和边(edge)——联系组成
顶点集合为 V V V,边的集合为 E E E,图表示为 G = ( V , E ) G=(V,E) G=(V,E)

二、种类
  1. 无向图:边没有指定方向的图。
  2. 有向图:边具有指定方向的图。
    注:有向图所连的边也叫做弧,一条边起点为弧头,终点为弧尾。
  3. 带权图:边上带有权值的图。
三、无向图的术语
  1. 两个顶点之间有边连接,则称两个顶点相邻。
  2. 路径:相邻顶点的序列。
  3. 圈:起点与终点重合的路径。
  4. 度:顶点连接边的条数。
  5. 树:没有圈的连通图。
  6. 森林:没有圈的非连通图。
四、有向图的术语
  1. 在有向图中,边是单向的,它们的邻接性是单向的。
  2. 有向路径:相邻顶点的序列。
  3. 有向环:一条至少含有一条边且起点和终点相同的路径。特别地,自环(见下图)
  4. 有向无环图(DAG):没有环的有向图。
  5. 度:一个顶点的出度和入度之和即为该顶点的度。
    (1) 入度:以顶点为弧尾的边的数量。
    (2) 出度:以顶点为弧头的边的数量。
五、图的表示
  1. 邻接矩阵
    对于一个有 V V V个顶点的图而言,使用 V × V V \times V V×V的二维矩阵表示
    G i , j = 1 G_{i,j}=1

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值