Golang每日一练(leetDay0019)

简介: Golang每日一练(leetDay0019)

55. 跳跃游戏 Jump Game


给定一个非负整数数组 nums ,你最初位于数组的 第一个下标

数组中的每个元素代表你在该位置可以跳跃的最大长度。


判断你是否能够到达最后一个下标。


示例 1:

输入:nums = [2,3,1,1,4]

输出:true

解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。


示例 2:

输入:nums = [3,2,1,0,4]

输出:false

解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。


提示:

   1 <= nums.length <= 3 * 10^4

   0 <= nums[i] <= 10^5


代码1:

贪心算法,记录每个位置能跳到的最远距离,如果当前位置已经超过了当前能跳到的最远距离,说明无法到达终点,返回 false。  


package main
import "fmt"
func canJump(nums []int) bool {
  n := len(nums)
  maxPos := 0
  for i := 0; i < n; i++ {
    if i > maxPos {
      return false
    }
    maxPos = max(maxPos, i+nums[i])
  }
  return true
}
func max(a, b int) int {
  if a > b {
    return a
  }
  return b
}
func main() {
  nums := []int{2, 3, 1, 1, 4}
  fmt.Println(canJump(nums))
  nums2 := []int{3, 2, 1, 0, 4}
  fmt.Println(canJump(nums2))
}


代码2:

回溯算法,从第一个位置开始,依次尝试跳到后面的每一个位置,如果遇到无法跳到的位置,则回溯到上一个位置,直到跳到终点或无法回溯为止。

package main
import "fmt"
func canJump(nums []int) bool {
  return backtrack(nums, 0)
}
func backtrack(nums []int, pos int) bool {
  if pos == len(nums)-1 {
    return true
  }
  furthestJump := min(pos+nums[pos], len(nums)-1)
  for nextPos := furthestJump; nextPos > pos; nextPos-- {
    if backtrack(nums, nextPos) {
      return true
    }
  }
  return false
}
func min(a, b int) int {
  if a < b {
    return a
  }
  return b
}
func main() {
  nums := []int{2, 3, 1, 1, 4}
  fmt.Println(canJump(nums))
  nums2 := []int{3, 2, 1, 0, 4}
  fmt.Println(canJump(nums2))
}

代码3:

动态规划,定义状态 dp[i] 表示能否从起点跳到第 i 个位置。转移方程为 dp[i] = dp[j] && j + nums[j] >= i,其中 j 为能够跳到的位置。

package main
import "fmt"
func canJump(nums []int) bool {
  n := len(nums)
  dp := make([]bool, n)
  dp[0] = true
  for i := 1; i < n; i++ {
    for j := 0; j < i; j++ {
      if dp[j] && j+nums[j] >= i {
        dp[i] = true
        break
      }
    }
  }
  return dp[n-1]
}
func main() {
  nums := []int{2, 3, 1, 1, 4}
  fmt.Println(canJump(nums))
  nums2 := []int{3, 2, 1, 0, 4}
  fmt.Println(canJump(nums2))
}


代码4:

反向贪心算法,从终点开始向前遍历,记录能够跳到终点的最小位置,如果当前位置能够跳到这个最小位置,则更新最小位置,最后判断起点是否能够跳到这个最小位置即可。

package main
import "fmt"
func canJump(nums []int) bool {
  n := len(nums)
  lastPos := n - 1
  for i := n - 2; i >= 0; i-- {
    if i+nums[i] >= lastPos {
      lastPos = i
    }
  }
  return lastPos == 0
}
func main() {
  nums := []int{2, 3, 1, 1, 4}
  fmt.Println(canJump(nums))
  nums2 := []int{3, 2, 1, 0, 4}
  fmt.Println(canJump(nums2))
}

输出:

true

false


56. 合并区间 Mmerge Intervals


以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。


示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]

输出:[[1,6],[8,10],[15,18]]

解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].


示例 2:

输入:intervals = [[1,4],[4,5]]

输出:[[1,5]]

解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。


提示:

   1 <= intervals.length <= 10^4

   intervals[i].length == 2

   0 <= starti <= endi <= 10^4


代码1:


排序 + 双指针 首先将区间按照起始位置从小到大排序,然后使用双指针对区间进行合并。具体来说,定义左右指针 left 和 right,初始时 left 指向第一个区间的起始位置,right 指向第一个区间的结束位置。然后依次遍历每个区间,如果当前区间的起始位置小于等于 right,则将 right 更新为当前区间的结束位置,否则将当前区间的起始位置和结束位置作为一个新区间加入结果集,并将左右指针更新为当前区间的起始位置和结束位置。最后将最后一个区间加入结果集即可。

