Golang每日一练(leetDay0033) 二叉树专题(2)

简介: Golang每日一练(leetDay0033) 二叉树专题(2)

二叉树专题(2)第97题除外




97. 交错字符串 Interleaving String


给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。

两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:


69e03ba937b8b7fd673bf493606fe127.jpeg


输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"

输出:true


示例 2:

输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"

输出:false


示例 3:

输入:s1 = "", s2 = "", s3 = ""

输出:true


提示:

   0 <= s1.length, s2.length <= 100

   0 <= s3.length <= 200

   s1、s2、和 s3 都由小写英文字母组成


进阶:您能否仅使用 O(s2.length) 额外的内存空间来解决它?

代码1:动态规划


package main
import (
  "fmt"
)
func isInterleave(s1 string, s2 string, s3 string) bool {
  if len(s1)+len(s2) != len(s3) {
    return false
  }
  dp := make([][]bool, len(s1)+1)
  for i := range dp {
    dp[i] = make([]bool, len(s2)+1)
  }
  dp[0][0] = true
  for i := 1; i <= len(s1); i++ {
    dp[i][0] = dp[i-1][0] && s1[i-1] == s3[i-1]
  }
  for j := 1; j <= len(s2); j++ {
    dp[0][j] = dp[0][j-1] && s2[j-1] == s3[j-1]
  }
  for i := 1; i <= len(s1); i++ {
    for j := 1; j <= len(s2); j++ {
      if s1[i-1] == s3[i+j-1] {
        dp[i][j] = dp[i][j] || dp[i-1][j]
      }
      if s2[j-1] == s3[i+j-1] {
        dp[i][j] = dp[i][j] || dp[i][j-1]
      }
    }
  }
  return dp[len(s1)][len(s2)]
}
func main() {
  s1 := "aabcc"
  s2 := "dbbca"
  s3 := "aadbbcbcac"
  fmt.Println(isInterleave(s1, s2, s3))
  s1 = "aabcc"
  s2 = "dbbca"
  s3 = "aadbbbaccc"
  fmt.Println(isInterleave(s1, s2, s3))
  s1 = ""
  s2 = ""
  s3 = ""
  fmt.Println(isInterleave(s1, s2, s3))
}



输出:

true

false

true

代码2:广度优先搜索

package main
import (
  "fmt"
)
func isInterleave(s1 string, s2 string, s3 string) bool {
  if len(s1)+len(s2) != len(s3) {
    return false
  }
  queue := [][]int{{0, 0}}
  visited := make([][]bool, len(s1)+1)
  for i := range visited {
    visited[i] = make([]bool, len(s2)+1)
  }
  for len(queue) > 0 {
    i, j := queue[0][0], queue[0][1]
    queue = queue[1:]
    if visited[i][j] {
      continue
    }
    visited[i][j] = true
    if i == len(s1) && j == len(s2) {
      return true
    }
    if i < len(s1) && s1[i] == s3[i+j] {
      queue = append(queue, []int{i + 1, j})
    }
    if j < len(s2) && s2[j] == s3[i+j] {
      queue = append(queue, []int{i, j + 1})
    }
  }
  return false
}
func main() {
  s1 := "aabcc"
  s2 := "dbbca"
  s3 := "aadbbcbcac"
  fmt.Println(isInterleave(s1, s2, s3))
  s1 = "aabcc"
  s2 = "dbbca"
  s3 = "aadbbbaccc"
  fmt.Println(isInterleave(s1, s2, s3))
  s1 = ""
  s2 = ""
  s3 = ""
  fmt.Println(isInterleave(s1, s2, s3))
}



98. 验证二叉搜索树 Validate Binary Search Tree


给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。


有效 二叉搜索树定义如下:


  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:


9f8afdea7f201b0c335c945c7327d066.jpeg



输入:root = [2,1,3]

输出:true


示例 2:

f7ce0b9cecd18bc6690858b4222ccecf.jpeg

输入:root = [5,1,4,null,null,3,6]

输出:false

解释:根节点的值是 5 ,但是右子节点的值是 4 。



提示:

  • 树中节点数目范围在[1, 10^4]
  • -2^31 <= Node.val <= 2^31 - 1

代码1:

package main
import (
  "fmt"
)
const null = -1 << 31
type TreeNode struct {
  Val   int
  Left  *TreeNode
  Right *TreeNode
}
func isValidBST(root *TreeNode) bool {
  var pre *TreeNode
  return validate(root, &pre)
}
func validate(node *TreeNode, pre **TreeNode) bool {
  if node == nil {
    return true
  }
  if !validate(node.Left, pre) {
    return false
  }
  if *pre != nil && node.Val <= (*pre).Val {
    return false
  }
  *pre = node
  return validate(node.Right, pre)
}
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
}
func (root *TreeNode) LevelOrder() []int {
  var res []int
  if root == nil {
    return res
  }
  Queue := []*TreeNode{root}
  for len(Queue) > 0 {
    cur := Queue[0]
    Queue = Queue[1:]
    res = append(res, cur.Val)
    if cur.Left != nil {
      Queue = append(Queue, cur.Left)
    }
    if cur.Right != nil {
      Queue = append(Queue, cur.Right)
    }
  }
  return res
}
func main() {
  nums := []int{2, 1, 3}
  root := buildTree(nums)
  fmt.Println(isValidBST(root))
  nums = []int{5, 1, 4, null, null, 3, 6}
  root = buildTree(nums)
  fmt.Println(isValidBST(root))
  nums = []int{3, 1, 5, null, null, 4, 6}
  root = buildTree(nums)
  fmt.Println(isValidBST(root))
}



输出:

true

false

true

代码2: 递归法

package main
import (
  "fmt"
)
const null = -1 << 31
type TreeNode struct {
  Val   int
  Left  *TreeNode
  Right *TreeNode
}
func isValidBST(root *TreeNode) bool {
  return validate(root, nil, nil)
}
func validate(node *TreeNode, min *int, max *int) bool {
  if node == nil {
    return true
  }
  if (min != nil && node.Val <= *min) || (max != nil && node.Val >= *max) {
    return false
  }
  return validate(node.Left, min, &node.Val) && validate(node.Right, &node.Val, max)
}
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
}
func main() {
  nums := []int{2, 1, 3}
  root := buildTree(nums)
  fmt.Println(isValidBST(root))
  nums = []int{5, 1, 4, null, null, 3, 6}
  root = buildTree(nums)
  fmt.Println(isValidBST(root))
  nums = []int{3, 1, 5, null, null, 4, 6}
  root = buildTree(nums)
  fmt.Println(isValidBST(root))
}


代码3: 中序遍历+判断

package main
import (
  "fmt"
)
const null = -1 << 31
type TreeNode struct {
  Val   int
  Left  *TreeNode
  Right *TreeNode
}
func isValidBST(root *TreeNode) bool {
  var pre *TreeNode
  stack := []*TreeNode{}
  for root != nil || len(stack) > 0 {
    for root != nil {
      stack = append(stack, root)
      root = root.Left
    }
    node := stack[len(stack)-1]
    stack = stack[:len(stack)-1]
    if pre != nil && node.Val <= pre.Val {
      return false
    }
    pre = node
    root = node.Right
  }
  return true
}
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
}
func main() {
  nums := []int{2, 1, 3}
  root := buildTree(nums)
  fmt.Println(isValidBST(root))
  nums = []int{5, 1, 4, null, null, 3, 6}
  root = buildTree(nums)
  fmt.Println(isValidBST(root))
  nums = []int{3, 1, 5, null, null, 4, 6}
  root = buildTree(nums)
  fmt.Println(isValidBST(root))
}




99. 恢复二叉搜索树 Recover Binary Search Tree

给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树

示例 1:


cffc11b04e083586263564a57a1fd163.jpeg


输入:root = [1,3,null,null,2]

输出:[3,1,null,null,2]

解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。


示例 2:

9aa031e3047c2b6c15ec7275d6300859.jpeg



输入:root = [3,1,4,null,null,2]

输出:[2,1,4,null,null,3]

解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。


提示:

   树上节点的数目在范围 [2, 1000] 内

   -2^31 <= Node.val <= 2^31 - 1


进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用 O(1) 空间的解决方案吗?


代码1:中序遍历+交换节点值


对于二叉搜索树,中序遍历得到的序列是递增的。因此,如果有两个节点的值被错误地交换了,那么在中序遍历序列中一定存在两个相邻的逆序对。具体做法是,在中序遍历的过程中,用一个变量pre记录上一个遍历到的节点,每次遍历到一个节点时,判断其值是否小于pre的值,如果小于,则说明存在逆序对,记录下这两个节点,并继续遍历。最后,交换这两个节点的值即可。


package main
import (
  "fmt"
  "strconv"
)
const null = -1 << 31
type TreeNode struct {
  Val   int
  Left  *TreeNode
  Right *TreeNode
}
func recoverTree(root *TreeNode) {
  var pre, first, second *TreeNode
  var stack []*TreeNode
  for root != nil || len(stack) > 0 {
    for root != nil {
      stack = append(stack, root)
      root = root.Left
    }
    node := stack[len(stack)-1]
    stack = stack[:len(stack)-1]
    if pre != nil && node.Val < pre.Val {
      if first == nil {
        first = pre
      }
      second = node
    }
    pre = node
    root = node.Right
  }
  first.Val, second.Val = second.Val, first.Val
}
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
}
func levelOrder(root *TreeNode) string {
  if root == nil {
    return "[]"
  }
  arr := []int{}
  que := []*TreeNode{root}
  for len(que) > 0 {
    levelSize := len(que)
    for i := 0; i < levelSize; i++ {
      node := que[0]
      que = que[1:]
      if node == nil {
        arr = append(arr, null)
        continue
      }
      arr = append(arr, node.Val)
      que = append(que, node.Left, node.Right)
    }
  }
  size := len(arr)
  for size > 0 && arr[size-1] == null {
    arr = arr[:size-1]
    size = len(arr)
  }
  result := "["
  for i, n := range arr {
    if n == null {
      result += "null"
    } else {
      result += strconv.FormatInt(int64(n), 10)
    }
    if i < size-1 {
      result += ","
    } else {
      result += "]"
    }
  }
  return result
}
func main() {
  nums := []int{1, 3, null, null, 2}
  root := buildTree(nums)
  fmt.Println(levelOrder(root))
  recoverTree(root)
  fmt.Println(levelOrder(root))
  nums = []int{3, 1, 4, null, null, 2}
  root = buildTree(nums)
  fmt.Println(levelOrder(root))
  recoverTree(root)
  fmt.Println(levelOrder(root))
}


