叠盘子排序 | 豆包MarsCode AI刷题

74 阅读6分钟

叠盘子排序 | 豆包MarsCode AI刷题

叠盘子排序 - MarsCode

一开始我使用队列来做,其实完全没有必要,用两个指针就可以了,记录一下

以及学习了strings.Join的使用,放在文章末尾


问题描述

小M有一个独特的方式来收拾家中的盘子。每次用餐后,他会将盘子按照他们的序号顺序叠放。盘子的序号都是唯一的整数,并且在收拾前就是递增的。小M的叠放规则是,每一堆盘子的序号都是连续递增的,并且至少包含3个盘子。需要编写程序帮助小M确定盘子的叠放方式。

例如,输入的盘子序号是 [-3, -2, -1, 2, 10, 15, 16, 18, 19, 20],按照小M的规则,连续递增序列 -3, -2, -1 可以叠在一起表示为 -3--1,而 18, 19, 20 可以叠在一起表示为 18-20。不满足连续递增至少3个的,如 2, 10, 15, 16 都应单独列出。


输入参数

  • plates: 一个整数数组,表示盘子的序号。
  • n: 盘子总数。

测试样例

样例1

输入:

plates = [-3, -2, -1, 2, 10, 15, 16, 18, 19, 20]
n = 10

输出:

"-3--1,2,10,15,16,18-20"
样例2

输入:

plates = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
n = 20

输出:

"-6,-3-1,3-5,7-11,14,15,17-20"
样例3

输入:

plates = [1, 2, 7, 8, 9, 10, 11, 19]
n = 8

输出:

"1,2,7-11,19"

算法原理

解题思路
  1. 遍历盘子序列

    • 遍历 plates 数组,识别出连续递增的子序列。
  2. 判断子序列长度

    • 如果子序列长度大于等于3,则将其按照 start-end 的格式存储。
    • 如果子序列长度小于3,则逐个列出所有盘子的序号。
  3. 处理最后一个子序列

    • 遍历完成后,检查最后一个子序列,进行同样的长度判断和格式存储。
  4. 输出结果

    • 将所有子序列结果用 , 拼接,形成最终字符串。

时间复杂度

  1. 遍历数组的时间复杂度:O(n)O(n)
  2. 处理结果的时间复杂度:O(n)O(n)

总时间复杂度O(n)O(n)


空间复杂度

  1. 存储结果列表的空间复杂度:O(n)O(n)
  2. 其他常数级变量。

总空间复杂度O(n)O(n)


Go 实现

package main

import (
	"fmt"
	"strconv"
	"strings"
)

func solution(plates []int, n int) string {
	if n == 0 {
		return ""
	}

	// 初始化结果集和当前区间的起点和终点
	var result []string
	start := plates[0]
	end := plates[0]

	for i := 1; i < n; i++ {
		if plates[i] == plates[i-1]+1 { // 如果当前盘子与前一个盘子连续
			end = plates[i]
		} else {
			// 处理当前区间
			if end-start >= 2 { // 区间长度 >= 3
				result = append(result, fmt.Sprintf("%d-%d", start, end))
			} else { // 区间长度 < 3,逐个输出
				for j := start; j <= end; j++ {
					result = append(result, strconv.Itoa(j))
				}
			}
			// 开始新的区间
			start = plates[i]
			end = plates[i]
		}
	}

	// 处理最后一个区间
	if end-start >= 2 { // 区间长度 >= 3
		result = append(result, fmt.Sprintf("%d-%d", start, end))
	} else { // 区间长度 < 3,逐个输出
		for j := start; j <= end; j++ {
			result = append(result, strconv.Itoa(j))
		}
	}

	// 合并结果为字符串
	return strings.Join(result, ",")
}

func main() {
	// 测试用例
	fmt.Println(solution([]int{-3, -2, -1, 2, 10, 15, 16, 18, 19, 20}, 10) == "-3--1,2,10,15,16,18-20")
	fmt.Println(solution([]int{-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20}, 20) == "-6,-3-1,3-5,7-11,14,15,17-20")
	fmt.Println(solution([]int{1, 2, 7, 8, 9, 10, 11, 19}, 8) == "1,2,7-11,19")
}

Python 实现

