Golang每日一练(leetDay0062) BST迭代器、地下城游戏

简介: Golang每日一练(leetDay0062) BST迭代器、地下城游戏

173. 二叉搜索树迭代器 Binary Search Tree Iterator


实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:


   BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。


   boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。


   int next()将指针向右移动,然后返回指针处的数字。


注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。


你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。


示例:

9267734d77d87b900bdb02b272670e8e.png


输入
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
输出
[null, 3, 7, true, 9, true, 15, true, 20, false]
解释
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next();    // 返回 3
bSTIterator.next();    // 返回 7
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 9
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 15
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 20
bSTIterator.hasNext(); // 返回 False


提示:

   树中节点的数目在范围 [1, 10^5] 内

   0 <= Node.val <= 10^6

   最多调用 10^5 次 hasNext 和 next 操作


进阶:

   你可以设计一个满足下述条件的解决方案吗?next() 和 hasNext() 操作均摊时间复杂度为 O(1) ,并使用 O(h) 内存。其中 h 是树的高度。


代码:


package main
import "fmt"
const null = -1 << 31
type TreeNode struct {
  Val   int
  Left  *TreeNode
  Right *TreeNode
}
func buildTree(nums []int) *TreeNode {
  if len(nums) == 0 {
    return nil
  }
  root := &TreeNode{Val: nums[0]}
  Queue := []*TreeNode{root}
  idx := 1
  for idx < len(nums) {
    node := Queue[0]
    Queue = Queue[1:]
    if nums[idx] != null {
      node.Left = &TreeNode{Val: nums[idx]}
      Queue = append(Queue, node.Left)
    }
    idx++
    if idx < len(nums) && nums[idx] != null {
      node.Right = &TreeNode{Val: nums[idx]}
      Queue = append(Queue, node.Right)
    }
    idx++
  }
  return root
}
type BSTIterator struct {
  stack []*TreeNode
}
func Constructor(root *TreeNode) BSTIterator {
  stack := []*TreeNode{}
  node := root
  for node != nil {
    stack = append(stack, node)
    node = node.Left
  }
  return BSTIterator{stack}
}
func (it *BSTIterator) next() int {
  node := it.stack[len(it.stack)-1]
  it.stack = it.stack[:len(it.stack)-1]
  if node.Right != nil {
    n := node.Right
    for n != nil {
      it.stack = append(it.stack, n)
      n = n.Left
    }
  }
  return node.Val
}
func (it *BSTIterator) hasNext() bool {
  return len(it.stack) > 0
}
func main() {
  nums := []int{7, 3, 15, null, null, 9, 20}
  root := buildTree(nums)
  bSTIterator := Constructor(root)
  fmt.Println(bSTIterator.next())    // 返回 3
  fmt.Println(bSTIterator.next())    // 返回 7
  fmt.Println(bSTIterator.hasNext()) // 返回 True
  fmt.Println(bSTIterator.next())    // 返回 9
  fmt.Println(bSTIterator.hasNext()) // 返回 True
  fmt.Println(bSTIterator.next())    // 返回 15
  fmt.Println(bSTIterator.hasNext()) // 返回 True
  fmt.Println(bSTIterator.next())    // 返回 20
  fmt.Println(bSTIterator.hasNext()) // 返回 False
}


输出:

3

7

true

9

true

15

true

20

false



174. 地下城游戏 Dungeon Game


一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。

我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。


骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。


有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。


为了尽快到达公主,骑士决定每次只向右或向下移动一步。


编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。


例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。


-2 (K) -3 3
-5 -10 1
10 30 -5 (P)


说明:

  • 骑士的健康点数没有上限。
  • 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

代码1: 动态规划

func calculateMinimumHP(dungeon [][]int) int {
    m, n := len(dungeon), len(dungeon[0])
    dp := make([][]int, m)
    for i := range dp {
        dp[i] = make([]int, n)
    }
    dp[m-1][n-1] = max(1-dungeon[m-1][n-1], 1) // 初始化右下角的值
    for i := m-2; i >= 0; i-- { // 递推最后一列的值
        dp[i][n-1] = max(dp[i+1][n-1]-dungeon[i][n-1], 1)
    }
    for j := n-2; j >= 0; j-- { // 递推最后一行的值
        dp[m-1][j] = max(dp[m-1][j+1]-dungeon[m-1][j], 1)
    }
    for i := m-2; i >= 0; i-- {
        for j := n-2; j >= 0; j-- {
            dp[i][j] = max(min(dp[i+1][j], dp[i][j+1])-dungeon[i][j], 1)
        }
    }
    return dp[0][0]
}
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

代码2: 二分查找

func calculateMinimumHP(dungeon [][]int) int {
    m, n := len(dungeon), len(dungeon[0])
    left, right := 1, 1000000
    for left < right {
        mid := left + (right-left)/2
        if canSave(mid, dungeon, m, n) {
            right = mid
        } else {
            left = mid+1
        }
    }
    return left
}
func canSave(x int, dungeon [][]int, m, n int) bool {
    dp := make([][]int, m)
    for i := range dp {
        dp[i] = make([]int, n)
    }
    dp[0][0] = x + dungeon[0][0]
    if dp[0][0] <= 0 {
        return false
    }
    for i := 1; i < n; i++ { // 处理第一行
        dp[0][i] = dp[0][i-1] + dungeon[0][i]
        if dp[0][i] <= 0 {
            return false
        }
    }
    for i := 1; i < m; i++ { // 处理第一列
        dp[i][0] = dp[i-1][0] + dungeon[i][0]
        if dp[i][0] <= 0 {
            return false
        }
    }
    for i := 1; i < m; i++ {
        for j := 1; j < n; j++ {
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + dungeon[i][j]
            if dp[i][j] <= 0 {
                return false
            }
        }
    }
    return true
}
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}
目录
相关文章
|
8月前
|
Shell Linux 算法
Shell编程——弱数据类型的脚本语言快速入门指南
Shell编程——弱数据类型的脚本语言快速入门指南
97 0
Shell编程——弱数据类型的脚本语言快速入门指南
|
8月前
|
Go Linux Shell
Linux 终端命令之文件浏览(2) more
Linux 终端命令之文件浏览(2) more
68 0
Linux 终端命令之文件浏览(2) more
|
8月前
|
Shell 机器学习/深度学习 Linux
Linux 终端操作命令(2)内部命令
Linux 终端操作命令(2)内部命令
77 0
Linux 终端操作命令(2)内部命令
|
8月前
|
C++ 算法 存储
力扣 C++|一题多解之动态规划专题(2)
力扣 C++|一题多解之动态规划专题(2)
73 0
力扣 C++|一题多解之动态规划专题(2)
|
4月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
149 4
Golang语言之管道channel快速入门篇
|
4月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
75 4
Golang语言文件操作快速入门篇
|
4月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
121 3
Golang语言之gRPC程序设计示例
|
4月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
101 4
|
4月前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
55 3
|
4月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
86 4
Golang语言goroutine协程篇