输出:

[1,3,null,null,2]

[3,1,null,null,2]

[3,1,4,null,null,2]

[2,1,4,null,null,3]



代码2:Morris遍历


Morris遍历是一种不需要额外空间的遍历二叉树的方法,它的核心思想是利用叶子节点的空指针来存储遍历中的临时信息。对于二叉搜索树,Morris中序遍历的过程中,每个节点的左子树都已经被遍历完毕,因此可以在遍历到每个节点时,比较它的值和它的前驱节点的值,如果它的值小于前驱节点的值,那么就找到了一个逆序对。我们用两个指针first和second来记录这两个节点,最后交换它们的值即可。


package main
import (
  "fmt"
  "strconv"
)
const null = -1 << 31
type TreeNode struct {
  Val   int
  Left  *TreeNode
  Right *TreeNode
}
func recoverTree(root *TreeNode) {
  var first, second, pre *TreeNode
  var temp *TreeNode
  for root != nil {
    if root.Left != nil {
      temp = root.Left
      for temp.Right != nil && temp.Right != root {
        temp = temp.Right
      }
      if temp.Right == nil {
        temp.Right = root
        root = root.Left
      } else {
        if pre != nil && root.Val < pre.Val {
          if first == nil {
            first = pre
          }
          second = root
        }
        pre = root
        temp.Right = nil
        root = root.Right
      }
    } else {
      if pre != nil && root.Val < pre.Val {
        if first == nil {
          first = pre
        }
        second = root
      }
      pre = root
      root = root.Right
    }
  }
  first.Val, second.Val = second.Val, first.Val
}
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
}
func levelOrder(root *TreeNode) string {
  if root == nil {
    return "[]"
  }
  arr := []int{}
  que := []*TreeNode{root}
  for len(que) > 0 {
    levelSize := len(que)
    for i := 0; i < levelSize; i++ {
      node := que[0]
      que = que[1:]
      if node == nil {
        arr = append(arr, null)
        continue
      }
      arr = append(arr, node.Val)
      que = append(que, node.Left, node.Right)
    }
  }
  size := len(arr)
  for size > 0 && arr[size-1] == null {
    arr = arr[:size-1]
    size = len(arr)
  }
  result := "["
  for i, n := range arr {
    if n == null {
      result += "null"
    } else {
      result += strconv.FormatInt(int64(n), 10)
    }
    if i < size-1 {
      result += ","
    } else {
      result += "]"
    }
  }
  return result
}
func main() {
  nums := []int{1, 3, null, null, 2}
  root := buildTree(nums)
  fmt.Println(levelOrder(root))
  recoverTree(root)
  fmt.Println(levelOrder(root))
  nums = []int{3, 1, 4, null, null, 2}
  root = buildTree(nums)
  fmt.Println(levelOrder(root))
  recoverTree(root)
  fmt.Println(levelOrder(root))
}




目录
相关文章
|
Shell Linux 算法
Shell编程——弱数据类型的脚本语言快速入门指南
Shell编程——弱数据类型的脚本语言快速入门指南
182 0
Shell编程——弱数据类型的脚本语言快速入门指南
|
Go Linux Shell
Linux 终端命令之文件浏览(2) more
Linux 终端命令之文件浏览(2) more
136 0
Linux 终端命令之文件浏览(2) more
|
Shell 机器学习/深度学习 Linux
Linux 终端操作命令(2)内部命令
Linux 终端操作命令(2)内部命令
243 0
Linux 终端操作命令(2)内部命令
|
C++ 算法 存储
力扣 C++|一题多解之动态规划专题(2)
力扣 C++|一题多解之动态规划专题(2)
193 0
力扣 C++|一题多解之动态规划专题(2)
|
Python 索引
Python Numpy入门基础(一)创建数组
Python Numpy入门基础(一)创建数组
225 0
Python Numpy入门基础(一)创建数组
|
Java 容器 程序员
Java语言程序设计试卷6套
Java语言程序设计试卷6套
1120 0
Java语言程序设计试卷6套
|
16天前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
68 1
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
522 4
Golang语言之管道channel快速入门篇
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
205 4
Golang语言文件操作快速入门篇
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
452 3
Golang语言之gRPC程序设计示例

热门文章

最新文章

推荐镜像

更多