ACM学习笔记_week6&7(待补充)


前言

事情太多多多多了然后就没时间练题了,先在这写一下知识点叭。


一、树和图的存储以及相应的DFS、BFS(记忆化搜索)

  • 树是n个结点的有限集合(可以是空集),在任一棵非空树中:
    (1)有且仅有一个称为根的结点。
    (2)其余结点可分为互不相交的子集,而且这些子集本身又是一棵树,称为根的子树。

  • 图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G=(V,E)
    其中:G表示一个图,V是图G中顶点的集合,E是图G中顶点之间边的集合。

    树是特殊的图,存储方式同图。而图可分为有向图和无向图,有向图又是特殊的无向图,所以主要应该掌握无向图的存储。图的存储分为邻接矩阵存储和邻接表存储。稠密图使用邻接矩阵存储,稀疏图使用邻接表存储。

  • 邻接矩阵存储(数组表示法)
    基本思想:
    首先,先用一个一维数组存储图中顶点的信息,再用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。
    最近数据结构的课正好在上这一部分,就用数据结构的写法好了。

//邻接矩阵存储无向图的类
const int MaxSize=10;
template <class T>
class Mgraph{
public:
	MGraph(T a[ ], int n, int e );
	~MGraph( )void DFSTraverse(int v);
	void BFSTraverse(int v);
private:
	T vertex[MaxSize];
	int arc[MaxSize][MaxSize];
	int vertexNum, arcNum;
};
//构造函数
template <class T>
MGraph::MGraph(T a[ ], int n, int e) {
vertexNum=n; arcNum=e;
for (i=0; i<vertexNum; i++)
	vertex[i]=a[i];
for (i=0; i<vertexNum; i++) //初始化邻接矩阵
	for (j=0; j<vertexNum; j++)
		arc[i][j]=0;
for (k=0; k<arcNum; k++) {
	cin>>i>>j; //边依附的两个顶点的序号
	arc[i][j]=1; //arc[j][i]=1; //置有边标志
	}
}
//DFS
template <class T>
void MGraph::DFSTraverse(int v)
{
cout<<vertex[v]; visited [v]=1;
for (j=0; j<vertexNum; j++)
	if (arc[v][j]==1 && visited[j]==0)
		DFSTraverse( j );
}
//BFS
int visited[MaxSize];
template <class T>
void MGraph::BFSTraverse(int v){
	Queue Q; //假设采用顺序队列且不会发生溢出
	cout<<vertex[v]; visited[v]=1; Q.EnQueue(v);
	while (!Q.IsEmpty())
	{
		Q.DeQueuer(v);
		for (j=0; j<vertexNum; j++)
			if (arc[v][j]==1 && visited[j]==0 ) {
				cout<<vertex[j]; 
				visited[j]=1; 
				Q.EnQueue(j);
				}
	}
}
  • 邻接表存储
    基本思想:
    对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(对于有向图则称为出边表),所有边表的头指针和存储顶点信息的一维数组构成了顶点表。
    邻接表有两种结点结构:顶点表结点和边表结点。
    在这里插入图片描述
    vertex:数据域,存放顶点信息。
    firstedge:指针域,指向边表中第一个结点。
    adjvex:邻接点域,边的终点在顶点表中的下标。
    next:指针域,指向边表中的下一个结点。
struct ArcNode
{
	int adjvex;
	ArcNode *next;
};
template <class T>
struct VertexNode
{
	T vertex;
	ArcNode *firstedge;
};
//邻接表存储有向图的类
const int MaxSize=10; //图的最大顶点数
template <class T>
class ALGraph
{
public:
	ALGraph(T a[ ], int n, int e);
	~ALGraph;
	void DFSTraverse(int v);
	void BFSTraverse(int v);
private:
	VertexNode adjlist[MaxSize];
	int vertexNum, arcNum;
};
//构造函数
template <class T>
ALGraph::ALGraph(T a[ ], int n, int e)
{
	vertexNum=n; arcNum=e;
	for (i=0; i<vertexNum; i++)
	{
		adjlist[i].vertex=a[i];
		adjlist[i].firstedge=NULL;
	}
	for (k=0; k<arcNum; k++)
	{
		cin>>i>>j;
		s=new ArcNode; s->adjvex=j;
		s->next=adjlist[i].firstedge;
		adjlist[i].firstedge=s;
	}
}
//DFS
template <class T>
void ALGraph::DFSTraverse(int v)
{
	cout<<adjlist[v].vertex; visited[v]=1;
	p=adjlist[v].firstedge;
	while (p!=NULL)
	{
		j=p->adjvex;
		if (visited[j]==0) DFSTraverse(j);
		p=p->next;
	}
}
//BFS
template <class T>
void ALGraph::BFSTraverse(int v){
	Queue Q;
	cout<<adjlist[v].vertex; visited[v]=1; Q.EnQueuer(v);
	while (!Q.IsEmpty( )) {
		Q.DeQueue(v); p=adjlist[v].firstedge;
		while (p!=NULL) {
			j= p->adjvex;
			if (visited[j]==0) {
				cout<<adjlist[j].vertex; 
				visited[j]=1; 
				Q.EnQueuer(j);
			}
			p=p->next;
		}
	}
}

二、最短路迪杰斯特拉算法和其优化

基本思想:

  1. 设置一个集合S存放已经找到最短路径的顶点,S的初始状态只包含源点v;
  2. 对vi∈V-S,假设从源点v到vi的有向边为最短路径。
  3. 以后每求得一条最短路径v, …, vk,就将vk加入集合S中,并将路径v, …, vk , vi与原来的假设相比较,取路径长度较小者为最短路径。
    重复上述过程,直到集合V中全部顶点加入到集合S中。
void Dijkstra(MGraph g, int v){
	for ( i =0; i<g.vexnum ; i++){
		dist[i]=g.arcs[v][i];
		if ( dist[i]!= MAX) path [i]=g.vertex[v]+g.vertex[i];
		else path[i]=“”;
	}
	S[0]=g.vertex[v]; dist[v]=0;
	num=1;
	While (num<g.vextexNum;i++){
	k=0;
	for(i=0;i<G.vertexNum;i++){
		if((dist[i]<dist[k] && dist[i]!=0) k=i;
			cout<<dist[k]<<path[k];
			s[num++]=G.vertex[k]; dist[k]=0;
	for(i=0;i<G.vertexNum;i++)
		if(dist[k]+g.arc[k][i]<dist[i] {
			dist[i]=dist[k]+g.arc[k][i];
			path[i]=path[k]+g.vertex[i];
		}
	}
}

优化

三、最小生成树算法

  • Prim算法(加点法)
    适合稠密图的最小生成树。
Void prime(MGraph G){
for(int i=1;i<G.vertexNum;i++){
	lowcost[i]=G.arc[0][i]; 
	adjvex[i]=0;
	}
lowcost[0]=0;
for(i=1;i<G.vertexNum;i++){
	k=MinEdge(lowcost,G.vertexNum);
	cout<<K<<adjvex[k]<<lowcost[k];
	lowcost[k]=0;
	for(j=1;j<G.vertexNum;j++)
		if(G.arc[k][j]<lowcost[j]){
			lowcost[j]=G.arc[k][j];
			arjvex[j]=k;
		}
	}
}
  • Kruskal算法(加边法)
    适合稀疏图的最小生成树。
在这里插入代码片

四、并查集原理以及应用

以2017年天梯赛决赛L2-4 部落 为例
AC代码

#include <bits/stdc++.h>

using namespace std;
int a[10010];
int vis[10010];

int fd(int x)
{
    if(a[x]==x)
    {
        return x;
    }
    else
    {
        return a[x]=fd(a[x]);
    }
}
void join(int x,int y)
{
    int xx=fd(x);
    int yy=fd(y);
    if(xx!=yy)
    {
        a[xx]=yy;
    }
}
int main()
{
    int i,k,m,p,n,count1=0,count2=0;
    int r,s,t;
    cin>>n;
    for(i=1;i<10010;i++)
    {
        a[i]=i;
        vis[i]=0;
    }
    for(i=0;i<n;i++)
    {
        cin>>k;
        cin>>m;
        vis[m]=1;
        k--;
        while(k--)
        {
            cin>>p;
            vis[p]=1;
            join(m,p);
        }
    }
    for(i=0;i<10010;i++)
    {
        if(vis[i]==1)
        {
            count1++;
        }
    }
    cout<<count1<<" ";
    for(i=1;i<=count1;i++)
    {
        if(a[i]==i)
        {
            count2++;
        }
    }
    cout<<count2<<endl;
    cin>>r;
    while(r--)
    {
        cin>>s>>t;
        if(fd(s)==fd(t))
        {
            cout<<"Y";
        }
        else
        {
            cout<<"N";
        }
        if(r!=0)
        {
            cout<<endl;
        }
    }
    return 0;
}

,另外,对于路径可进行压缩

int found(int x)
{
    int r=x;
    while(r!=pre[r])
    {
        r=pre[r];
    }
    int i=x,j;
    while(i!=r)//路径压缩
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}

五、拓展:LCA最近公共祖先

总结

以上就是所学内容,本文仅仅简单介绍了相关算法,至于应用还需要更多练习。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值