Welcome everyone

图 基础

算法和数据结构 汪明鑫 668浏览 0评论

前言

任何数据结构都是由数组和链表组成

包括树、图、跳表等等,万变不离其宗

本文主要是对图相关内容进行复习和整理

树图这些玩意在大学都是老大难的一块

 

图的定义

图(Graph)是由顶点和连接顶点的边构成的离散结构

图是很灵活的一种数据结构

在某种意义上树和链表也是特殊的图

 

图的分类

  • 有向图
  • 无向图
  • 带权图

 

图的存储

  • 邻接矩阵

  • 邻接表

 

一个大型的社交网络选择哪种存储方式呢?

临接表,因为矩阵太占内存资源,比如有2亿个用户,2亿用户间的关系用图表示,是一个稀疏图

很多用户之间并没有关系,也要表示在矩阵里

因此使用临接表更合适,虽然存在链表的遍历,但总比2亿 * 2亿的矩阵更好吧

 

广度优先与深度优先搜索

图常常用来搜索一个顶点到另一个顶点的最短路径

由此衍生了很多算法

本文只简单高下广度优先和深度优先算法

因为算法水平着实垃圾,这两个都看了好久,嘛的

 

 

我们统一使用邻接表

public class Graph { // 无向图
    private int v; // 顶点的个数
    private LinkedList<Integer> adj[]; // 邻接表
    
    public Graph(int v) {
        //初始化临接表
        this.v = v;
        adj = new LinkedList[v];
        for (int i=0; i<v; ++i) {
            adj[i] = new LinkedList<>();
        }
    }
    
    public void addEdge(int s, int t) { // 无向图一条边存两次
        adj[s].add(t);
        adj[t].add(s);
    }

}

 

广度优先

顾名思义,地毯式搜索,优先搜索距离出发地最近的点,慢慢扫秒式的向外扩散

 

 

s表示起始顶点, t表示终止顶点。搜索一条从s到t的路径
借助队列来实现,每次出队列,就把当前顶点的邻接点全部依次入队列 queue
visited 表示顶点是否已遍历
prev 表示当前顶点的前一个顶点,比如上图:prev(4) = 1

public void bfs(int s, int t) {
    if (s == t) return;
    //顶点个数
    boolean[] visited = new boolean[v];
    visited[s]=true;
    Queue<Integer> queue = new LinkedList<>();
    queue.add(s);
    int[] prev = new int[v];
    //最初前一个节点都是-1
    for (int i = 0; i < v; ++i) {
        prev[i] = -1;
    }
    //循环终止条件:队列为空
    while (queue.size() != 0) {
        //出队列
        int w = queue.poll();
        
        //找到w的临接点
        for (int i = 0; i < adj[w].size(); ++i) {
            int q = adj[w].get(i);
            //没有被遍历过,入队列
            if (!visited[q]) {
                //置当前顶点前置节点为w
                prev[q] = w;
                
                //找到目标顶点
                if (q == t) {
                    print(prev, s, t);
                    return;
                }
                
                //顶点q置为已遍历
                visited[q] = true;
                
                //入队列
                queue.add(q);
            }
        }
    }
}

// 递归打印s->t的路径
private void print(int[] prev, int s, int t) { 
    if (prev[t] != -1 && t != s) {
        print(prev, s, prev[t]);
    }
    System.out.print(t + " ");
}

 

 

深度优先

走迷宫式,一直往里走,走到头再回头换条路走

 

boolean found = false; 

public void dfs(int s, int t) {
    found = false;
    boolean[] visited = new boolean[v];
    int[] prev = new int[v];
    for (int i = 0; i < v; ++i) {
        prev[i] = -1;
    }
    recurDfs(s, t, visited, prev);
    print(prev, s, t);
}

private void recurDfs(int w, int t, boolean[] visited, int[] prev) {
    if (found == true) return;
    visited[w] = true;
    
    if (w == t) {
        found = true;
        return;
    }
    
    for (int i = 0; i < adj[w].size(); ++i) {
        int q = adj[w].get(i);
        if (!visited[q]) {
            prev[q] = w;
            //深度搜索
            recurDfs(q, t, visited, prev);
        }
    }
    
}

 

 

应用场景

图可以表示一个庞大的社交网络  

地铁/公交/路线等等

状态机

搜索任务

 

转载请注明:汪明鑫的个人博客 » 图 基础

喜欢 (5)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz