Golang每日一练(leetDay0050) 对链表进行插入排序、排序链表、直线上最多的点、逆波兰表达式

简介: Golang每日一练(leetDay0050) 对链表进行插入排序、排序链表、直线上最多的点、逆波兰表达式

147. 对链表进行插入排序 Insertion Sort List

给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头

插入排序 算法的步骤:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

对链表进行插入排序。

示例 1:

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

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

示例 2:

输入: head = [-1,5,3,4,0]

输出: [-1,0,3,4,5]


提示:

  • 列表中的节点数在 [1, 5000]范围内
  • -5000 <= Node.val <= 5000

代码: 插入排序

package main
import "fmt"
type ListNode struct {
  Val  int
  Next *ListNode
}
func build(list []int) *ListNode {
  head := &ListNode{Val: -1 << 31} //-5000 <= Node.val <= 5000
  for i := len(list) - 1; i >= 0; i-- {
    p := &ListNode{Val: list[i]}
    p.Next = head.Next
    head.Next = p
  }
  return head
}
func (head *ListNode) travel() {
  for p := head.Next; p != nil; p = p.Next {
    fmt.Print(p.Val)
    if p.Next != nil {
      fmt.Print("->")
    }
  }
  fmt.Println("<nil>")
}
func insertionSortList(head *ListNode) *ListNode {
  if head == nil || head.Next == nil {
    return head
  }
  dummy := &ListNode{}
  dummy.Next = head
  cur := head.Next
  lastSorted := head
  for cur != nil {
    if cur.Val >= lastSorted.Val {
      lastSorted = lastSorted.Next
    } else {
      prev := dummy
      for prev.Next.Val < cur.Val {
        prev = prev.Next
      }
      lastSorted.Next = cur.Next
      cur.Next = prev.Next
      prev.Next = cur
    }
    cur = lastSorted.Next
  }
  return dummy.Next
}
func main() {
  nums := []int{4, 2, 1, 3}
  head := build(nums)
  head.travel()
  head = insertionSortList(head)
  head.travel()
  nums2 := []int{-1, 5, 3, 4, 0}
  head2 := build(nums2)
  head2.travel()
  head2 = insertionSortList(head2)
  head2.travel()
}

输出:

4->2->1->3

1->2->3->4

-1->5->3->4->0

-1->0->3->4->5


148. 排序链表 Sort List

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

示例 1:

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

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


示例 2:

输入:head = [-1,5,3,4,0]

输出:[-1,0,3,4,5]


示例 3:

输入:head = []

输出:[]

提示:

  • 链表中节点的数目在范围 [0, 5 * 10^4]
  • -10^5 <= Node.val <= 10^5

进阶:你可以在 O(nlogn) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

代码: 归并排序,与147题对比多了对时间和空间复杂度的要求

package main
import "fmt"
type ListNode struct {
  Val  int
  Next *ListNode
}
func build(list []int) *ListNode {
  head := &ListNode{Val: -1 << 31} //-5000 <= Node.val <= 5000
  for i := len(list) - 1; i >= 0; i-- {
    p := &ListNode{Val: list[i]}
    p.Next = head.Next
    head.Next = p
  }
  return head
}
func (head *ListNode) travel() {
  for p := head.Next; p != nil; p = p.Next {
    fmt.Print(p.Val)
    if p.Next != nil {
      fmt.Print("->")
    }
  }
  fmt.Println("<nil>")
}
func sortList(head *ListNode) *ListNode {
  if head == nil || head.Next == nil {
    return head
  }
  slow, fast := head, head.Next
  for fast != nil && fast.Next != nil {
    slow = slow.Next
    fast = fast.Next.Next
  }
  mid := slow.Next
  slow.Next = nil
  left := sortList(head)
  right := sortList(mid)
  return merge(left, right)
}
func merge(a, b *ListNode) *ListNode {
  dummy := &ListNode{}
  cur := dummy
  for a != nil && b != nil {
    if a.Val < b.Val {
      cur.Next = a
      a = a.Next
    } else {
      cur.Next = b
      b = b.Next
    }
    cur = cur.Next
  }
  if a != nil {
    cur.Next = a
  } else {
    cur.Next = b
  }
  return dummy.Next
}
func main() {
  nums := []int{4, 2, 1, 3}
  head := build(nums)
  head.travel()
  head = sortList(head)
  head.travel()
  nums2 := []int{-1, 5, 3, 4, 0}
  head2 := build(nums2)
  head2.travel()
  head2 = sortList(head2)
  head2.travel()
}

输出:

4->2->1->3

1->2->3->4

-1->5->3->4->0

-1->0->3->4->5


149. 直线上最多的点数 Max Points On A Line

给你一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。

示例 1:

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

输出:3


