图-基础算法题

0.基础知识总结

常见概念

  • 强连通图:有向图中存在一条经过所有结点的回路
  • 单向连通图:有向图中存在一条经过所有结点的路
  • 弱连通图:有向图的基图是连通图
  • 有向完全图:有向图的任意顶点到其余各点均有一条弧
  • 无向完全图:有向图的任意两个顶点均有一条边

经典算法总结

算法时间复杂度
基于邻接表的 DFS / BFS / topological O ( n + e ) O(n+e) O(n+e)
基于邻接矩阵的 DFS / BFS / topological O ( n 2 ) O(n^2) O(n2)
Prim O ( n 2 ) O(n^2) O(n2)
Kruskal O ( e l o g 2 e ) O(elog2e) O(elog2e)
Dijkstra O ( n 2 ) O(n^2) O(n2)
Floyd O ( n 3 ) O(n^3) O(n3)
  • Prim:首先任选一结点作为起点,每次迭代在满足一端已选,一端未选的边选择一条最小边
  • Kruskal:每次迭代选择一条最小边,该边与已选边不构成回路
  • Dijkstra:每次迭代先更新距离表,然后从未选结点中选择距离起点最短的结点
  • Floyd A i j ( k ) = A i k ( k − 1 ) + A k j ( k − 1 ) A_{ij}^{(k)} = A_{ik}^{(k-1)} + A_{kj}^{(k-1)} Aij(k)=Aik(k1)+Akj(k1)

图的邻接矩阵存储

  • 无向图的邻接矩阵一定是对称矩阵,有向图的邻接矩阵不一定是对称的
  • 有向图的邻接矩阵的每行表示某个结点的出度,每列表示入度

图的邻接表存储

#include <iostream>
#include <vector>

template <typename T> struct Vertex {
    T data;
    struct Edge<T>* firstEdge;
    
    Vertex() {}
    Vertex(const T& data, Edge<T>* edge = NULL) 
        : data(data), firstEdge(edge) {}
};

template <typename T> struct Edge {
    T data;
    int weight;
    struct Edge<T>* nextEdge;
    
    Edge() {}
    Edge(const T& data, int weight = 0, Edge<T>* edge = NULL) 
        : data(data), weight(weight), nextEdge(edge) {}
};

template <typename T> class Graph {
private:
    std::vector<Vertex<T>*> vertices;
    std::vector<bool> visited;
public:
    int firstadj(int v) {}
    int nextadj(int v, int w) {}
    void dfs(int v) {}
    void bfs(int v) {}
};

DFS算法

  • 基于邻接表的深度遍历
void Graph::dfs(int v) {
    visited[v] = true;
    int w = firstadj(v);
    while(w != 0) {
        if (visited[w] == false)
            dfs(w);
        w = nextadj(v, w);
    }
} 
  • 基于邻接矩阵的深度遍历
void Graph::dfs(int v) {
    visited[v] = true;
	for(int w = 1; w <= n; w++) {
		if (matrix[v][w] != 0 && visited[w] == false)
            dfs(w);
	}
} 

1.图的统计问题

无向图的顶点数

void dfs(int v, int& vertex) {
    vertex++;
    visited[v] = true;
    int w = firstadj(v);
    while(w != 0) {
        if (visited[w] == false)
            dfs(w, vertex);
        w = nextadj(v, w);
    }
}

int vertexCount() {
    int count = 0;
    for (int i = 1; i <= n; i++)
        visited[i] = false;
    for (int i = 1; i <= n; i++) {
        if (visited[i] == false) {    
            dfs(i, count);
        }
    }
    return count;
}

无向图的边数

void dfs(int v, int& edge) {
    visited[v] = true;
    int w = firstadj(v);
    while(w != 0) {
        edge++;
        if (visited[w] == false)
            dfs(w, edge);
        w = nextadj(v, w);
    }
} 

int edgeCount() {
    int count = 0;
    for (int i = 1; i <= n; i++)
        visited[i] = false;
    for (int i = 1; i <= n; i++) {
        if (visited[i] == false) {    
            dfs(i, count);
        }
    }
    return count / 2;
}

无向图的连通分量数

int segmentCount() {
    int count = 0;
    for (int i = 1; i <= n; i++)
        visited[i] = false;
    for (int i = 1; i <= n; i++) {
        // 每遇到一个未访问结点,统计结果加1
        if (visited[i] == false) {    
            count++;
            dfs(i);
        }
    }
    return count;
}

2.图的连通性问题

