算法 - 图

  • 图是网络结构的抽象模型,是一组由边连接的节点
  • 图可以表示任何二元关系,比如道路、航班
  • JS中没有图,但是可以用Object和Array构建图
  • 图的表示法:邻接矩阵、邻接表、关联矩阵…
邻接矩阵
邻接表

图的深度/广度优先遍历

  • 深度优先遍历:尽可能深的搜索图的分支
  • 广度优先遍历:先访问离根节点最近的节点

深度优先遍历算法口诀

  • 访问根节点
  • 对根节点的没访问过的相邻节点挨个进行深度优先遍历
深度优先遍历
// 图,后面不会重复写
const graph = {
    0: [1, 2],
    1: [2],
    2: [0, 3],
    3: [3]
}
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) // 2 0 1 3

广度优先遍历算法口诀

  • 新建一个队列,把根节点入队
  • 把队头出队并访问
  • 把队头的没访问过的相邻节点入队
  • 重复第二、三步,直到队列为空
广度优先遍历
const visited = new Set()
visited.add(2)
const q = [2]

while (q.length) {
    const n = q.shift()
    console.log(n) // 2 0 3 1
    graph[n].forEach(c => {
        if (!visited.has(c)) {
            q.push(c)
            visited.add(c)
        }
    })
}

有效数字

leeCode第65题

  • 构建一个表示状态的图
  • 遍历字符串,并沿着图走,如果到了某个节点无路可走就返回false
  • 遍历结束,如走到3/5/6,就返回true,否则返回false
有效数字邻接表图
/**
 * @param {string} s
 * @return {boolean}
 * @description 难点在于画出邻接表图,循环遍历输入,将每一个字符转化为对应属性,用于获取当前状态
 */
 const isNumber = function(s) {
    const graph = {
        0: { blank: 0, sign: 1, dot: 2, digit: 6 },
        1: { digit: 6, dot: 2 },
        2: { digit: 3 },
        3: { digit: 3, e: 4 },
        4: { digit: 5, sign: 7 },
        5: { digit: 5 },
        6: { digit: 6, dot: 3, e: 4 },
        7: { digit: 5 },
    }

    let state = 0 // 初始状态都是0
    for (c of s.trim()) { // 题目中前后括号不影响判断,所以先去除前后空格
        if (c === ' ') {
            c = 'blank'
        } else if (c === '+' || c === '-') {
            c = 'sign'
        } else if (c === '.') {
            c = 'dot'
        } else if (c.toLowerCase() === 'e') {
            c = 'e'
        } else if (!isNaN(c)) {
            c = 'digit'
        }
        
        state = graph[state][c]
        if (state === undefined) return false
    }

    // 合法的三个状态
    const validState = [3, 5, 6]
    return validState.includes(state)
}

// "abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"
const testArr = ["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"]

testArr.forEach(item => {
    console.log(isNumber(item))
})

太平洋大西洋水流问题

leeCode第417题

  • 把矩阵想象成图
  • 从海岸线逆流而上遍历图,所到之处就是可以流到某个大洋的坐标
  • 新建两个矩阵,分别记录能流到两个大洋的坐标
  • 从海岸线,多管齐下,同事深度优先遍历图,过程中填充上述矩阵
  • 遍历两个矩阵,找出能流到两个大洋的坐标
/**
 * @param {number[][]} matrix
 * @return {number[][]}
 */
const pacificAtlantic = function(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
        const location = [[r - 1, c], [r + 1, c], [r, c -1], [r, c + 1]]
        location.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)
    }

    // 收集能流到两个大洋里的坐标
    let res = []
    for (let r = 0; r < m; r += 1) {
        for (let c = 0; c < n; c += 1) {
            if (flow1[r][c] && flow2[r][c]) {
                res.push([r, c])
            }
        }
    }

    return res
}

const matrix = [
    [1, 2, 2, 3, 5],
    [3, 2, 3, 4, 4],
    [2, 4, 5, 3, 1],
    [6, 7, 1, 4, 5],
    [5, 1, 1, 2, 4]
]

console.log(pacificAtlantic(matrix))

克隆图

leeCode第133题

  • 拷贝所有节点
  • 拷贝所有的边
  • 将拷贝的节点,按照原图的连接方法进行连接

这道题没法模拟,dfs中会丢失原索引值,导致填充都为0,但如果将索引一起放进去leeCode又跑不过,只能去leeCode上测试

深度优先解法:

const cloneGraph = function(node) {
    if (!node) return

    const visited = new Map()
    const dfs = n => {
        const nCopy = new Node(n.val)
        visited.set(n, nCopy)
        const neighbors = n.neighbors || []
        neighbors.forEach(ne => {
            if (!visited.has(ne)) { // 已被记录的邻接边不再记录
                dfs(ne)
            }
            nCopy.neighbors.push(visited.get(ne))
        })
    }
    dfs(node)
    return visited.get(node)
}

广度优先解法:

const cloneGraph = function(node) {
    if (!node) return

    const visited = new Map()
    visited.set(node, new Node(node.val))
    const q = [node]
    while (q.length) {
        const n = q.shift()
        const neighbors = n.neighbors || []
        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)
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容