示例 2:

输入:points = [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]

输出:4


提示:

  • 1 <= points.length <= 300
  • points[i].length == 2
  • -10^4 <= xi, yi <= 10^4
  • points 中的所有点 互不相同

代码1: 暴力枚举

package main
import (
  "fmt"
)
func maxPoints(points [][]int) int {
  if len(points) < 3 {
    return len(points)
  }
  ans := 0
  for i := 0; i < len(points); i++ {
    kMap := make(map[Fraction]int)
    same := 1
    for j := i + 1; j < len(points); j++ {
      dx, dy := points[i][0]-points[j][0], points[i][1]-points[j][1]
      if dx == 0 && dy == 0 {
        same++
        continue
      }
      gcd := GCD(dx, dy)
      f := Fraction{
        Numerator:   dy / gcd,
        Denominator: dx / gcd,
      }
      kMap[f]++
    }
    maxCnt := 0
    for _, cnt := range kMap {
      if cnt > maxCnt {
        maxCnt = cnt
      }
    }
    ans = max(ans, maxCnt+same)
  }
  return ans
}
type Fraction struct {
  Numerator   int
  Denominator int
}
func GCD(a, b int) int {
  if b == 0 {
    return a
  }
  return GCD(b, a%b)
}
func max(a, b int) int {
  if a > b {
    return a
  }
  return b
}
func main() {
  points := [][]int{{1, 1}, {2, 2}, {3, 3}}
  fmt.Println(maxPoints(points))
  points = [][]int{{1, 1}, {3, 2}, {5, 3}, {4, 1}, {2, 3}, {1, 4}}
  fmt.Println(maxPoints(points))
}

代码2: 哈希表

package main
import (
  "fmt"
)
func maxPoints(points [][]int) int {
  if len(points) < 2 {
    return len(points)
  }
  ans := 1
  for i, p := range points {
    if ans >= len(points)-i {
      break
    }
    cnt := map[fraction]int{}
    for _, q := range points[i+1:] {
      k := newFraction(p, q)
      cnt[*k]++
    }
    for _, c := range cnt {
      ans = max(ans, c+1)
    }
  }
  return ans
}
type fraction struct {
  dx, dy int
}
func newFraction(p, q []int) *fraction {
  dx, dy := p[1]-q[1], p[0]-q[0]
  if dx == 0 {
    dy = 1
  } else if dy == 0 {
    dx = 1
  } else if dy < 0 {
    dx, dy = -dx, -dy
  }
  g := gcd(abs(dx), abs(dy))
  return &fraction{dx / g, dy / g}
}
func gcd(a, b int) int {
  for b != 0 {
    a, b = b, a%b
  }
  return a
}
func abs(x int) int {
  if x < 0 {
    return -x
  }
  return x
}
func max(a, b int) int {
  if a > b {
    return a
  }
  return b
}
func main() {
  points := [][]int{{1, 1}, {2, 2}, {3, 3}}
  fmt.Println(maxPoints(points))
  points = [][]int{{1, 1}, {3, 2}, {5, 3}, {4, 1}, {2, 3}, {1, 4}}
  fmt.Println(maxPoints(points))
}

输出:

3

4


150. 逆波兰表达式求值 Evaluate Reverse Polish Notation

根据 逆波兰表示法,求表达式的值。

有效的算符包括 +-*/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

注意 两个整数之间的除法只保留整数部分。

可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:

输入:tokens = ["2","1","+","3","*"]

输出:9

解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9


示例 2:

输入:tokens = ["4","13","5","/","+"]

输出:6

解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6


示例 3:

输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]

输出:22

解释:该算式转化为常见的中缀算术表达式为:

 ((10 * (6 / ((9 + 3) * -11))) + 17) + 5

= ((10 * (6 / (12 * -11))) + 17) + 5

= ((10 * (6 / -132)) + 17) + 5

= ((10 * 0) + 17) + 5

= (0 + 17) + 5

= 17 + 5

= 22


提示:

  • 1 <= tokens.length <= 10^4
  • tokens[i] 是一个算符("+""-""*""/"),或是在范围 [-200, 200] 内的一个整数

逆波兰表达式:

逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。

  • 平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 )
  • 该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * )

逆波兰表达式主要有以下两个优点:

  • 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
  • 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

代码1: 栈 stack