判断是否连通

  • 判断无向图G是否连通
  • 等价问题:判断有向图G中顶点 v 0 v_0 v0 到每个顶点是否都有路径
bool isConnected() {
    for (int i = 1; i <= n; i++)
        visited[i] = false;
    dfs(1);
    for (int i = 1; i <= n; i++) {
        if (visited[i] == false)
            return false;
    }
    return true;
}

判断是否有路径

判断无向图的结点 v i v_i vi v j v_j vj 是否有路径

bool hasPath(int vi, int vj) {
    for (int i = 1; i <= n; i++)
        visited[i] = false;
    dfs(vi);
    return visited[vj];
}

判断关节点 ★

  • 在无向图G中,删除结点 v 0 v_0 v0 后,图是否被分割成2个以上的连通分量
  • 镜像问题:在无向图G中,删除结点 v 0 v_0 v0 后,图是否仍然连通
bool isJoint(int v0) {
    for (int i = 1; i <= n; i++) {
        visited[i] = false;
    }
    visited[v0] = true;           // 先设置v0已被访问过,把路堵住
    int w = firstadj(v0);  		  
	dfs(w);						  // 再从v0的某一个邻接点w开始深度遍历					
    w = nextadj(v0, w);
    while (w != 0) {
        if (visited[w] == false)  // 如果v0有邻接点未被访问,说明是关节点
            return true;
        w = nextadj(v0, w);
    }
    return false;
}

3.图是否为树

判断无向图是否为一棵树

  • 图用邻接表存储:从任意一点开始 dfs,若能访问到 n 个结点和 n-1 条边,则为树
void dfs(int v, int& vn, int& en) {
    vn++;
    visited[v] = true;
    int w = firstadj(v);
    while(w != 0) {
        en++;
        if (visited[w] == false)
            dfs(w, vn, en);
        w = nextadj(v, w);
    }    
}

bool isTree() {
    int vn = 0, en = 0, n = nodes(G);
    dfs(1, vn, en);
    return vn == n && en == 2 * (n - 1);
}

判断有向图是否为以v0为根的有向树

  • 图用邻接表存储:从 v 0 v_0 v0 开始 bfs,比较访问的结点数和图的结点数:如果等于,说明是有向树;如果大于,说明存在环;如果小于,说明存在多个连通分量
bool isDirectedTree(int v0) {
    int count = 1;
    Queue queue; queue.enqueue(v0);
    while(!queue.isEmpty()) {
        int v = queue.dequeue();
        int w = firstadj(v);
        while(w != 0) {    // 不再判断结点是否被访问过,只要搜素到就直接入队
            queue.enqueue(w);
            count++;
            w = nextadj(v, w);
        }
    }
    return count == nodes(G);
}
  • 图用邻接矩阵存储:如果 v 0 v_0 v0 的入度为0,其余点入度均为1,则为树
bool isDirectedTree(int v0) {
	for (int i = 1; i <= n; i++) {
		if (matrix[i][v0] != 0)
			return false;
	}
	for (int i = 1; i <= n && i != v0; i++) {
		int indegree = 0;
		for (int j = 1; j <= n; j++) {
			if (matrix[j][i] != 0)
				indegree++;
		}
		if (indegree > 1)
			return false;
	}
	return true;
}

4.结点距离问题

求距离v0最远的一个结点

  • v 0 v_0 v0 开始 bfs,返回最后一个出队的结点
int farthestNode(int v0) {
    for (int i = 1; i <= n; i++) {
        visited[i] = false;
    } 
    visited[v0] = true;
    Queue queue; queue.enqueue(v0);
    while(!queue.isEmpty()) {
        int v = queue.dequeue();
        int w = firstadj(v);
        while(w != 0) {   
            if (visited[w] == false) {
                visited[w] = true;
                queue.enqueue(w);
            }
            w = nextadj(v, w);
        }
    }
    return v;
}

判断vi到vj是否存在长度为 k 的路径

bool hasPath(int vi, int vj, int k) {
    for (int i = 1; i <= n; i++) {
        visited[i] = false;
    } 
    visited[vi] = true;
    Queue queue; queue.enqueue(vi, 1);
    while(!queue.isEmpty()) {
        int v = queue.dequeue();
        int w = firstadj(v, length);
        while(w != 0) { 
            if (w == vj && length == k) 
            	return true;          
            if (visited[w] == false) {
                visited[w] = true;
                queue.enqueue(w, length + 1);
            }
            w = nextadj(v, w);
        }
    }
    return false;
}
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值