func merge(intervals [][]int) [][]int {
    sort.Slice(intervals, func(i, j int) bool {
        return intervals[i][0] < intervals[j][0]
    })
    res := [][]int{}
    left, right := intervals[0][0], intervals[0][1]
    for i := 1; i < len(intervals); i++ {
        if intervals[i][0] <= right {
            if intervals[i][1] > right {
                right = intervals[i][1]
            }
        } else {
            res = append(res, []int{left, right})
            left, right = intervals[i][0], intervals[i][1]
        }
    }
    res = append(res, []int{left, right})
    return res
}



代码2:

排序 + 栈  首先将区间按照起始位置从小到大排序,然后使用栈对区间进行合并。具体来说,依次遍历每个区间,如果当前区间的起始位置小于等于栈顶区间的结束位置,则将栈顶区间的结束位置更新为当前区间的结束位置,否则将当前区间加入栈中。最后将栈中所有区间加入结果集即可。

1.func merge(intervals [][]int) [][]int {
    sort.Slice(intervals, func(i, j int) bool {
        return intervals[i][0] < intervals[j][0]
    })
    stack := [][]int{}
    for _, interval := range intervals {
        if len(stack) == 0 || interval[0] > stack[len(stack)-1][1] {
            stack = append(stack, interval)
        } else {
            stack[len(stack)-1][1] = max(stack[len(stack)-1][1], interval[1])
        }
    }
    return stack
}
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}



代码3:

排序 + 遍历 首先将区间按照起始位置从小到大排序,然后依次遍历每个区间,如果当前区间的起始位置小于等于结果集中最后一个区间的结束位置,则将结果集中最后一个区间的结束位置更新为当前区间的结束位置,否则将当前区间加入结果集。最后输出结果集即可。

func merge(intervals [][]int) [][]int {
    sort.Slice(intervals, func(i, j int) bool {
        return intervals[i][0] < intervals[j][0]
    })
    res := [][]int{}
    for _, interval := range intervals {
        if len(res) == 0 || interval[0] > res[len(res)-1][1] {
            res = append(res, interval)
        } else {
            res[len(res)-1][1] = max(res[len(res)-1][1], interval[1])
        }
    }
    return res
}
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}





57. 插入区间 Insert Interval


给你一个 无重叠的按照区间起始端点排序的区间列表。


在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。


示例 1:


输入:intervals = [[1,3],[6,9]], newInterval = [2,5]

输出:[[1,5],[6,9]]


示例 2:

输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]

输出:[[1,2],[3,10],[12,16]]

解释:这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。


示例 3:

输入:intervals = [], newInterval = [5,7]

输出:[[5,7]]


示例 4:

输入:intervals = [[1,5]], newInterval = [2,3]

输出:[[1,5]]


示例 5:

输入:intervals = [[1,5]], newInterval = [2,7]

输出:[[1,7]]


提示:

   0 <= intervals.length <= 10^4

   intervals[i].length == 2

   0 <= intervals[i][0] <= intervals[i][1] <= 10^5

   intervals 根据 intervals[i][0] 按 升序 排列

   newInterval.length == 2

   0 <= newInterval[0] <= newInterval[1] <= 10^5


代码1:

二分查找  先通过二分查找找到新区间需要插入的位置,然后按照方法一的方式插入新区间并合并重叠的区间。  

func insert(intervals [][]int, newInterval []int) [][]int {
    n := len(intervals)
    res := make([][]int, 0)
    // 找到新区间需要插入的位置
    left := 0
    right := n - 1
    for left <= right {
        mid := left + (right-left)/2
        if intervals[mid][1] < newInterval[0] {
            left = mid + 1
        } else {
            right = mid - 1
        }
    }
    insertIndex := left
    // 插入新区间
    i := 0
    for i < insertIndex {
        res = append(res, intervals[i])
        i++
    }
    if i < n && intervals[i][0] <= newInterval[1] {
        newInterval[0] = min(intervals[i][0], newInterval[0])
        newInterval[1] = max(intervals[i][1], newInterval[1])
        i++
    }
    res = append(res, newInterval)
    // 合并重叠的区间
    for i < n {
        if intervals[i][0] <= res[len(res)-1][1] {
            res[len(res)-1][1] = max(intervals[i][1], res[len(res)-1][1])
        } else {
            res = append(res, intervals[i])
        }
        i++
    }
    return res
}
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}



