图论03-【无权无向】-图的深度优先DFS遍历-路径问题/检测环/二分图

文章目录

  • [1. 代码仓库](#1. 代码仓库)
  • [2. 单源路径](#2. 单源路径)
    • [2.1 思路](#2.1 思路)
    • [2.2 主要代码](#2.2 主要代码)
  • [3. 所有点对路径](#3. 所有点对路径)
    • [3.1 思路](#3.1 思路)
    • [3.2 主要代码](#3.2 主要代码)
  • [4. 路径问题的优化-提前结束递归](#4. 路径问题的优化-提前结束递归)
    • [4.1 思路](#4.1 思路)
    • [4.2 主要代码](#4.2 主要代码)
  • [5. 检测环](#5. 检测环)
    • [5.1 思路](#5.1 思路)
    • [5.2 主要代码](#5.2 主要代码)
  • [6. 二分图](#6. 二分图)
    • [6.1 思路](#6.1 思路)
    • [6.2 主要代码](#6.2 主要代码)
      • [6.2.1 遍历每个联通分量](#6.2.1 遍历每个联通分量)
      • [6.2.2 递归判断相邻两点的颜色是否一致](#6.2.2 递归判断相邻两点的颜色是否一致)

1. 代码仓库

https://github.com/Chufeng-Jiang/Graph-Theory

2. 单源路径

2.1 思路

  1. 构造visited数组和pre数组
    1.1 visited数组记录当前节点是否访问过
    也可以不使用visited数组,pre数组全部初始化为-1,联通的顶点对应的pre数组的值为前一个节点,pre数组中值为-1的都是不连通的顶点。
    1.2 pre数组记录当前节点的前一个节点
  2. 使用pre数组对终点进行反推回源点,并记录
  3. 将终点到原点的路径,反序输出

2.2 主要代码

cpp 复制代码
   public SingleSourcePath(Graph G, int s){ //单源路径,要把源s传进来,而且只考虑与s连通的顶点,不连通的不考虑

        G.validateVertex(s);

        this.G = G;
        this.s = s;
        visited = new boolean[G.V()];
        pre = new int[G.V()];

        dfs(s, s);
    }

    private void dfs(int v, int parent){ //参数一:当前顶点; 参数二:上一个顶点

        visited[v] = true;
        pre[v] = parent;

        for(int w: G.adj(v)) //跟v相邻的所有顶点,相当于v是源,遍历与当前顶点相邻的所有点
            if(!visited[w])
                dfs(w, v); //(顶点,源)
    }
    
     public Iterable<Integer> path(int t){ //从源到t的路径

	     ArrayList<Integer> res = new ArrayList<Integer>();
	     if(!isConnectedTo(t)) return res;	
	     int cur = t; // 从t往回找
	     
	     while(cur != s){
	         res.add(cur); //添加当前节点(循环内不包含源)
	         cur = pre[cur]; //pre[cur]的值是cur的上一个节点
	     }
	     
	     res.add(s); //添加源
	     Collections.reverse(res);
	     return res;
 }

3. 所有点对路径

3.1 思路

对所有顶点进行遍历,创建每一个点的单源路径数组。

3.2 主要代码

cpp 复制代码
public AllPairsPath(Graph G){
    this.G = G;
    paths = new SingleSourcePath[G.V()];
    for(int v = 0; v < G.V(); v ++)
        paths[v] = new SingleSourcePath(G, v);
}

4. 路径问题的优化-提前结束递归

4.1 思路

在填充visited和pre数组的时候,如果遇到了目标节点,直接结束。剩下的节点不进行处理。

if(v == t) return true; //程序出口,当到达t顶点时,返回true提前结束递归,而不仅仅是返回return

4.2 主要代码

cpp 复制代码
    private boolean dfs(int v, int parent){

        visited[v] = true;
        pre[v] = parent;

        if(v == t) return true; //程序出口,当到达t顶点时,返回true提前结束递归,而不仅仅是返回return

        for(int w: G.adj(v)) //遍历与v相邻的顶点
            if(!visited[w]) //如果相邻的顶点没有被访问过
                if(dfs(w, v)) //递归遍历相邻的顶点,如果到达 v==t,则值为true
                    return true; //提前返回true

        return false; // 转一圈没法达到t,就可以返回false
    }

5. 检测环

5.1 思路

从某一点v出发,找到了点w,w被访问过,并且w不是v的前一个节点

5.2 主要代码

cpp 复制代码
public CycleDetection(Graph G){
    this.G = G;
    visited = new boolean[G.V()];

    //要对所有的连通分量进行环检测
    for(int v = 0; v < G.V(); v ++)
        if(!visited[v])  //如果没有访问过
            if(dfs(v, v)){ //则进行深度搜索,如果深度搜索出来的是true,说明有环,则进入循环break
                hasCycle = true;
                break;
            }
}

    
private boolean dfs(int v, int parent){
    visited[v] = true;

    for(int w: G.adj(v))
        if(!visited[w]){ //case1:如果w没有被访问过
            if(dfs(w, v)) //如果dfs返回true,则说明有环。因为dfs有环才会返回true,那么进入if选择语句return true提前结束
                return true;
        }
        else if(w != parent) // case2:从v出发,找到了w,w还被访问过,并且w不是v的前一个节点
            return true; // 此时找到了环

    //其他的情况,找一圈没有找到环,返回false
    return false;
}

6. 二分图

6.1 思路

二分图可以通过染色过程把顶点区分开,
[-1:顶点还没染色]
[0:一种颜色]
[1:另外一种颜色]

6.2 主要代码

6.2.1 遍历每个联通分量

  1. dfs(v, 0) 返回true代表相连的两点颜色不一样,暂未出现矛盾;
  2. dfs(v, 0) 返回false代表相连的两点颜色一样,不符合二分图的定义,因此进入if语句块,设置isBipartite = false;并且提前结束循环。
cpp 复制代码
for(int v = 0; v < G.V(); v ++)
    if(!visited[v]) //如果没有被访问
    // 起始的时候把v统一染成0色,如果dfs返回的false,进入下面结构体,否则跳出执行v++
        if(!dfs(v, 0)){ 
            isBipartite = false; // 检测出错了,就设置成false
            break; // 后续的循环就不需要进行了
        }

6.2.2 递归判断相邻两点的颜色是否一致

cpp 复制代码
private boolean dfs(int v, int color){  //参数一:顶点   参数二:颜色
    visited[v] = true;
    colors[v] = color;

    //依次判断相邻顶点w的颜色
    for(int w: G.adj(v))
        if(!visited[w]){ //如果w没有被访问过,则进入判断
            if(!dfs(w, 1 - color)) //如果v的颜色是0,那么w的颜色应该是1。如果v的颜色是1,那么w的颜色应该是0.
                return false; //如果相邻的两个顶点颜色一样,那么就不是二分图
        }
        else if(colors[w] == colors[v]) //如果相邻的两个顶点颜色一样,那么就不是二分图
            return false;

        return true;
}
相关推荐
京东零售技术8 分钟前
SIGIR 2025 | 基于图同构网络的群体建模在点击率预测中的应用
算法
程序员西西16 分钟前
深入剖析 Java 中的 ZGC 机制:原理、优势与实践
java·后端·算法
月明长歌18 分钟前
【码道初阶】Leetcode.189 轮转数组:不熟悉ArrayList时踩得坑,被Arraylist初始化骗了?
java·算法·leetcode·职场和发展
卡尔AI工坊20 分钟前
万众瞩目的 GPT 5.2,连个火柴人游戏都做不明白?
后端·算法
fantasy_arch21 分钟前
leetcode算法-最大乘积子数组
算法·leetcode·职场和发展
dragoooon3433 分钟前
[hot100 NO.8~12]
算法
kaikaile199544 分钟前
MATLAB计算卫星星下点轨迹
开发语言·算法·matlab
_OP_CHEN1 小时前
【算法基础篇】(三十一)动态规划之基础背包问题:从 01背包到完全背包,带你吃透背包问题的核心逻辑
算法·蓝桥杯·动态规划·背包问题·01背包·完全背包·acm/icpc
长安er1 小时前
LeetCode876/141/142/143 快慢指针应用:链表中间 / 环形 / 重排问题
数据结构·算法·leetcode·链表·双指针·环形链表
Aaron15881 小时前
电子战侦察干扰技术在反无人机领域的技术浅析
算法·fpga开发·硬件架构·硬件工程·无人机·基带工程