新征程2.16 数据结构(二)

虽然标题写的是“数据结构(二)”
但可能不会有一了,毕竟上次讲课已是放假前的事了,有些东西记不太全。等我复习的时候再补上。


链式前向星和vector

首先,在研究星球大战问题中,引入了两种常用的建图方法,即链式前向星和vector(邻接表)。这两种方法可以帮助我们更好地研究并查集问题。

链式前向星和vector都是常用的数据结构,用于存储和操作图或者其他需要动态增删元素的数据。

vector是C++标准库中的一个容器类,它可以动态地调整大小,并且支持随机访问。vector内部使用数组来存储元素,当元素数量超过当前容量时,vector会自动重新分配更大的内存空间,并将原有元素复制到新的内存中。vector的主要优点是支持快速的随机访问和尾部插入操作,同时也可以在常数时间内在尾部删除元素。因此,vector常用于需要频繁访问和修改元素的场景。

链式前向星(Linked Forward Star)是一种用于表示稀疏图的数据结构。它通过使用链表来存储每个顶点的邻接边,以实现高效的图遍历和查询操作。链式前向星的主要优点是节省空间,因为它只存储实际存在的边,而不是所有可能的边。同时,它也支持动态增删边的操作,使得图的修改更加方便。

(摘自链式前向星和vector


链式前向星 代码:
void add(int x,int y)  //新增x到y的单向边
{
  ++tot;
  to[tot]=y;  //指向y
  nt[tot]=head[x]; //tot的这一条件是之前的(从x出去的)最后一条边(head[x])
  head[x]=tot;  //更新(从x出去的)最后一条边
}
void bianli(int x)
{
  for(int i=head[x];i;i=nt[i])
  {
  内容 
  }
}
int main()
{
  cin>>n>>m;
  for(int i=1,x,y;i<=m;++i)
  {
     add(x,y);add(y,x);
  }
vector 代码:
vector<int>v[100];
int main()
{
  for(int i=1,x,y;i<=m;++i)
  {
    v[x].push.back(y);
    v[y].push.back(x);
  }

最小生成树

之前做过板子题,感觉一般,不如线段树
两种方法Prim和Kruskal,两者区别:Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。Prim是以更新过的节点的连边找最小值,Kruskal是直接将边排序。两者其实都是运用贪心的思路。
我个人比较喜欢Kruskal(简单且方便
这是我的代码

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct node
{
	int x;int y;int z;
};
node a[200010];
int zx,zy,f[5010],sum,cnt;
int n,m;
int find(int x)
{
    if(f[x]==x)
    return x;
    else return find(f[x]);
}
bool cmp(node a,node b)
{
	return a.z<b.z;
}
void krusal()
{
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		zx=find(a[i].x);
		zy=find(a[i].y);
		if(zx==zy)
		{
			continue;
		}
		sum=a[i].z+sum;
		f[zx]=zy;
		if(++cnt==n-1)
		{
			break;
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		f[i]=i;
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
	}
	krusal();
	if(cnt==n-1)
	{
		printf("%d",sum);
	}
	else printf("orz");
	return 0;
}

然后是哈希表,哈希表的基本思想是将记录的存储位置与它的关键字之间建立一个确定的关系H,使每个关键字和唯一的存储位置对应,相当于给一个关键字设置了唯一且不重复的密码,只有正确输入,才能得到判断正确。详见字符串哈希

#include<bits/stdc++.h>
#define ULL unsigned long long

using namespace std;

int n,ans,len;
ULL now,zhi=112453531;
char ch[1514];
set<ULL>s;
int main()
{
	cin>>n;
	while(n--)
	{
		scanf("%s",ch+1);len=strlen(ch+1);
        now=1;for(int i=1;i<=len;++i)now=now*zhi+(ULL)ch[i];
        if(s.count(now))continue;
        else s.insert(now),++ans;
	}
	cout<<ans;
	return 0;
}

差分

在做树状数组2时,用到了这个思想,差分与之前讲过的前缀和一样,更多是一种优化的思想,一种数学规律,甚至可以说前缀和与差分是一个相伴相生的关系,可以理解为“一对情侣”。

如果我将a数组中任意一个位置元素的值加一个数x,那么当前位置及之后前缀和数组都的值都会比原先多x,相同的,如果我将a数组中的任意一个位置元素的值减一个数y,那么当前位置及之后的前缀和数组的的值都会比原先少y,应用这个特点,如果在上图中s数组内的第2到第4个值都加一个数x,那么先让a数组内的第2个值加x,这样前缀和数组中后续每一个数都会比之前多x,但根据要求第4个值以后将不能再多x,所以我们同样也要让a数组中第5个值减x,这样则可以达到我们想要的结果

也就是说$ b[i]=a[i]-a[i-1];(a[0]=0;),\(那么\) a[i]=b[1]+....+b[i]; $


树状数组

本来想补的,没想到拖到现在。(\(2024.4.3\)

posted @ 2024-04-03 16:08  deviancez  阅读(24)  评论(0)    收藏  举报