def solution(plates, n):
    if n == 0:
        return ""

    result = []  # 存储最终结果
    start = plates[0]  # 当前区间的起点
    end = plates[0]  # 当前区间的终点

    for i in range(1, n):
        if plates[i] == plates[i - 1] + 1:  # 如果连续
            end = plates[i]
        else:
            # 处理当前区间
            if end - start >= 2:  # 如果区间长度 >= 3
                result.append(f"{start}-{end}")
            else:  # 否则逐个输出
                result.extend(map(str, range(start, end + 1)))
            # 开始新的区间
            start = plates[i]
            end = plates[i]

    # 处理最后一个区间
    if end - start >= 2:  # 如果区间长度 >= 3
        result.append(f"{start}-{end}")
    else:  # 否则逐个输出
        result.extend(map(str, range(start, end + 1)))

    # 返回结果字符串
    return ",".join(result)


if __name__ == "__main__":
    # 测试用例
    print(solution([-3, -2, -1, 2, 10, 15, 16, 18, 19, 20], 10) == "-3--1,2,10,15,16,18-20")
    print(solution([-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20], 20) == "-6,-3-1,3-5,7-11,14,15,17-20")
    print(solution([1, 2, 7, 8, 9, 10, 11, 19], 8) == "1,2,7-11,19")

strings.Join

在 Go 中,strings.Join 是一个非常常用的函数,用来将字符串切片中的元素连接成一个单一的字符串,并且可以指定连接时使用的分隔符。


函数签名

func Join(elems []string, sep string) string
  • 参数:

    • elems []string:字符串切片,表示要连接的字符串数组。
    • sep string:分隔符,指定在连接时用来分隔字符串的内容。
  • 返回值:

    • 返回一个字符串,包含 elems 中的所有元素,并用 sep 分隔。

示例代码

示例 1:简单连接字符串
package main

import (
	"fmt"
	"strings"
)

func main() {
	// 定义一个字符串切片
	words := []string{"Go", "is", "awesome"}

	// 使用 strings.Join
	result := strings.Join(words, " ")
	fmt.Println(result)
}
输出:
Go is awesome
说明:
  • words 是一个字符串切片,包含 "Go", "is", "awesome"
  • 使用 " " 作为分隔符,最终得到 "Go is awesome"

示例 2:用逗号分隔
package main

import (
	"fmt"
	"strings"
)

func main() {
	// 定义一个字符串切片
	words := []string{"apple", "banana", "cherry"}

	// 使用 strings.Join
	result := strings.Join(words, ", ")
	fmt.Println(result)
}
输出:
apple, banana, cherry

示例 3:没有分隔符
package main

import (
	"fmt"
	"strings"
)

func main() {
	// 定义一个字符串切片
	words := []string{"123", "456", "789"}

	// 使用 strings.Join,无分隔符
	result := strings.Join(words, "")
	fmt.Println(result)
}
输出:
123456789

示例 4:空切片处理

如果传入的是空切片,strings.Join 返回空字符串。

package main

import (
	"fmt"
	"strings"
)

func main() {
	// 定义一个空切片
	var words []string

	// 使用 strings.Join
	result := strings.Join(words, ", ")
	fmt.Println(result) // 输出为空字符串
}
输出:
<空白>

注意事项

  1. 性能:

    • strings.Join 在底层会一次性计算出最终字符串的长度,然后创建最终字符串。这比手动拼接字符串效率更高。
  2. 参数类型:

    • elems 必须是 []string 类型。如果是其他类型的切片(如 []int),需要先转换成 []string,比如:

      package main
      
      import (
          "fmt"
          "strconv"
          "strings"
      )
      
      func main() {
          // 定义一个整数切片
          nums := []int{1, 2, 3, 4, 5}
      
          // 将整数切片转换为字符串切片
          strNums := make([]string, len(nums))
          for i, num := range nums {
              strNums[i] = strconv.Itoa(num)
          }
      
          // 使用 strings.Join
          result := strings.Join(strNums, ", ")
          fmt.Println(result)
      }
      
      输出:
      1, 2, 3, 4, 5
      
  3. 空分隔符:

    • 如果 sep 为空字符串,结果是直接拼接 elems 中的所有元素,没有任何分隔符。