当青训营遇上码上掘金-寻友之旅

31 阅读2分钟

当青训营遇上码上掘金

题目描述

小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)

请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)

解题思路

使用广度优先搜索,从小青所在地开始,依次搜索小青可以到达的所有节点(即小青所在地的前后和小青所在地的两倍),直到找到小码所在的节点。

使用一个队列来存储小青可以到达的节点,并使用 map 记录到达这些节点用到的步数。当找到小码所在的节点 K 时,map[K] 即为小青找到小码的最短时间。

伪代码:

// queue 中存放深度搜索中检测到的节点,初始元素是小码所在的节点
queue = {N}
// map 存放到达节点 i 用了 k 分钟
steps = map<int, int>
steps[N] = 0

while len(queue) > 0:
    // 取出第一个
    cur = queue.pop()
    // 小青找到了小码
    if cur == k:
        break
    if 小青尝试往前往后或者乘公交车找小码可以到达的节点不在 steps 中:
        queue.push(小青当前所在的节点)
        steps[小青当前所在的节点] = steps[小青之前所在的节点] + 1

// steps[K] 即小青找小码需要的最短时间
print(steps[K])

算法复杂度

BFS 相当于是暴力枚举,每个节点只会被遍历到一次,所以时间复杂度为 O(n),需要为遍历到的节点记录“时间”,空间复杂度也为 O(n)。

go 版本的代码

package main

import (
    "fmt"
)

func main() {
    // var N, K int
    // if _, err := fmt.Scanf("%d%d", &N, &K); err != nil {
    // 	return
    // }

    N := 20
    K := 100

    queue := []int{N}
    steps := map[int]int{}

    for len(queue) > 0 {
        cur := queue[0]
        queue = queue[1:]
        if cur == K {
            break
        }

        if cur-1 >= 0 && steps[cur-1] == 0 {
            queue = append(queue, cur-1)
            steps[cur-1] = steps[cur] + 1
        }
        if cur+1 <= 100000 && steps[cur+1] == 0 {
            queue = append(queue, cur+1)
            steps[cur+1] = steps[cur] + 1
        }
        if cur*2 <= 200000 && steps[cur*2] == 0 {
            queue = append(queue, cur*2)
            steps[cur*2] = steps[cur] + 1
        }
    }

    fmt.Println(steps[K])
}