数据结构
并查集
int p[N];//p[1]=2意思是节点1的父亲是2,祖宗节点的父节点设置为自身,即p[x]=x
int v[N];//可以存储每个集合的值
v[find(i)]=1;//i结点所在集合值为1
int find(int x)
{
if(p[x]!=x)return p[x]=find(p[x]);
return p[x];
}
//***初始化
for(int i=1;i<=n;i++)p[i]=i;
//***合并x和y
p[find(x)]=find(y);
//***判断是否在一个集合
find(x)==find(y);
//***最后可以执行下面代码进行最终路径压缩
for(int i=1;i<=n;i++)int t=find(i);
//***通过并查集求解连通块数量
for(int i=1;i<=n;i++)p[i]=i;
int cnt=n;//初始连通块数量为结点个数
if(find(a)!=find(b))
{
p[find(a)]=find(b);
cnt--;//连通块数量减一
}
//***定义边结构体存储边,然后循环每条边进行合并集合操作
int m//边数
struct e
{
int a,b;
}
for(int i=0;i<m;i++)
{
int a=e[i].a,b=e[i].b;
int pa=p[a],pb=p[b];
if(pa!=pb)pa=pb;//根据边联通节点
}
//***判断树中有无环
//不断插入有向边a->b
int root1 = find(a), root2 = find(b);
if (root1 != root2) s[root2] = root1;
else flag = false;//说明两个点已经在一个集合了,再插入边就会出现环(回路)
单链表
//head 表示头结点的下标
//e[i] 表示结点i的值
//ne[i] 表示结点i的next指针是多少
//idx 存储当前已经用到了哪个点
//-1 表示空结点
int head,e[N],ne[N].idx;
/*********初始化*********/
void init()
{
head=-1;
idx=0;
}
/*********将x插到头结点*********/
void add_to_head(int x)
{
e[idx]=x;
ne[idx]=head;
head=idx;
idx++;
}
/*********将x插到下标为k的点后面*********/
void add(int x,int k)
{
e[idx]=x;
ne[idx]=ne[k];
ne[k]=idx;
idx++;
}
/*********将下标为k的后面的点删除*********/
void remove(int k)
{
ne[k]=ne[ne[k]];
}
/*********输出链表*********/
for(int i=head;i!=-1;i=ne[i])cout<<e[i];
双链表
int e[N],l[N],r[N],idx;
void init()
{
//0表示左端点,1表示右端点
r[0]=1;
l[1]=0;
idx=2;
}
//在下标为k的点的右边插入x
void add(int k,int k)
{
e[idx]=x;
r[idx]=r[k];
l[idx]=k;
l[r[k]]=idx;
r[k]=idx;
}
//删除第k个点
void remove(int k)
{
r[l[k]]=r[k];
l[r[k]]=l[k];
}
邻接表/树
int h[N];//存储每个头结点
int e[M],ne[M],idx;//存储结点的值和next指针
int w[M];//存储边的权值
void init()//初始化邻接表
{
memset(h,-1,sizeof(h));
}
void add(int a,int b,int t)//插入从a到b的边,且边权为t
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=t;
h[a]=idx++;
}
for (int i = h[u]; ~i; i = ne[i])//遍历一个节点的所有边
dfs(e[i], depth + 1);
for(int i=0;i<idx;i++)
int a=e[i^1],b=e[i];// 对于无向图,取出方向相反的一对边,因为每次加边都是成对添加,相反方向的两个边编号的奇偶有特殊关系