算法总结-图(正在更新)

19 篇文章 0 订阅
本文详细介绍了图数据结构的定义,包括无向图、有向图和完全图的概念,探讨了图的两种存储结构——邻接矩阵和邻接表,并通过深度优先搜索(DFS)和广度优先搜索(BFS)两种算法进行图的搜索。文章还提供了实例解析,如最小高度树问题和重新安排行程问题,展示了如何应用图理论解决实际问题。
摘要由CSDN通过智能技术生成

参考:https://mp.weixin.qq.com/s/A4ogzRG5AFMLer5HyNFMFg

简述

图是一种重要的数据结构,属于一种复杂的非线性结构,由顶点的有穷非空集合和顶点之间边的集合组成,前述专题的数据结构大多为线性表和树两类结构,其中线性表中的元素是“一对一”的关系,树中的元素是“一对多”的关系,而图结构中的元素则是“多对多”的关系。下面一起来看看

图的定义

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

(1)无向图

由顶点和边构成

(2)有向图

由顶点和有向边构成

(3)完全图

如果任意两个顶点之间都存在边叫完全图,有向的边叫有向完全图。如果无重复的边或者顶点到自身的边叫简单图

图的存储结构

图的存储结构分为邻接矩阵和邻接表两种。
(1)邻接矩阵:图的邻接矩阵存储方式是用两个数组来表示,一个一维数组存储图顶点的信息,一个二维数组(称为邻接矩阵)存储图中边或者弧的信息。
(2)邻接表:邻接表示一个有单链表组成的数组(也就是数组+链表) ,数组的大小等于图中顶点的个数,无向图的链的第一个元素是本顶点,后继分别连接着和这个顶点相连的顶点;有向图的链第一个顶点是本顶点,后继是以本顶点为起点的边的终点,如果是有权图,可以在节点元素中设置权值属性 。
邻接矩阵适用于稠密图,即图上的任意两点之间均(差不多都)存在一条边,相比邻接矩阵,邻接表要更加节省空间。
下面以邻接表存储有向带权图为例进行介绍:
在这里插入图片描述
注:数组存储的是所有的顶点,每一个顶点后面连接的块代表前面顶点所指向的顶点和路线的权值,如果该点还指向其他顶点,则继续在块后面添加。

图的搜索

图的搜索就是逐个访问图中的所有顶点,由于图中多对多的关系,可能存在重复访问同一顶点,所以为了避免这种情况,在图的结构设计时设置了一个布尔型数组,来确定某一顶点是否已经被遍历。
常用的遍历图的方法有两种:广度优先法和深度优先法

(1)深度优先搜索(Depth First Search)

和树的先序遍历比较类似
搜索思路:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。 若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
显然,深度优先搜索是一个递归的过程。
有向图深度优先搜素
有向图深度优先搜素
注:上述图片来自
https://blog.csdn.net/hehuanchun0311/article/details/80168109

(2)广度优先搜索(Breadth First Search)

是一个分层搜索的过程,和树的层序遍历算法类同
搜索思路:从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“ 先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问 ”,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。
有向图广度优先搜素
注:上述图片来自
https://blog.csdn.net/hehuanchun0311/article/details/80168109

实例解析

题目1:最小高度树

题目描述:对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点
格式:该图包含 n 个节点,标记为 0 到 n - 1。给定数字 n 和一个无向边 edges 列表(每一个边都是一对标签)。你可以假设没有重复的边会出现在 edges 中。由于所有的边都是无向边, [0, 1]和 [1, 0] 是相同的,因此不会同时出现在 edges 里。
示例1:
输入: n = 4, edges = [[1, 0], [1, 2], [1, 3]]

       0 
       |
       1
    /    \
  2       3

输出: [1]
示例2:
输入: n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]

0  1  2
 \ | /
   3
   |
   4
   |
   5

输出:[3, 4]
解题思路:(1)构建图;(2)环遍历图,找出叶子节点;(3)去除叶子节点;(4)直到图中节点只剩下2个或1个,返回剩下的节点。
注:下面代码可左右滑动查看