代码2:

双指针 由于区间列表是按照起始端点排序的,因此可以使用双指针的方法,分别找到新区间需要插入的位置和合并重叠的区间。

func insert(intervals [][]int, newInterval []int) [][]int {
    n := len(intervals)
    res := make([][]int, 0)
    // 找到新区间需要插入的位置
    i := 0
    for i < n && intervals[i][1] < newInterval[0] {
        res = append(res, intervals[i])
        i++
    }
    // 合并重叠的区间
    for i < n && intervals[i][0] <= newInterval[1] {
        newInterval[0] = min(intervals[i][0], newInterval[0])
        newInterval[1] = max(intervals[i][1], newInterval[1])
        i++
    }
    res = append(res, newInterval)
    // 合并剩余的区间
    for i < n {
        res = append(res, intervals[i])
        i++
    }
    // 再次合并重叠的区间
    n = len(res)
    i = 1
    for i < n {
        if res[i][0] <= res[i-1][1] {
            res[i-1][1] = max(res[i][1], res[i-1][1])
            res = append(res[:i], res[i+1:]...)
            n--
        } else {
            i++
        }
    }
    return res
}
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}



代码3:

暴力插入 遍历区间列表,找到新区间需要插入的位置,然后插入新区间。再遍历一遍区间列表,合并重叠的区间。

func insert(intervals [][]int, newInterval []int) [][]int {
    n := len(intervals)
    res := make([][]int, 0)
    // 找到新区间需要插入的位置
    i := 0
    for i < n && intervals[i][1] < newInterval[0] {
        res = append(res, intervals[i])
        i++
    }
    // 插入新区间
    if i < n && intervals[i][0] <= newInterval[1] {
        newInterval[0] = min(intervals[i][0], newInterval[0])
        newInterval[1] = max(intervals[i][1], newInterval[1])
        i++
    }
    res = append(res, newInterval)
    // 合并重叠的区间
    for i < n {
        if intervals[i][0] <= res[len(res)-1][1] {
            res[len(res)-1][1] = max(intervals[i][1], res[len(res)-1][1])
        } else {
            res = append(res, intervals[i])
        }
        i++
    }
    return res
}
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}






目录
相关文章
|
Shell Linux 算法
Shell编程——弱数据类型的脚本语言快速入门指南
Shell编程——弱数据类型的脚本语言快速入门指南
174 0
Shell编程——弱数据类型的脚本语言快速入门指南
|
Go Linux Shell
Linux 终端命令之文件浏览(2) more
Linux 终端命令之文件浏览(2) more
122 0
Linux 终端命令之文件浏览(2) more
|
Shell 机器学习/深度学习 Linux
Linux 终端操作命令(2)内部命令
Linux 终端操作命令(2)内部命令
226 0
Linux 终端操作命令(2)内部命令
|
C++ 算法 存储
力扣 C++|一题多解之动态规划专题(2)
力扣 C++|一题多解之动态规划专题(2)
175 0
力扣 C++|一题多解之动态规划专题(2)
|
Python 索引
Python Numpy入门基础(一)创建数组
Python Numpy入门基础(一)创建数组
204 0
Python Numpy入门基础(一)创建数组
|
Java 容器 程序员
Java语言程序设计试卷6套
Java语言程序设计试卷6套
1071 0
Java语言程序设计试卷6套
|
Java Go C++
Golang每日一练(leetDay0120) 反转字符串中的元音字母、前K个高频元素
Golang每日一练(leetDay0120) 反转字符串中的元音字母、前K个高频元素
179 0
Golang每日一练(leetDay0120) 反转字符串中的元音字母、前K个高频元素
|
Go 机器学习/深度学习 Rust
Golang每日一练(leetDay0119) 反转字符串I\II Reverse String
Golang每日一练(leetDay0119) 反转字符串I\II Reverse String
250 0
Golang每日一练(leetDay0119) 反转字符串I\II Reverse String
|
Java Go C++
Golang每日一练(leetDay0118) 扁平化嵌套列表迭代器、整数拆分
Golang每日一练(leetDay0118) 扁平化嵌套列表迭代器、整数拆分
167 0
Golang每日一练(leetDay0118) 扁平化嵌套列表迭代器、整数拆分
|
Java Go C++
Golang每日一练(leetDay0117) 打家劫舍III、比特位计数
Golang每日一练(leetDay0117) 打家劫舍III、比特位计数
140 0
Golang每日一练(leetDay0117) 打家劫舍III、比特位计数