当青训营遇上码上掘金,我参与的第五届青训营码上掘金活动,选择的是 寻友之旅 主题
主题内容
主题 3:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
作者:青训营官方账号
链接:juejin.cn/post/718775…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思路
-
本问题在我看来是一个最短路径问题,这个题中的最短时间就是最短路径,我选择使用bfs算法求解,使用bfs算法需要构建队列,go没有现成的队列,所以我自己构建了个队列,因为有三种选择(+1,-1,*2),所以是bfs树是一个三叉树,树上每个点的深度就是这个点到根结点的最短距离,所以当搜索到终点时,树的深度就是起点到终点的最短路径,即小青到小码家所需的最短时间。
-
我在构建队列时需要记录每个节点的层次信息,所以这个队列有两个动态数组,一个记录小青的移动位置坐标,一个记录移动位置的层次信息,当搜索到终点时,队尾元素的层次信息就是起点到终点的最短距离。
-
在使用bfs时,我考虑到当起点在终点的右边时,移动方式只有-1,当起点在终点左边时,移动方式有+1,*2。所以我在搜索时也做了相应的调整。
-
一个成熟编程人员应该学会水代码,所以我选择了通过构建队列,使用广度优先搜索算法解决这个问题。
-
这东西让我写的复杂了,编程能力有限,这个问题也有很多其他的求解思路,我之后会再学习再改进的。
代码
package main
import (
"errors"
"fmt"
)
// 寻友之旅
// 三叉树 队列 广搜
// 树的深度即为最短时间
type queue struct { //头出尾入
array []int //动态数组 存位置坐标
level []int //记录层次信息
head int //队头
tail int //队尾
}
// 第一个元素是空开的
// 队头指向的是失效的数据 队尾指向的是有效的数据
func (q *queue) push(data int, leveldata int) {
//因为使用动态数组创建的队列 无需考虑队列容量
q.tail++
q.array = append(q.array, data) //q.array[q.tail] = data 因为使用的是切片 所以要使用append 追加元素 会自动扩容
q.level = append(q.level, leveldata) //q.level[q.tail] = leveldata
return //入队
}
func (q *queue) pop() (data int, leveldata int) {
q.head++
data = q.array[q.head]
leveldata = q.level[q.head]
return data, leveldata //队头移动,然后返回队头的数据
}
// 显示队列数据
func (q *queue) show() {
//从队头遍历到队尾
for i := 1; i <= len(q.array)-1; i++ {
fmt.Println(i, q.array[i], " ", q.level[i])
}
}
func (q *queue) init() {
q.head = 0
q.tail = 0
q.array = make([]int, 1)
q.level = make([]int, 1)
}
func (q *queue) isempty() bool {
if q.head == q.tail {
return true
} else {
return false
}
}
var N, K int // 输入坐标青N 码K
var que = queue{
array: make([]int, 1), //此处必须为1 因为动态数组扩容 索引就对不上了
level: make([]int, 1),
head: 0,
tail: 0,
}
func main() {
var t int
fmt.Printf("请输入次数")
fmt.Scanf("%d\n", &t)
for t != 0 {
que.init()
fmt.Printf("请输入起点N和终点K:")
fmt.Scanf("%d%d\n", &N, &K) //读取换行
depth, err := bfs(N)
if err != nil {
fmt.Println(err.Error()) //显示错误信息
return
}
fmt.Printf("最短时间为%d分钟\n", depth)
//que.show()
t--
}
}
// 记录层次信息 父的层次信息+1就是子的层次信息
func bfs(loc int) (depth int, err error) { //广搜 层次遍历
//先loc入队
var levelsum = 0 //父节点层次信息
que.push(loc, 0) //根结点0层
var nowloc int
for !que.isempty() { //不为空一直搜索
nowloc, levelsum = que.pop()
//三叉树+1 -1 *2
//如果终点 在 当前点的左边 那只能 -1
if nowloc > K {
que.push(nowloc-1, levelsum+1) //当前坐标入队
if nowloc-1 == K {
return que.level[que.tail], nil //搜素完成 直接退出 返回队尾元素的层次信息
}
} else if nowloc < K { //如果终点 在 当前点的右边 可以 +1 *2
que.push(nowloc+1, levelsum+1)
que.push(nowloc*2, levelsum+1)
if nowloc+1 == K || nowloc*2 == K {
return que.level[que.tail], nil //搜素完成 直接退出 返回队尾元素的层次信息
}
} else { //父节点 正好就在终点 只有 N=K的时候
return que.level[que.tail], nil //返回队尾元素的层次信息
}
}
//为空的可能性 只有无解的时候 或者 起点就是终点的情况 而上面已经预防了
return que.level[que.tail], errors.New("无解")
//最终得到的队尾元素对应的就是最短的时间
}
```
```