• ⑨ 数据结构之“图”


    一、理论

    1. 图简介

    • 图是 网络结构 的抽象模型,是一组由 连接的 节点

    • 图可以表示任何二元关系

    • js中没有图,但可以用Object和Array构建图

    • 图的表示法:邻接矩阵、邻接表、关联矩阵...

    2. 图的表示法

    2.1 邻接矩阵

    2.2 邻接表

    const graph = {
      0: [1, 2],
      1: [2],
      2: [0, 3],
      3: [3]
    }
    

    3. 图的常用操作

    • 深度优先遍历
    • 广度优先遍历

    3.1 深度优先遍历

    3.1.1 什么是深度优先遍历
    • 尽可能深的搜索图的分支
    3.1.2 深度优先遍历算法口诀
    • 访问根节点
    • 对根节点的 没访问过的相邻节点 挨个进行深度优先遍历
    3.1.3 coding part
    const visited = new Set()
    const dfs = n => {
      console.log(n);
      visited.add(n)
      graph[n].forEach(c => {
        if(!visited.has(c)) {
          dfs(c)
        }
      })
    }
    dfs(2)
    

    3.2 广度优先遍历

    3.1.1 什么是广度优先遍历
    • 先访问离根节点最近的节点
    3.1.2 广度优先遍历算法口诀
    • 新建一个队列,把根节点入队
    • 把队头出队并访问
    • 把队头的 没访问过的相邻节点 入队
    • 重复第二三步,直到队列为空
    3.1.3 coding part
    const visited = new Set()
    const q = [2]
    visited.add(2)
    while(q.length) {
      const n = q.shift()
      console.log(n)
      graph[n].forEach(c => {
        if(!visited.has(c)) {
          q.push(c)
          visited.add(c)
        }
      })
    }
    
    

    二、刷题

    1. 有效的数字(65)

    1.1 题目描述

    • 给你一个字符串 s ,如果 s 是一个 有效数字 ,请返回 true

    • 有效数字(按顺序)可以分成以下几个部分:

      1. 一个 小数 或者 整数
      2. (可选)一个 'e' 或 'E' ,后面跟着一个 整数
    • 小数(按顺序)可以分成以下几个部分:

      1. (可选)一个符号字符('+' 或 '-')
      2. 下述格式之一:
        1. 至少一位数字,后面跟着一个点 '.'
        2. 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
        3. 一个点 '.' ,后面跟着至少一位数字
    • 整数(按顺序)可以分成以下几个部分:

      1. (可选)一个符号字符('+' 或 '-')
      2. 至少一位数字

    1.2 解题思路

    1.3 解题步骤

    • 构建一个表示状态的图
    • 遍历字符串,并沿着图走,如果到了某节点无路可走则返回false
    • 遍历结束,如走到3/5/6,则返回true,否则返回false
    function isNumber(s) {
      const graph = {
        0: { 'blank': 0, 'sign': 1, '.': 2, 'digit': 6 },
        1: { '.': 2, 'digit': 6 },
        2: { 'digit': 3 },
        3: { 'digit': 3, 'e': 4 },
        4: { 'digit': 5, 'sign': 7 },
        5: { 'digit': 5 },
        6: { '.': 3, 'e': 4, 'digit': 6 },
        7: { 'digit': 5 }
      }
      let state = 0
      for(c of s.trim()) {
        if(c >= '0' && c <= '9') {
          c = 'digit'
        } else if(c === '') {
          c = 'blank'
        } else if(c === '+' || c === '-') {
          c = 'sign'
        }
        state = graph[state][c]
        if(state === undefined) {
          return false
        }
      }
      if(state === 3 || state === 5 || state === 6) {
        return true
      }
      return false
    }
    

    1.4 时间复杂度&空间复杂度

    • 时间复杂度: O(n)
    • 空间复杂度: O(1)

    2. 太平洋大西洋水流问题(417)

    2.1 题目描述

    • 给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界
    • 规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动
    • 请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标
    • 提示:
      • 输出坐标的顺序不重要
      • m 和 n 都小于150

    2.2 解题思路

    • 把矩阵想象成图
    • 从海岸线逆流而上遍历图,所到之处就是可以流到某大洋的坐标

    2.3 解题步骤

    • 新建两个矩阵,分别记录能流到两个大洋的坐标
    • 从海岸线,同时深度优先遍历图,过程中填充上述矩阵
    • 遍历两个矩阵,找出能流到两大洋的坐标
    function pacificAtlantic(matrix) {
      if(!matrix || !matrix[0]) return []
      const m = matrix.length
      const n = matrix[0].length
      const flow1 = Array.from({ length: m }, () => new Array(n).fill(false))
      const flow2 = Array.from({ length: m }, () => new Array(n).fill(false))
      // 深度优先遍历
      const dfs = (r, c, flow) => {
        flow[r][c] = true
        [[r-1, c], [r+1, c], [r, c-1], [r, c+1]].forEach(([nr, nc]) => {
          if(
            // 保证下一个节点在矩阵中
            nr  >= 0 && nr < m && nc >= 0 && nc < n &&
            // 保证下一个节点还未访问 -- 防止死循环 
            !flow[nr][nc] &&
            // 保证逆流而上
            matrix[nr][nc] >= matrix[r][c]
          ) {
            dfs(nr, nc, flow)
          }
        })
      }
      // 沿着海岸线逆流而上
      for(let r = 0; r < m; r++) {
        // 第一列
        dfs(r, 0, flow1) // 流到太平洋
        // 最后一列
        dfs(r, n-1, flow2) // 流到大西洋
      }
      for(let c = 0; c < n; c++) {
        // 第一行
        dfs(0, c, flow1)
        // 最后一行
        dfs(m-1, c, flow2)
      }
      // 收集能流到两个大洋的坐标
      const res = []
      for(let r = 0; r < m; r++) {
        for(let c = 0; c < n; c++) {
          if(flow1[r][c] && flow2[r][c]) {
            res.push([r, c])
          }
        }
      }
      return res
    }
    

    2.4 时间复杂度&空间复杂度

    • 时间复杂度: O(m*n)
    • 空间复杂度: O(m*n)

    3. 克隆图(133)

    3.1 题目描述

    • 给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)
    • 图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])
    • 每个节点的值都和它的索引相同
    function Node(val, neighbors) {
      this.val = val === undefined ? 0 : val;
      this.neighbors = neighbors === undefined ? [] : neighbors;
    }
    

    3.2 解题思路

    输入:adjList = [[2,4],[1,3],[2,4],[1,3]]
    输出:[[2,4],[1,3],[2,4],[1,3]]

    • 拷贝所有节点
    • 拷贝所有边

    3.3 解题步骤

    • 深度/广度优先遍历所有节点
    • 拷贝所有节点存储起来
    • 将拷贝的节点按照原图的连接方法进行连接
    // 深度优先遍历
    function cloneGraph(node) {
      if(!node) return;
      const visited = new Map();
      const dfs = n => {
        const nCopy = new Node(n.val);
        visited.set(n, nCopy);
        (n.neighbors || []).forEach(ne => {
          if(!visited.has(ne)) {
            dfs(ne);
          }
          nCopy.neighbors.push(visited.get(ne));
        }) 
      };
      dfs(node);
      return visited.get(node);
    }
    // 广度优先遍历
    function cloneGraph(node) {
      if(!node) return;
      const visited = new Map();
      const q = [node];
      visited.set(node, new Node(node.val));
      while(q.length) {
        const n = q.shift();
        (n.neighbors || []).forEach(ne => {
          if(!visited.has(ne)) {
            q.push(ne)
            visited.set(ne, new Node(ne.val));
          }
          visited.get(n).neighbors.push(visited.get(ne))
        })
      }
      return visited.get(node);
    }
    

    3.4 时间复杂度&空间复杂度

    深度优先遍历
    • 时间复杂度: O(n)
    • 空间复杂度: O(n)
    广度优先遍历
    • 时间复杂度: O(n)
    • 空间复杂度: O(n)

    三、总结 -- 技术要点

    • 图是 网络结构 的抽象模型,是一组由 连接的 节点
    • 图可以表示任何二元关系,比如道路、航班...
    • js中没有图,但可以用Object和Array构建图
    • 图的表示法:邻接矩阵、邻接表、关联矩阵...
    • 图的常用操作:深度/广度优先遍历
  • 相关阅读:
    2020软件工程作业——团队03
    2020软件工程作业 ——团队02
    2020软件工程作业05
    2020软件工程作业04
    2020软件工程作业03
    2020软件工程作业02
    2020软件工程作业01
    微服务:基本介绍
    excel模板解析前后设计变化,以及我对此的看法和感受
    纸上得来终觉浅,绝知此事要躬行——Spring boot任务调度
  • 原文地址:https://www.cnblogs.com/pleaseAnswer/p/15846477.html
Copyright © 2020-2023  润新知