研究生复试上机基本题型及思路总结(图论算法专题)

研究生复试上机基本题型及思路总结(图论算法专题)

  • 一、 并查集
    • 1.1 基本原理
    • 1.2 操作方法
    • 1.3 解决问题
  • 二、最小生成树
    • 2.1 基本原理
    • 2.2 操作方法
    • 2.3 解决问题
  • 三、最短路径
    • 3.1 基本原理
    • 3.2 操作方法
    • 3.3 解决问题
  • 四、拓扑排序
    • 4.1 基本原理
    • 4.2 操作方法
    • 4.3 解决问题
      • 关于复试的其他专题:

一、 并查集

1.1 基本原理

并查集用于处理一些不交集的合并和查询问题,有两个主要的功能,一是判断任意的两个元素是否处在同一个集合,二是按照要求合并不同的集合。

1.2 操作方法

并查集的基本操作是查找(Find)和合并(Uion)。

具体的代码如下:

int father[n];	//记录父亲节点
int height[n];	//记录当前高度(用于合并)
void initial(){		//初始化,节点的父亲节点都为自己,高度都为 0 .
	for(int i = 0;i < n;++i){
		father[i] = i;
		height[i] = 0;
	}
	return;
}
int Find(int n){	//查找结点。
	if(n != father[n]){
		father[n] = Find(father[x]);	//路径压缩,如果自己不是根节点,那么找到它父亲节点的根节点当自己的根节点。
	}
	return father[n];
}
void Union(int x,int y){
	x = father[x];
	y = father[y];
	if(x != y){
		if(height[x] > height[y]){
			father[y]  = x;
			/*不用给height[x]++,因为 x 的 height 本来就大,并小的不会影响它。*/
		}else if(height[x] < height[y]){
			father[x] = y;
		}else{
			father[x] = y;
			hright[y]++;	//指定一个规则即可,也可以将 y 并入 x。
		}
	}
	return;
}

以上的代码为最优代码,即保证并查集的树状结构高度最小,保证查找操作的效率最高,在实际的使用中可以不用这么复杂,比如在 Find 的时候,取消路径压缩;在Union 时取消对高度的判断,直接并入任意一个节点,但是这样在查找最终的祖先节点的时候要加一个循环结构。

1.3 解决问题

并查集一般可以查找图的联通分量数,由此可以引申出来判断图是否联通(联通分量大于 1 不连通),还需要几条边可以让图成为连通图(联通分量数减 1 ),判断图是否为一棵树(1. 联通分量数为 1 ,2. 入度为 0 的节点有且只有 1 个,入度为 1 的节点有且只有 总结点数目 - 1 个)

e.g. 1. 连通图(判断是否联通)2.畅通工程(联通分量的个数)3. Is It a tree?(判断是否为树)

二、最小生成树

2.1 基本原理

最小生成树的生成算法主要有 Kruskal(以下简称 K ) 和 Prim 算法,因为 K 算法编写起来比较简单,而且对于稀疏图(边数比较少)的运行效率较高,所以采用 K 算法来生成最小生成树。

2.2 操作方法

K 法 最小生成树的主要原理是从边的集合中,依次选择权值最短的边,判断此边的两端顶点是否处在同一个并查集中,如果不在同一个并查集中,则加入该边合并两端顶点,如果在同一个并查集中,那么就舍弃该边。边的集合遍历完后,如果所有的顶点都在一个并查集中,那么最小生成树已经生成,如果不在同一个并查集中,那么最小生成树不存在。

具体的代码如下:

struct edge{
	int start;
	int end;
	int lenght;
}
bool sort_edge(edge a,edge b){
	/*按照边长度从小到大排序*/
}
int height[n];
int father[n];
edge Edge[n][n];	//存储边。

void initival(){
/*初始化,同上方代码*/
}
int Find(int x){
/*返回父亲节点*/
}
int Union(int x,int y){
/*合并节点*/
}
void Kruskal(int n,int edgeNumber){
	initival(n);
	sort(Edge,Edge + edgeNumber,sort_edge);
	for(int i = 0;i < edgrNumber;++i){
		edge current = edge[i];
		if(Find(current.start) != Find(current.end)){\
			/*需要这条边,按照题目要求自定义操作*/
		}
	}
	int num = 0;
	for(int i = 0;i < n;++i){
		if(i = father[i]) num++;	//判断联通分量个数
	}
	if(num == 1){
		/*有最小生成树*/
	}else	//否则不存在。
}

2.3 解决问题

解决一切跟最小生成树有关的问题。

e.g. 1. 还是畅通工程 2. 继续畅通工程

三、最短路径

3.1 基本原理

迪杰斯特拉(Dijkstra)算法可以用来解决单源最短路径问题,在运行过程中将所有的源点 V 分为集合 S 和 T,S 中包含已经确定的点,初始只包含源点,T中包含未被确定的点。从 T 中选出源点到当前点最近的那个点 u ,将 u 加入 S 中,然后对所有从 u 出发的点进行松弛操作,直到集合 T 为空,S 为 V 为止。

3.2 操作方法

从 T 中选择最近的顶点 u 时,并不需要遍历所有的顶点,可以用一个优先级队列来实现。

具体的代码如下:

struct Edge{
	int to;
	int length;
	Edge(int t,int l):to(t),length(l){}
};
struct Point(){
	int number;
	int distance;
	Point(int n,int d):number(n),distance(d){}
	friend operator < (Point a,Point b){	//重载 < 运算符,实现优先队列排序。
		a.distance > b.distance;
	} 
};

vector<Edge> graph[maxn];	//邻接表实现的图。
int dis[maxn];	//存储从 S 到各点的最短路径。

void Dijkstra(int s){
	priority_queue<Point> q;
	dis[s] = 0;		//源点到自身的距离为 0 。
	q.push(Point(s,dis[s]));	//压入队列。
	while(!q.empty()){
		int u = q.top().number;		//获取与源点距离最近的点。
		q.pop();
		for(int i = 0;i < graph[u].size;++i){	//松弛所有以 u 为起点的路径。
			int v = graph[u][i].to;
			int d = graph[u][i].length;
			if(dis[v] > dis[v] + d){
				dis[v] =  dis[u] + d;
				q.push(Point(v,dis[v]));
			}
		}
	}
	return;
}	

3.3 解决问题

所有需要用最短路径解决的问题。图中点与点之间的最短路径,最小代价等。

e.g. 1. 最短路径问题

四、拓扑排序

4.1 基本原理

从图中选取入度为 0 的节点;删除该顶点并删除所有以它为起点的边;重复以上过程直到所有的点都被删除或者图中没有入度为 0 的点,说明不存在拓扑序列。

4.2 操作方法

使用一个数组来储存各个顶点的入度,当入度为 0 时,将该顶点存入栈(或者队列)中。

具体的代码如下:

vector<int> graph[maxn];	//邻接表存储图。
int indegree[maxn];

bool topo(int n){
	queue<int> node;
	for(int i = 0;i < n;++i){
		if(indegree[i] == 0){	//寻找第一个入度为 0 的点,放入队列中。
			node.push[i];
		}
	}
	int num = 0;
	while(!node.empty()){
		int u = node.front();
		node.pop();
		for(int i = 0;i < graph[u].size();++i){
			int v = graph[u][i];
			indegree[v]--;
			if(ingree[v] == 0) node.push(v);
		}
	}
	return num == n;
}

4.3 解决问题

e.g. 1. 确定比赛名次等。

关于复试的其他专题:

简单思路
动态规划
搜索专题

你可能感兴趣的:(考研复试上机)