class Solution {
    private boolean[][] graph;
    private boolean[] visited;
    private int[] e;
    private Queue<Integer> queue;
    
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        graph = new  boolean[n][n];
        visited = new boolean[n];
        e = new int[n];
        queue = new LinkedList<>();
        //初始化建图
        for(int i = 0; i < edges.length; i++){
            graph[edges[i][0]][edges[i][1]] = true;
            graph[edges[i][1]][edges[i][0]] = true;
            e[edges[i][0]]++;
            e[edges[i][1]]++;
        }
        //去除最外层的节点
        while(n>2){
            //遍历图,找到最外层节点
            findOuter();
            while(!queue.isEmpty()){
                Integer v = queue.poll();
                e[v]--;
                n--;
                visited[v] = true;
                for(int i = 0; i < graph[v].length; i++){
                    if(graph[v][i]){
                        e[i]--;
                        graph[v][i] = false;
                        graph[i][v] = false;
                    }
                }  
            }            
        } 
        List<Integer> rt = new ArrayList<>();
        for(int i = 0; i < visited.length; i++){
            if(!visited[i]){
                rt.add(i);
            }
        }
        return rt;
    }
    
    public void findOuter(){
        for(int i = 0; i<e.length; i++){
            if(e[i] == 1){
                queue.add(i);
            }
        }
    }
}

题目2:重新安排行程

题目描述:给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 出发。
说明:

(1)如果存在多种有效的行程,你可以按字符自然排序返回最小的行程组合。例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前
(2)所有的机场都用三个大写字母表示(机场代码)。
(3)假定所有机票至少存在一种合理的行程。
示例 1:

输入: [[“MUC”, “LHR”], [“JFK”, “MUC”], [“SFO”, “SJC”], [“LHR”, “SFO”]]

输出: [“JFK”, “MUC”, “LHR”, “SFO”, “SJC”]

示例 2:

输入: [[“JFK”,“SFO”],[“JFK”,“ATL”],[“SFO”,“ATL”],[“ATL”,“JFK”],[“ATL”,“SFO”]]

输出: [“JFK”,“ATL”,“JFK”,“SFO”,“ATL”,“SFO”]

解释: 另一种有效的行程是

[“JFK”,“SFO”,“ATL”,“JFK”,“ATL”,“SFO”]。但是它自然排序更大更靠后。

解题思路:(1)构建图;(2)对列表排序;(3)深度优先搜素;(4)回溯。
注:下面代码可左右滑动查看

class Solution {
    public boolean tag = false;//判断是否找到路线
    public List<String> findItinerary(String[][] t) {
        Map<String,ArrayList<String>> map = new TreeMap<>();
        //构图
        for(String[] str:t){
            if(!map.containsKey(str[0])) map.put(str[0],new ArrayList<>());
            map.get(str[0]).add(str[1]);
        }
        //对列表排序
        for(String s:map.keySet()){
            Collections.sort(map.get(s));
        }
        List<String> res = new ArrayList<>();
        dfs(res,map,"JFK",t.length,new HashSet<>(),"");
        return res;
     }
    public void dfs(List<String> res,Map<String,ArrayList<String>> map,String curString,int len,Set<String> set,String s){
        res.add(curString);
        //下面的条件为找到合适路线
        if(res.size() == len + 1) {
            tag = true;
            return ;
        }
        //下面的情况为去了一个没有票离开的站点
        if(map.get(curString) == null) {
            set.remove(s);//回溯,即这张票归为未用状态
            res.remove(res.size() - 1);//从路线中删除该站
            return;
        }
        //深度优先遍历
        //使用Set存储当前站 到 某一个站的票已用。
        for(int i = 0; i < map.get(curString).size(); i++){
            String str = map.get(curString).get(i);
            if(!set.contains(curString + "*" + i)){
                set.add(curString + "*" + i);
                dfs(res,map,str,len,set,curString + "*" + i);
                if(tag){
                 return;   
                } 
            }
        }
        //回溯
        set.remove(s);
        res.remove(res.size() - 1);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值