package main
import (
  "fmt"
  "strconv"
)
func evalRPN(tokens []string) int {
  stack := make([]int, 0)
  for _, t := range tokens {
    if t == "+" {
      a, b := stack[len(stack)-2], stack[len(stack)-1]
      stack = stack[:len(stack)-2]
      stack = append(stack, a+b)
    } else if t == "-" {
      a, b := stack[len(stack)-2], stack[len(stack)-1]
      stack = stack[:len(stack)-2]
      stack = append(stack, a-b)
    } else if t == "*" {
      a, b := stack[len(stack)-2], stack[len(stack)-1]
      stack = stack[:len(stack)-2]
      stack = append(stack, a*b)
    } else if t == "/" {
      a, b := stack[len(stack)-2], stack[len(stack)-1]
      stack = stack[:len(stack)-2]
      stack = append(stack, a/b)
    } else {
      num, _ := strconv.Atoi(t)
      stack = append(stack, num)
    }
  }
  return stack[0]
}
func main() {
  tokens := []string{"2", "1", "+", "3", "*"}
  fmt.Println(evalRPN(tokens))
  tokens = []string{"4", "13", "5", "/", "+"}
  fmt.Println(evalRPN(tokens))
  tokens = []string{"10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"}
  fmt.Println(evalRPN(tokens))
}

代码2: DFS 递归

package main
import (
  "fmt"
  "strconv"
)
func evalRPN(tokens []string) int {
  var dfs func() int
  dfs = func() int {
    if len(tokens) == 0 {
      return 0
    }
    op := tokens[len(tokens)-1]
    tokens = tokens[:len(tokens)-1]
    if op == "+" {
      return dfs() + dfs()
    } else if op == "-" {
      a, b := dfs(), dfs()
      return b - a
    } else if op == "*" {
      return dfs() * dfs()
    } else if op == "/" {
      a, b := dfs(), dfs()
      return b / a
    } else {
      num, _ := strconv.Atoi(op)
      return num
    }
  }
  return dfs()
}
func main() {
  tokens := []string{"2", "1", "+", "3", "*"}
  fmt.Println(evalRPN(tokens))
  tokens = []string{"4", "13", "5", "/", "+"}
  fmt.Println(evalRPN(tokens))
  tokens = []string{"10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"}
  fmt.Println(evalRPN(tokens))
}

输出:

9

6

22


🌟 每日一练刷题专栏 🌟

持续,努力奋斗做强刷题搬运工!

👍 点赞,你的认可是我坚持的动力!

🌟 收藏,你的青睐是我努力的方向!

评论,你的意见是我进步的财富!  

主页:https://hannyang.blog.csdn.net/


目录
相关文章
|
2月前
|
搜索推荐 编译器 C语言
【C++核心】特殊的元素集合-数组与字符串详解
这篇文章详细讲解了C++中数组和字符串的基本概念、操作和应用,包括一维数组、二维数组的定义和使用,以及C风格字符串和C++字符串类的对比。
80 4
|
1月前
|
缓存 网络协议 API
C/C++ StringToAddress(字符串转 boost::asio::ip::address)
通过上述步骤和示例代码,你可以轻松地在C++项目中实现从字符串到 `boost::asio::ip::address`的转换,从而充分利用Boost.Asio库进行网络编程。
52 0
|
1月前
|
编译器 C语言 C++
C/C++数字与字符串互相转换
C/C++数字与字符串互相转换
|
2月前
|
C++
HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具
HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具
|
2月前
|
存储 C++
C++(五)String 字符串类
本文档详细介绍了C++中的`string`类,包括定义、初始化、字符串比较及数值与字符串之间的转换方法。`string`类简化了字符串处理,提供了丰富的功能如字符串查找、比较、拼接和替换等。文档通过示例代码展示了如何使用这些功能,并介绍了如何将数值转换为字符串以及反之亦然的方法。此外,还展示了如何使用`string`数组存储和遍历多个字符串。
|
4月前
|
算法 C++
2730. 找到最长的半重复子字符串(c++,滑动窗口)
2730. 找到最长的半重复子字符串(c++,滑动窗口)
|
4月前
|
C++
567. 字符串的排列(c++)滑动窗口
567. 字符串的排列(c++)滑动窗口
|
4月前
|
编译器 C++
【C++】string类的使用④(字符串操作String operations )
这篇博客探讨了C++ STL中`std::string`的几个关键操作,如`c_str()`和`data()`,它们分别返回指向字符串的const char*指针,前者保证以&#39;\0&#39;结尾,后者不保证。`get_allocator()`返回内存分配器,通常不直接使用。`copy()`函数用于将字符串部分复制到字符数组,不添加&#39;\0&#39;。`find()`和`rfind()`用于向前和向后搜索子串或字符。`npos`是string类中的一个常量,表示找不到匹配项时的返回值。博客通过实例展示了这些函数的用法。
|
5月前
|
C++ 容器
C++字符串string容器(构造、赋值、拼接、查找、替换、比较、存取、插入、删除、子串)
C++字符串string容器(构造、赋值、拼接、查找、替换、比较、存取、插入、删除、子串)
|
5月前
|
编译器 C++
【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
38 1