1101-1200-Easy
1103.分糖果 II(3)
排排坐,分糖果。
我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友。
给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果。
然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,
依此类推,直到给最后一个小朋友 2 * n 颗糖果。
重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果。
注意,就算我们手中的剩下糖果数不够(不比前一次发出的糖果多),这些糖果也会全部发给当前的小朋友。
返回一个长度为 num_people、元素之和为 candies 的数组,
以表示糖果的最终分发情况(即 ans[i] 表示第 i 个小朋友分到的糖果数)。
示例 1:输入:candies = 7, num_people = 4 输出:[1,2,3,1]
解释:
第一次,ans[0] += 1,数组变为 [1,0,0,0]。
第二次,ans[1] += 2,数组变为 [1,2,0,0]。
第三次,ans[2] += 3,数组变为 [1,2,3,0]。
第四次,ans[3] += 1(因为此时只剩下 1 颗糖果),最终数组变为 [1,2,3,1]。
示例 2:输入:candies = 10, num_people = 3 输出:[5,2,3]
解释:
第一次,ans[0] += 1,数组变为 [1,0,0]。
第二次,ans[1] += 2,数组变为 [1,2,0]。
第三次,ans[2] += 3,数组变为 [1,2,3]。
第四次,ans[0] += 4,最终数组变为 [5,2,3]。
提示:
1 <= candies <= 10^9
1 <= num_people <= 1000
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
暴力法 |
O(n^(1/2)) |
O(n) |
02 |
暴力法 |
O(n^(1/2)) |
O(n) |
03 |
等差数列求和公式 |
O(n^(1/2)) |
O(n) |
func distributeCandies(candies int, num_people int) []int {
res := make([]int, num_people)
i := 0
count := 0
for candies > 0 {
count++
if candies >= count {
res[i%num_people] += count
} else {
res[i%num_people] += candies
}
i++
candies = candies - count
}
return res
}
#
func distributeCandies(candies int, num_people int) []int {
res := make([]int, num_people)
count := 1
for candies > 0 {
for i := 0; i < num_people; i++ {
if candies >= count {
res[i] = res[i] + count
candies = candies - count
} else {
res[i] = res[i] + candies
candies = 0
}
count++
}
}
return res
}
#
func distributeCandies(candies int, num_people int) []int {
res := make([]int, num_people)
times := 1
for times*(times+1)/2 <= candies {
times++
}
times--
last := candies - times*(times+1)/2
for i := 0; i < num_people; i++ {
n := times / num_people
if times%num_people > i {
n = n + 1
}
res[i] = n * (2*(i+1) + (n-1)*num_people) / 2
if times%num_people == i {
res[i] = res[i] + last
}
}
return res
}
1108.IP地址无效化(2)
给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本。
所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 "."。
示例 1:输入:address = "1.1.1.1" 输出:"1[.]1[.]1[.]1"
示例 2:输入:address = "255.100.50.0" 输出:"255[.]100[.]50[.]0"
提示:
给出的 address 是一个有效的 IPv4 地址
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
内置函数 |
O(n) |
O(n) |
02 |
遍历 |
O(n) |
O(n) |
func defangIPaddr(address string) string {
return strings.ReplaceAll(address, ".", "[.]")
}
#
func defangIPaddr(address string) string {
res := ""
for i := range address {
if address[i] == '.' {
res = res + "[.]"
} else {
res = res + string(address[i])
}
}
return res
}
1122.数组的相对排序(3)
给你两个数组,arr1 和 arr2,
arr2 中的元素各不相同
arr2 中的每个元素都出现在 arr1 中
对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。
未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。
示例:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]
提示:
arr1.length, arr2.length <= 1000
0 <= arr1[i], arr2[i] <= 1000
arr2 中的元素 arr2[i] 各不相同
arr2 中的每个元素 arr2[i] 都出现在 arr1 中
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
哈希辅助 |
O(nlog(n)) |
O(n) |
02 |
暴力法 |
O(n^2) |
O(1) |
03 |
数组辅助 |
O(n) |
O(1) |
func relativeSortArray(arr1 []int, arr2 []int) []int {
if len(arr2) == 0 {
sort.Ints(arr1)
return arr1
}
res := make([]int, 0)
m := make(map[int]int)
for i := range arr1 {
m[arr1[i]]++
}
for i := 0; i < len(arr2); i++ {
for j := 0; j < m[arr2[i]]; j++ {
res = append(res, arr2[i])
}
m[arr2[i]] = 0
}
tempArr := make([]int, 0)
for key, value := range m {
for value > 0 {
tempArr = append(tempArr, key)
value--
}
}
sort.Ints(tempArr)
res = append(res, tempArr...)
return res
}
#
func relativeSortArray(arr1 []int, arr2 []int) []int {
count := 0
for i := 0; i < len(arr2); i++ {
for j := count; j < len(arr1); j++ {
if arr2[i] == arr1[j] {
arr1[count], arr1[j] = arr1[j], arr1[count]
count++
}
}
}
sort.Ints(arr1[count:])
return arr1
}
#
func relativeSortArray(arr1 []int, arr2 []int) []int {
temp := make([]int, 1001)
for i := range arr1 {
temp[arr1[i]]++
}
count := 0
for i := range arr2 {
for temp[arr2[i]] > 0 {
arr1[count] = arr2[i]
temp[arr2[i]]--
count++
}
}
for i := 0; i < len(temp); i++ {
for temp[i] > 0 {
arr1[count] = i
temp[i]--
count++
}
}
return arr1
}
1128.等价多米诺骨牌对的数量(2)
给你一个由一些多米诺骨牌组成的列表 dominoes。
如果其中某一张多米诺骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌,我们就认为这两张牌是等价的。
形式上,dominoes[i] = [a, b] 和 dominoes[j] = [c, d]
等价的前提是 a==c 且 b==d,或是 a==d 且 b==c。
在 0 <= i < j < dominoes.length 的前提下,
找出满足 dominoes[i] 和 dominoes[j] 等价的骨牌对 (i, j) 的数量。
示例:输入:dominoes = [[1,2],[2,1],[3,4],[5,6]] 输出:1
提示:
1 <= dominoes.length <= 40000
1 <= dominoes[i][j] <= 9
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
哈希辅助 |
O(n) |
O(1) |
02 |
数组辅助 |
O(n) |
O(1) |
func numEquivDominoPairs(dominoes [][]int) int {
m := make(map[string]int)
for i := 0; i < len(dominoes); i++ {
a := dominoes[i][0]
b := dominoes[i][1]
if a > b {
a, b = b, a
}
m[fmt.Sprintf("%d,%d", a, b)]++
}
res := 0
for _, v := range m {
res = res + v*(v-1)/2
}
return res
}
#
func numEquivDominoPairs(dominoes [][]int) int {
res := 0
arr := make([]int, 101)
for i := 0; i < len(dominoes); i++ {
a := dominoes[i][0]
b := dominoes[i][1]
if a > b {
a, b = b, a
}
res = res + arr[a*10+b]
arr[a*10+b]++
}
return res
}
1137.第N个泰波那契数(3)
泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。
示例 1:输入:n = 4 输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4
示例 2:
输入:n = 25
输出:1389537
提示:
0 <= n <= 37
答案保证是一个 32 位整数,即 answer <= 2^31 - 1。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
动态规划 |
O(n) |
O(n) |
02 |
递推 |
O(n) |
O(1) |
03 |
递归 |
O(n) |
O(n) |
func tribonacci(n int) int {
arr := make([]int, n+3)
arr[0] = 0
arr[1] = 1
arr[2] = 1
for i := 3; i <= n; i++ {
arr[i] = arr[i-1] + arr[i-2] + arr[i-3]
}
return arr[n]
}
#
func tribonacci(n int) int {
if n == 0 {
return 0
}
if n == 1 || n == 2 {
return 1
}
a := 0
b := 1
c := 1
for i := 3; i <= n; i++ {
c, b, a = a+b+c, c, b
}
return c
}
#
var m map[int]int
func tribonacci(n int) int {
if m == nil {
m = make(map[int]int)
}
if n == 0 {
return 0
}
if n == 1 || n == 2 {
return 1
}
if value, ok := m[n]; ok {
return value
} else {
value := tribonacci(n-1) + tribonacci(n-2) + tribonacci(n-3)
m[n] = value
}
return m[n]
}
1154.一年中的第几天(2)
给你一个按 YYYY-MM-DD 格式表示日期的字符串 date,请你计算并返回该日期是当年的第几天。
通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 2 天,依此类推。
每个月的天数与现行公元纪年法(格里高利历)一致。
示例 1:输入:date = "2019-01-09" 输出:9
示例 2:输入:date = "2019-02-10" 输出:41
示例 3:输入:date = "2003-03-01" 输出:60
示例 4:输入:date = "2004-03-01" 输出:61
提示:
date.length == 10
date[4] == date[7] == '-',其他的 date[i] 都是数字。
date 表示的范围从 1900 年 1 月 1 日至 2019 年 12 月 31 日。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
遍历 |
O(1) |
O(1) |
02 |
内置函数 |
O(1) |
O(1) |
func dayOfYear(date string) int {
arr := strings.Split(date, "-")
year, _ := strconv.Atoi(arr[0])
month, _ := strconv.Atoi(arr[1])
day, _ := strconv.Atoi(arr[2])
res := 0
for i := 0; i < month; i++ {
switch i {
case 1, 3, 5, 7, 8, 10, 12:
res = res + 31
case 4, 6, 9, 11:
res = res + 30
case 2:
res = res + 28
if year%400 == 0 || (year%4 == 0 && year%100 != 0) {
res = res + 1
}
}
}
res = res + day
return res
}
#
func dayOfYear(date string) int {
format := "2006-01-02"
t, _ := time.Parse(format, date)
return t.YearDay()
}
1160.拼写单词(3)
给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),
那么我们就认为你掌握了这个单词。
注意:每次拼写(指拼写词汇表中的一个单词)时,chars 中的每个字母都只能用一次。
返回词汇表 words 中你掌握的所有单词的 长度之和。
示例 1:输入:words = ["cat","bt","hat","tree"], chars = "atach" 输出:6
解释: 可以形成字符串 "cat" 和 "hat",所以答案是 3 + 3 = 6。
示例 2:输入:words = ["hello","world","leetcode"], chars = "welldonehoneyr" 输出:10
解释: 可以形成字符串 "hello" 和 "world",所以答案是 5 + 5 = 10。
提示:
1 <= words.length <= 1000
1 <= words[i].length, chars.length <= 100
所有字符串中都仅包含小写英文字母
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
哈希辅助 |
O(n^2) |
O(1) |
02 |
遍历-内置函数 |
O(n^2) |
O(1) |
03 |
数组辅助 |
O(n^2) |
O(1) |
func countCharacters(words []string, chars string) int {
m := make(map[byte]int)
for i := range chars {
m[chars[i]]++
}
res := 0
for i := 0; i < len(words); i++ {
temp := make(map[byte]int)
flag := true
for j := range words[i] {
temp[words[i][j]]++
}
if len(temp) > len(m) {
continue
}
for k, v := range temp {
if v > m[k] {
flag = false
break
}
}
if flag == true {
res = res + len(words[i])
}
}
return res
}
#
func countCharacters(words []string, chars string) int {
res := 0
for i := 0; i < len(words); i++ {
flag := true
for _, v := range words[i] {
if strings.Count(words[i], string(v)) > strings.Count(chars, string(v)) {
flag = false
continue
}
}
if flag == true {
res = res + len(words[i])
}
}
return res
}
#
func countCharacters(words []string, chars string) int {
m := make([]int, 26)
for i := range chars {
m[chars[i]-'a']++
}
res := 0
for i := 0; i < len(words); i++ {
temp := make([]int, 26)
flag := true
for j := range words[i] {
temp[words[i][j]-'a']++
}
if len(temp) > len(m) {
continue
}
for k, v := range temp {
if v > m[k] {
flag = false
break
}
}
if flag == true {
res = res + len(words[i])
}
}
return res
}
1170.比较字符串最小字母出现频次(2)
我们来定义一个函数 f(s),其中传入参数 s 是一个非空字符串;
该函数的功能是统计 s 中(按字典序比较)最小字母的出现频次。
例如,若 s = "dcce",那么 f(s) = 2,因为最小的字母是 "c",它出现了 2 次。
现在,给你两个字符串数组待查表 queries 和词汇表 words,请你返回一个整数数组 answer 作为答案,
其中每个 answer[i] 是满足 f(queries[i]) < f(W) 的词的数目,W 是词汇表 words 中的词。
示例 1:输入:queries = ["cbd"], words = ["zaaaz"] 输出:[1]
解释:查询 f("cbd") = 1,而 f("zaaaz") = 3 所以 f("cbd") < f("zaaaz")。
示例 2:输入:queries = ["bbb","cc"], words = ["a","aa","aaa","aaaa"] 输出:[1,2]
解释:第一个查询 f("bbb") < f("aaaa"),第二个查询 f("aaa") 和 f("aaaa") 都 > f("cc")。
提示:
1 <= queries.length <= 2000
1 <= words.length <= 2000
1 <= queries[i].length, words[i].length <= 10
queries[i][j], words[i][j] 都是小写英文字母
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
数组辅助 |
O(n^2) |
O(n) |
02 |
排序+二分查找 |
O(nlog(n)) |
O(n) |
func numSmallerByFrequency(queries []string, words []string) []int {
queriesArr := make([]int, len(queries))
wordsArr := make([]int, len(words))
res := make([]int, 0)
for i := 0; i < len(words); i++ {
wordsArr[i] = f(words[i])
}
for i := 0; i < len(queries); i++ {
queriesArr[i] = f(queries[i])
count := 0
for j := 0; j < len(wordsArr); j++ {
if queriesArr[i] < wordsArr[j] {
count++
}
}
res = append(res, count)
}
return res
}
func f(str string) int {
min := str[0]
count := 1
for i := 1; i < len(str); i++ {
if str[i] < min {
min = str[i]
count = 1
} else if str[i] == min{
count++
}
}
return count
}
#
func numSmallerByFrequency(queries []string, words []string) []int {
wordsArr := make([]int, len(words))
res := make([]int, 0)
for i := 0; i < len(words); i++ {
wordsArr[i] = f(words[i])
}
sort.Ints(wordsArr)
for i := 0; i < len(queries); i++ {
value := f(queries[i])
count := binarySearch(value, wordsArr)
res = append(res, count)
}
return res
}
func binarySearch(value int, target []int) int {
left := 0
right := len(target) - 1
for left < right {
mid := left + (right-left)/2
if target[mid] > value {
right = mid
} else {
left = mid + 1
}
}
if target[left] <= value {
return 0
}
return len(target) - left
}
func f(str string) int {
min := str[0]
count := 1
for i := 1; i < len(str); i++ {
if str[i] < min {
min = str[i]
count = 1
} else if str[i] == min {
count++
}
}
return count
}
1175.质数排列(1)
请你帮忙给从 1 到 n 的数设计排列方案,使得所有的「质数」都应该被放在「质数索引」(索引从 1 开始)上;
你需要返回可能的方案总数。
让我们一起来回顾一下「质数」:质数一定是大于 1 的,并且不能用两个小于它的正整数的乘积来表示。
由于答案可能会很大,所以请你返回答案 模 mod 10^9 + 7 之后的结果即可。
示例 1:输入:n = 5 输出:12
解释:举个例子,[1,2,5,4,3] 是一个有效的排列,但 [5,2,3,4,1] 不是,
因为在第二种情况里质数 5 被错误地放在索引为 1 的位置上。
示例 2:输入:n = 100 输出:682289015
提示:
1 <= n <= 100
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
遍历-全排列 |
O(n^3/2) |
O(1) |
func numPrimeArrangements(n int) int {
primeNum := 0
for i := 2; i <= n; i++ {
if isPrime(i) {
primeNum++
}
}
a := 1
for i := 2; i <= primeNum; i++ {
a = a * i % 1000000007
}
for i := 2; i <= n-primeNum; i++ {
a = a * i % 1000000007
}
return a
}
func isPrime(n int) bool {
if n == 2 || n == 3 {
return true
}
for i := 2; i*i <= n; i++ {
if n%i == 0 {
return false
}
}
return true
}
1184.公交站间的距离(2)
环形公交路线上有 n 个站,按次序从 0 到 n - 1 进行编号。
我们已知每一对相邻公交站之间的距离,
distance[i] 表示编号为 i 的车站和编号为 (i + 1) % n 的车站之间的距离。
环线上的公交车都可以按顺时针和逆时针的方向行驶。
返回乘客从出发点 start 到目的地 destination 之间的最短距离。
示例 1:输入:distance = [1,2,3,4], start = 0, destination = 1 输出:1
解释:公交站 0 和 1 之间的距离是 1 或 9,最小值是 1。
示例 2:输入:distance = [1,2,3,4], start = 0, destination = 2 输出:3
解释:公交站 0 和 2 之间的距离是 3 或 7,最小值是 3。
示例 3:输入:distance = [1,2,3,4], start = 0, destination = 3 输出:4
解释:公交站 0 和 3 之间的距离是 6 或 4,最小值是 4。
提示:
1 <= n <= 10^4
distance.length == n
0 <= start, destination < n
0 <= distance[i] <= 10^4
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
遍历 |
O(n) |
O(1) |
02 |
遍历 |
O(n) |
O(1) |
func distanceBetweenBusStops(distance []int, start int, destination int) int {
x := 0
y := 0
for i := start; i != destination; i = (i + 1) % len(distance) {
x = x + distance[i]
}
for i := destination; i != start; i = (i + 1) % len(distance) {
y = y + distance[i]
}
if x > y {
return y
}
return x
}
#
func distanceBetweenBusStops(distance []int, start int, destination int) int {
x := 0
sum := 0
for i := 0; i < len(distance); i++ {
sum = sum + distance[i]
if start < destination {
if i >= start && i < destination {
x = x + distance[i]
}
} else {
if i >= destination && i < start {
x = x + distance[i]
}
}
}
if sum-x > x {
return x
}
return sum - x
}
1185.一周中的第几天(3)
给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。
输入为三个整数:day、month 和 year,分别表示日、月、年。
您返回的结果必须是这几个值中的一个
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}。
示例 1:输入:day = 31, month = 8, year = 2019 输出:"Saturday"
示例 2:输入:day = 18, month = 7, year = 1999 输出:"Sunday"
示例 3:输入:day = 15, month = 8, year = 1993 输出:"Sunday"
提示:
给出的日期一定是在 1971 到 2100 年之间的有效日期。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
内置函数 |
O(1) |
O(1) |
02 |
公式 |
O(1) |
O(1) |
03 |
遍历 |
O(1) |
O(1) |
func dayOfTheWeek(day int, month int, year int) string {
t, _ := time.Parse("2006-01-02", fmt.Sprintf("%04d-%02d-%02d", year, month, day))
return t.Weekday().String()
}
#
func dayOfTheWeek(day int, month int, year int) string {
arr := []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
if month == 1 || month == 2 {
month = month + 12
year--
}
week := (year + year/4 - year/100 + year/400 + 2*month + 3*(month+1)/5 + day) % 7
return arr[week]
}
#
var arr = []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
var monthDate = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
func dayOfTheWeek(day int, month int, year int) string {
day1 := totalDay(1993, 8, 15)
day2 := totalDay(year, month, day)
diff := 6 - day1%7
return arr[(day2+diff)%7]
}
func totalDay(year, month, day int) int {
total := 0
for i := 1971; i < year; i++ {
total = total + 365
if isLeap(i) {
total = total + 1
}
}
for i := 0; i < month-1; i++ {
total = total + monthDate[i]
if i == 1 && isLeap(year) {
total = total + 1
}
}
total = total + day
return total
}
func isLeap(year int) bool {
return year%400 == 0 || (year%4 == 0 && year%100 != 0)
}
1189.“气球”的最大数量(3)
给你一个字符串 text,你需要使用 text 中的字母来拼凑尽可能多的单词 "balloon"(气球)。
字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 "balloon"。
示例 1:输入:text = "nlaebolko" 输出:1
示例 2:输入:text = "loonbalxballpoon" 输出:2
示例 3:输入:text = "leetcode" 输出:0
提示:
1 <= text.length <= 10^4
text 全部由小写英文字母组成
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
遍历-数组辅助 |
O(n) |
O(1) |
02 |
遍历-数组辅助 |
O(n) |
O(1) |
03 |
内置函数 |
O(n) |
O(1) |
func maxNumberOfBalloons(text string) int {
m := make([]int, 26)
str := "ablon"
for i := 0; i < len(str); i++ {
m[str[i]-'a']++
}
for i := 0; i < len(text); i++ {
if m[text[i]-'a'] > 0 {
m[text[i]-'a']++
}
}
min := math.MaxInt32
for k, v := range m {
if v == 0 {
continue
}
if k+'a' == 'l' || k+'a' == 'o' {
v = (v - 1) / 2
} else {
v = v - 1
}
if v < min {
min = v
}
}
return min
}
#
func maxNumberOfBalloons(text string) int {
m := make([]int, 26)
for i := 0; i < len(text); i++ {
m[text[i]-'a']++
}
res := 0
str := "balloon"
for {
for i := 0; i < len(str); i++ {
m[str[i]-'a']--
if m[str[i]-'a'] < 0 {
return res
}
}
res++
}
}
#
func maxNumberOfBalloons(text string) int {
arr := make([]int, 0)
str := "ablon"
for i := 0; i < len(str); i++ {
if str[i] == 'l' || str[i] == 'o' {
arr = append(arr, strings.Count(text, string(str[i]))/2)
} else {
arr = append(arr, strings.Count(text, string(str[i])))
}
}
min := arr[0]
for i := 1; i < len(arr); i++ {
if arr[i] < min {
min = arr[i]
}
}
return min
}
1200.最小绝对差(2)
给你个整数数组 arr,其中每个元素都 不相同。
请你找到所有具有最小绝对差的元素对,并且按升序的顺序返回。
示例 1:输入:arr = [4,2,1,3] 输出:[[1,2],[2,3],[3,4]]
示例 2:输入:arr = [1,3,6,10,15] 输出:[[1,3]]
示例 3:输入:arr = [3,8,-10,23,19,-4,-14,27] 输出:[[-14,-10],[19,23],[23,27]]
提示:
2 <= arr.length <= 10^5
-10^6 <= arr[i] <= 10^6
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
排序-遍历 |
O(nlog(n)) |
O(n) |
02 |
排序-遍历 |
O(nlog(n)) |
O(n) |
func minimumAbsDifference(arr []int) [][]int {
sort.Ints(arr)
result := make([][]int, 0)
min := arr[1] - arr[0]
result = append(result, []int{arr[0], arr[1]})
for i := 2; i < len(arr); i++ {
value := arr[i] - arr[i-1]
if value < min {
min = value
result = make([][]int, 0)
result = append(result, []int{arr[i-1], arr[i]})
} else if value == min {
result = append(result, []int{arr[i-1], arr[i]})
}
}
return result
}
#
func minimumAbsDifference(arr []int) [][]int {
sort.Ints(arr)
result := make([][]int, 0)
min := arr[1] - arr[0]
for i := 2; i < len(arr); i++ {
if min > arr[i]-arr[i-1] {
min = arr[i] - arr[i-1]
}
}
for i := 1; i < len(arr); i++ {
if min == arr[i]-arr[i-1] {
result = append(result, []int{arr[i-1], arr[i]})
}
}
return result
}
1101-1200-Medium
1104.二叉树寻路(3)
在一棵无限的二叉树上,每个节点都有两个子节点,树中的节点 逐行 依次按“之” 字形进行标记。
如下图所示,在奇数行(即,第一行、第三行、第五行……)中,按从左到右的顺序进行标记;
而偶数行(即,第二行、第四行、第六行……)中,按从右到左的顺序进行标记。
给你树上某一个节点的标号 label,请你返回从根节点到该标号为 label 节点的路径,
该路径是由途经的节点标号所组成的。
示例 1:输入:label = 14 输出:[1,3,4,14]
示例 2:输入:label = 26 输出:[1,2,6,10,26]
提示:1 <= label <= 10^6
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
遍历 |
O(log(n)) |
O(log(n)) |
02 |
遍历 |
O(log(n)) |
O(log(n)) |
03 |
位运算 |
O(log(n)) |
O(log(n)) |
func pathInZigZagTree(label int) []int {
res := make([]int, 0)
for label > 0 {
res = append(res, label)
label = label / 2
}
for i := 0; i < len(res)/2; i++ {
res[i], res[len(res)-1-i] = res[len(res)-1-i], res[i]
}
i := 1
if len(res)%2 == 0 {
i = 2
}
for ; i < len(res); i = i + 2 {
res[i] = (1<<i)*3 - 1 - res[i]
}
return res
}
# 2
func pathInZigZagTree(label int) []int {
length := int(math.Log2(float64(label)))
res := make([]int, length+1)
res[length] = label
length--
i := 1
for length >= 0 {
target := int(math.Pow(2, float64(length+1))) + int(math.Pow(2, float64(length))) - 1
if i%2 == 1 {
res[length] = target - label/2
} else {
res[length] = label / 2
}
i++
length--
label = label / 2
}
return res
}
# 3
func pathInZigZagTree(label int) []int {
res := make([]int, 0)
for label > 1 {
res = append([]int{label}, res...)
label = label / 2
length := bits.Len32(uint32(label)) - 1
label = label ^ ((1 << length) - 1)
}
res = append([]int{1}, res...)
return res
}
1105.填充书架(1)
附近的家居城促销,你买回了一直心仪的可调节书架,打算把自己的书都整理到新的书架上。
你把要摆放的书 books都整理好,叠成一摞:
从上往下,第 i本书的厚度为 books[i][0],高度为 books[i][1]。
按顺序将这些书摆放到总宽度为shelf_width 的书架上。
先选几本书放在书架上(它们的厚度之和小于等于书架的宽度 shelf_width),然后再建一层书架。
重复这个过程,直到把所有的书都放在书架上。
需要注意的是,在上述过程的每个步骤中,摆放书的顺序与你整理好的顺序相同。
例如,如果这里有 5 本书,那么可能的一种摆放情况是:
第一和第二本书放在第一层书架上,第三本书放在第二层书架上,第四和第五本书放在最后一层书架上。
每一层所摆放的书的最大高度就是这一层书架的层高,书架整体的高度为各层高之和。
以这种方式布置书架,返回书架整体可能的最小高度。
示例:输入:books = [[1,1],[2,3],[2,3],[1,1],[1,1],[1,1],[1,2]], shelf_width = 4 输出:6
解释:3 层书架的高度和为 1 + 3 + 2 = 6 。
第 2 本书不必放在第一层书架上。
提示:1 <= books.length <= 1000
1 <= books[i][0] <= shelf_width <= 1000
1 <= books[i][1] <= 1000
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
动态规划 |
O(n^2) |
O(n) |
func minHeightShelves(books [][]int, shelf_width int) int {
n := len(books)
dp := make([]int, n+1)
for i := 1; i <= n; i++ {
w, h := books[i-1][0], books[i-1][1]
dp[i] = dp[i-1] + h
for j := i - 1; j > 0; j-- {
if w+books[j-1][0] <= shelf_width {
w = w + books[j-1][0]
h = max(h, books[j-1][1])
dp[i] = min(dp[i], dp[j-1]+h)
} else {
break
}
}
}
return dp[n]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
1109.航班预订统计(2)
这里有 n 个航班,它们分别从 1 到 n 进行编号。
我们这儿有一份航班预订表,
表中第 i 条预订记录 bookings[i] = [i, j, k] 意味着我们在从 i 到 j 的每个航班上预订了 k 个座位。
请你返回一个长度为 n 的数组 answer,按航班编号顺序返回每个航班上预订的座位数。
示例:输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5 输出:[10,55,45,25,25]
提示:1 <= bookings.length <= 20000
1 <= bookings[i][0] <= bookings[i][1] <= n <= 20000
1 <= bookings[i][2] <= 10000
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
差分数组 |
O(n) |
O(n) |
02 |
差分数组 |
O(n) |
O(n) |
func corpFlightBookings(bookings [][]int, n int) []int {
arr := make([]int, n+1)
for i := 0; i < len(bookings); i++ {
start := bookings[i][0] - 1
end := bookings[i][1] - 1
count := bookings[i][2]
arr[start] = arr[start] + count
arr[end+1] = arr[end+1] - count
}
res := make([]int, 0)
total := 0
for i := 0; i < n; i++ {
total = total + arr[i]
res = append(res, total)
}
return res
}
# 2
func corpFlightBookings(bookings [][]int, n int) []int {
arr := make([]int, n)
for i := 0; i < len(bookings); i++ {
start := bookings[i][0]
end := bookings[i][1]
count := bookings[i][2]
arr[start-1] = arr[start-1] + count
if end < n {
arr[end] = arr[end] - count
}
}
for i := 1; i < n; i++ {
arr[i] = arr[i] + arr[i-1]
}
return arr
}
1110.删点成林(1)
给出二叉树的根节点root,树上每个节点都有一个不同的值。
如果节点值在to_delete中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。
返回森林中的每棵树。你可以按任意顺序组织答案。
示例:输入:root = [1,2,3,4,5,6,7], to_delete = [3,5] 输出:[[1,2,null,4],[6],[7]]
提示:树中的节点数最大为1000。
每个节点都有一个介于1 到1000之间的值,且各不相同。
to_delete.length <= 1000
to_delete 包含一些从1 到1000、各不相同的值。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
递归 |
O(n) |
O(n) |
var res []*TreeNode
var m map[int]bool
func delNodes(root *TreeNode, to_delete []int) []*TreeNode {
res = make([]*TreeNode, 0)
m = make(map[int]bool)
for i := 0; i < len(to_delete); i++ {
m[to_delete[i]] = true
}
root = dfs(root)
if root != nil {
res = append(res, root)
}
return res
}
func dfs(root *TreeNode) *TreeNode {
if root == nil {
return nil
}
root.Left = dfs(root.Left)
root.Right = dfs(root.Right)
if m[root.Val] == true {
if root.Left != nil {
res = append(res, root.Left)
}
if root.Right != nil {
res = append(res, root.Right)
}
return nil
}
return root
}
1111.有效括号的嵌套深度(3)
有效括号字符串 定义:对于每个左括号,都能找到与之对应的右括号,反之亦然。
详情参见题末「有效括号字符串」部分。
嵌套深度 depth 定义:即有效括号字符串嵌套的层数,depth(A) 表示有效括号字符串 A 的嵌套深度。
详情参见题末「嵌套深度」部分。
有效括号字符串类型与对应的嵌套深度计算方法如下图所示:
给你一个「有效括号字符串」 seq,请你将其分成两个不相交的有效括号字符串,A 和 B,
并使这两个字符串的深度最小。
不相交:每个 seq[i] 只能分给 A 和 B 二者中的一个,不能既属于 A 也属于 B 。
A 或 B 中的元素在原字符串中可以不连续。
A.length + B.length = seq.length
深度最小:max(depth(A), depth(B)) 的可能取值最小。
划分方案用一个长度为 seq.length 的答案数组 answer 表示,编码规则如下:
answer[i] = 0,seq[i] 分给 A 。
answer[i] = 1,seq[i] 分给 B 。
如果存在多个满足要求的答案,只需返回其中任意 一个 即可。
示例 1:输入:seq = "(()())" 输出:[0,1,1,1,1,0]
示例 2:输入:seq = "()(())()" 输出:[0,0,0,1,1,0,1,1]
解释:本示例答案不唯一。
按此输出 A = "()()", B = "()()", max(depth(A), depth(B)) = 1,它们的深度最小。
像 [1,1,1,0,0,1,1,1],也是正确结果,其中 A = "()()()", B = "()", max(depth(A), depth(B)) = 1 。
提示:1 < seq.size <= 10000
有效括号字符串:仅由 "(" 和 ")" 构成的字符串,对于每个左括号,都能找到与之对应的右括号,反之亦然。
下述几种情况同样属于有效括号字符串:
1. 空字符串
2. 连接,可以记作 AB(A 与 B 连接),其中 A 和 B 都是有效括号字符串
3. 嵌套,可以记作 (A),其中 A 是有效括号字符串
嵌套深度:类似地,我们可以定义任意有效括号字符串 s 的 嵌套深度 depth(S):
1. s 为空时,depth("") = 0
2. s 为 A 与 B 连接时,depth(A + B) = max(depth(A), depth(B)),其中 A 和 B 都是有效括号字符串
3. s 为嵌套情况,depth("(" + A + ")") = 1 + depth(A),其中 A 是有效括号字符串
例如:"","()()",和 "()(()())" 都是有效括号字符串,
嵌套深度分别为 0,1,2,而 ")(" 和 "(()" 都不是有效括号字符串。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
统计 |
O(n) |
O(n) |
02 |
找规律 |
O(n) |
O(n) |
03 |
找规律 |
O(n) |
O(n) |
func maxDepthAfterSplit(seq string) []int {
res := make([]int, 0)
level := 0
for i := 0; i < len(seq); i++ {
if seq[i] == '(' {
level++
res = append(res, level%2)
} else {
res = append(res, level%2)
level--
}
}
return res
}
# 2
func maxDepthAfterSplit(seq string) []int {
res := make([]int, 0)
for i := 0; i < len(seq); i++ {
if seq[i] == '(' {
res = append(res, i%2)
} else {
res = append(res, 1-i%2)
}
}
return res
}
# 3
func maxDepthAfterSplit(seq string) []int {
res := make([]int, 0)
a, b := 0, 0
for i := 0; i < len(seq); i++ {
if seq[i] == '(' {
if a <= b {
a++
res = append(res, 0)
} else {
b++
res = append(res, 1)
}
} else {
if a > b {
a--
res = append(res, 0)
} else {
b--
res = append(res, 1)
}
}
}
return res
}
1123.最深叶节点的最近公共祖先(2)
给你一个有根节点的二叉树,找到它最深的叶节点的最近公共祖先。
回想一下:
叶节点 是二叉树中没有子节点的节点
树的根节点的深度为0,如果某一节点的深度为d,那它的子节点的深度就是d+1
如果我们假定 A 是一组节点S的 最近公共祖先,S中的每个节点都在以 A 为根节点的子树中,
且 A的深度达到此条件下可能的最大值。
注意:本题与力扣 865 重复:
示例 1:输入:root = [3,5,1,6,2,0,8,null,null,7,4] 输出:[2,7,4]
解释:我们返回值为 2 的节点,在图中用黄色标记。
在图中用蓝色标记的是树的最深的节点。
注意,节点 6、0 和 8 也是叶节点,但是它们的深度是 2 ,而节点 7 和 4 的深度是 3 。
示例 2:输入:root = [1] 输出:[1]
解释:根节点是树中最深的节点,它是它本身的最近公共祖先。
示例 3:输入:root = [0,1,3,null,2] 输出:[2]
解释:树中最深的叶节点是 2 ,最近公共祖先是它自己。
提示:给你的树中将有1 到 1000 个节点。
树中每个节点的值都在 1 到 1000 之间。
每个节点的值都是独一无二的。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
递归 |
O(n) |
O(log(n)) |
02 |
递归 |
O(n^2) |
O(log(n)) |
func lcaDeepestLeaves(root *TreeNode) *TreeNode {
res, _ := dfs(root, 0)
return res
}
func dfs(root *TreeNode, level int) (*TreeNode, int) {
if root == nil {
return root, level
}
leftNode, left := dfs(root.Left, level+1)
rightNode, right := dfs(root.Right, level+1)
if left == right {
return root, left + 1
} else if left > right {
return leftNode, left + 1
}
return rightNode, right + 1
}
# 2
func lcaDeepestLeaves(root *TreeNode) *TreeNode {
if root == nil{
return nil
}
left := dfs(root.Left)
right := dfs(root.Right)
if left == right{
return root
}else if left > right{
return lcaDeepestLeaves(root.Left)
}
return lcaDeepestLeaves(root.Right)
}
func dfs(root *TreeNode) (int) {
if root == nil {
return 0
}
left := dfs(root.Left)
right := dfs(root.Right)
return 1+max(left,right)
}
func max(a, b int)int {
if a > b{
return a
}
return b
}
1124.表现良好的最长时间段(3)
给你一份工作时间表hours,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。
示例 1:输入:hours = [9,9,6,0,6,6,9] 输出:3
解释:最长的表现良好时间段是 [9,9,6]。
提示:1 <= hours.length <= 10000
0 <= hours[i] <= 16
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
单调栈+前缀和 |
O(n) |
O(n) |
02 |
前缀和+暴力法 |
O(n^2) |
O(n) |
03 |
哈希辅助 |
O(n) |
O(n) |
func longestWPI(hours []int) int {
arr := make([]int, 0)
for i := 0; i < len(hours); i++ {
if hours[i] > 8 {
arr = append(arr, 1)
} else {
arr = append(arr, -1)
}
}
temp := make([]int, len(hours)+1)
for i := 1; i <= len(hours); i++ {
temp[i] = temp[i-1] + arr[i-1]
}
stack := make([]int, 0)
for i := 0; i < len(temp); i++ {
if len(stack) == 0 || temp[stack[len(stack)-1]] > temp[i] {
stack = append(stack, i)
}
}
res := 0
for i := len(temp) - 1; i >= 0; i-- {
if len(stack) == 0 {
break
}
for len(stack) > 0 && temp[i] > temp[stack[len(stack)-1]] {
if i-stack[len(stack)-1] > res {
res = i - stack[len(stack)-1]
}
stack = stack[:len(stack)-1]
}
}
return res
}
# 2
func longestWPI(hours []int) int {
arr := make([]int, 0)
for i := 0; i < len(hours); i++ {
if hours[i] > 8 {
arr = append(arr, 1)
} else {
arr = append(arr, -1)
}
}
temp := make([]int, len(hours)+1)
for i := 1; i <= len(hours); i++ {
temp[i] = temp[i-1] + arr[i-1]
}
res := 0
for i := 0; i < len(hours); i++ {
for j := i; j < len(hours); j++ {
count := temp[j+1] - temp[i]
if count > 0 {
res = max(res, j-i+1)
}
}
}
return res
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
# 3
func longestWPI(hours []int) int {
m := make(map[int]int)
count := 0
res := 0
for i := 0; i < len(hours); i++ {
if hours[i] > 8 {
count++
} else {
count--
}
if count > 0 {
res = i + 1
} else {
if _, ok := m[count]; ok == false {
m[count] = i
}
if _, ok := m[count-1]; ok == true {
res = max(res, i-m[count-1])
}
}
}
return res
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
1129.颜色交替的最短路径(2)
在一个有向图中,节点分别标记为0, 1, ..., n-1。这个图中的每条边不是红色就是蓝色,且存在自环或平行边。
red_edges中的每一个[i, j]对表示从节点 i 到节点 j 的红色有向边。
类似地,blue_edges中的每一个[i, j]对表示从节点 i 到节点 j 的蓝色有向边。
返回长度为 n 的数组answer,其中answer[X]是从节点0到节点X的红色边和蓝色边交替出现的最短路径的长度。
如果不存在这样的路径,那么 answer[x] = -1。
示例 1:输入:n = 3, red_edges = [[0,1],[1,2]], blue_edges = [] 输出:[0,1,-1]
示例 2:输入:n = 3, red_edges = [[0,1]], blue_edges = [[2,1]] 输出:[0,1,-1]
示例 3:输入:n = 3, red_edges = [[1,0]], blue_edges = [[2,1]] 输出:[0,-1,-1]
示例 4:输入:n = 3, red_edges = [[0,1]], blue_edges = [[1,2]] 输出:[0,1,2]
示例 5:输入:n = 3, red_edges = [[0,1],[0,2]], blue_edges = [[1,0]] 输出:[0,1,1]
提示:1 <= n <= 100
red_edges.length <= 400
blue_edges.length <= 400
red_edges[i].length == blue_edges[i].length == 2
0 <= red_edges[i][j], blue_edges[i][j] < n
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
广度优先搜索 |
O(n^2) |
O(n^2) |
02 |
广度优先搜索 |
O(n^2) |
O(n^2) |
var redArr [][]int
var blueArr [][]int
var res []int
func shortestAlternatingPaths(n int, red_edges [][]int, blue_edges [][]int) []int {
redArr = make([][]int, n)
blueArr = make([][]int, n)
for i := 0; i < len(red_edges); i++ {
a, b := red_edges[i][0], red_edges[i][1]
redArr[a] = append(redArr[a], b)
}
for i := 0; i < len(blue_edges); i++ {
a, b := blue_edges[i][0], blue_edges[i][1]
blueArr[a] = append(blueArr[a], b)
}
res = make([]int, n)
for i := 0; i < n; i++ {
res[i] = -1
}
res[0] = 0
bfs(n, 0)
bfs(n, 1)
return res
}
func bfs(n int, color int) {
visited := make([][2]bool, n)
visited[0][color] = true
queue := make([]int, 0)
queue = append(queue, 0)
count := 0
for len(queue) > 0 {
length := len(queue)
for i := 0; i < length; i++ {
node := queue[i]
targetColor := count % 2
if targetColor == color {
for i := 0; i < len(redArr[node]); i++ {
next := redArr[node][i]
if visited[next][targetColor] == false {
visited[next][targetColor] = true
if res[next] == -1 || res[next] > count+1 {
res[next] = count + 1
}
queue = append(queue, next)
}
}
} else {
for i := 0; i < len(blueArr[node]); i++ {
next := blueArr[node][i]
if visited[next][targetColor] == false {
visited[next][targetColor] = true
if res[next] == -1 || res[next] > count+1 {
res[next] = count + 1
}
queue = append(queue, next)
}
}
}
}
queue = queue[length:]
count++
}
}
# 2
func shortestAlternatingPaths(n int, red_edges [][]int, blue_edges [][]int) []int {
redArr, blueArr := make([][]int, n), make([][]int, n)
for i := 0; i < len(red_edges); i++ {
a, b := red_edges[i][0], red_edges[i][1]
redArr[a] = append(redArr[a], b)
}
for i := 0; i < len(blue_edges); i++ {
a, b := blue_edges[i][0], blue_edges[i][1]
blueArr[a] = append(blueArr[a], b)
}
res := make([]int, n)
for i := 1; i < n; i++ {
res[i] = -1
}
queue, visited := make([][2]int, 0), make([][2]bool, n)
for i := 0; i < len(redArr[0]); i++ {
queue = append(queue, [2]int{redArr[0][i], 0})
}
for i := 0; i < len(blueArr[0]); i++ {
queue = append(queue, [2]int{blueArr[0][i], 1})
}
count := 1
for len(queue) > 0 {
length := len(queue)
for i := 0; i < length; i++ {
node, targetColor := queue[i][0], queue[i][1]
if res[node] == -1 {
res[node] = count
}
if targetColor == 0 && visited[node][targetColor] == false {
visited[node][targetColor] = true
for j := 0; j < len(blueArr[node]); j++ {
queue = append(queue, [2]int{blueArr[node][j], 1})
}
}
if targetColor == 1 && visited[node][targetColor] == false {
visited[node][targetColor] = true
for j := 0; j < len(redArr[node]); j++ {
queue = append(queue, [2]int{redArr[node][j], 0})
}
}
}
queue = queue[length:]
count++
}
return res
}
1130.叶值的最小代价生成树(3)
给你一个正整数数组arr,考虑所有满足以下条件的二叉树:
每个节点都有 0 个或是 2 个子节点。
数组arr中的值与树的中序遍历中每个叶节点的值一一对应。
(知识回顾:如果一个节点有 0 个子节点,那么该节点为叶节点。)
每个非叶节点的值等于其左子树和右子树中叶节点的最大值的乘积。
在所有这样的二叉树中,返回每个非叶节点的值的最小可能总和。这个和的值是一个32 位整数。
示例:输入:arr = [6,2,4] 输出:32
解释: 有两种可能的树,第一种的非叶节点的总和为 36,第二种非叶节点的总和为 32。
24 24
/ \ / \
12 4 6 8
/ \ / \
6 2 2 4
提示:2 <= arr.length <= 40
1 <= arr[i] <= 15
答案保证是一个 32 位带符号整数,即小于2^31。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
单调栈 |
O(n) |
O(n) |
02 |
动态规划 |
O(n^3) |
O(n^2) |
03 |
单调栈 |
O(n) |
O(n) |
func mctFromLeafValues(arr []int) int {
res := 0
stack := make([]int, 0)
stack = append(stack, math.MaxInt32)
for i := 0; i < len(arr); i++ {
for len(stack) > 0 && arr[i] >= stack[len(stack)-1] {
middle := stack[len(stack)-1]
stack = stack[:len(stack)-1]
left := stack[len(stack)-1]
right := arr[i]
res = res + middle*min(left, right)
}
stack = append(stack, arr[i])
}
for len(stack) > 2 {
a := stack[len(stack)-1]
stack = stack[:len(stack)-1]
b := stack[len(stack)-1]
res = res + a*b
}
return res
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
# 2
func mctFromLeafValues(arr []int) int {
n := len(arr)
maxArr := make([][]int, n)
for i := 0; i < n; i++ {
maxArr[i] = make([]int, n)
maxValue := arr[i]
for j := i; j < n; j++ {
maxValue = max(maxValue, arr[j])
maxArr[i][j] = maxValue
}
}
dp := make([][]int, n)
for i := 0; i < n; i++ {
dp[i] = make([]int, n)
}
for j := 0; j < n; j++ {
for i := j - 1; i >= 0; i-- {
dp[i][j] = math.MaxInt32
for k := i; k+1 <= j; k++ {
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]+maxArr[i][k]*maxArr[k+1][j])
}
}
}
return dp[0][n-1]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
# 3
func mctFromLeafValues(arr []int) int {
res := 0
stack := make([]int, 0)
for i := 0; i < len(arr); i++ {
for len(stack) > 0 && arr[i] >= stack[len(stack)-1] {
middle := stack[len(stack)-1]
stack = stack[:len(stack)-1]
right := arr[i]
var left int
if len(stack) == 0 {
left = math.MaxInt32
} else {
left = stack[len(stack)-1]
}
res = res + middle*min(left, right)
}
stack = append(stack, arr[i])
}
for len(stack) >= 2 {
a := stack[len(stack)-1]
stack = stack[:len(stack)-1]
b := stack[len(stack)-1]
res = res + a*b
}
return res
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
1131.绝对值表达式的最大值(2)
给你两个长度相等的整数数组,返回下面表达式的最大值:
|arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j|
其中下标 i,j 满足0 <= i, j < arr1.length。
示例 1:输入:arr1 = [1,2,3,4], arr2 = [-1,4,5,6] 输出:13
示例 2:输入:arr1 = [1,-2,-5,0,10], arr2 = [0,-2,-1,-7,-4] 输出:20
提示:2 <= arr1.length == arr2.length <= 40000
-10^6 <= arr1[i], arr2[i] <= 10^6
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
数学 |
O(n) |
O(n) |
02 |
数学 |
O(n) |
O(1) |
func maxAbsValExpr(arr1 []int, arr2 []int) int {
arr := make([][]int, 4)
for i := 0; i < len(arr1); i++ {
a, b := arr1[i], arr2[i]
arr[0] = append(arr[0], a+b+i)
arr[1] = append(arr[1], a+b-i)
arr[2] = append(arr[2], a-b+i)
arr[3] = append(arr[3], a-b-i)
}
a, b, c, d := getValue(arr[0]), getValue(arr[1]), getValue(arr[2]), getValue(arr[3])
return max(a, max(b, max(c, d)))
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func getValue(arr []int) int {
minValue, maxValue := arr[0], arr[0]
for i := 0; i < len(arr); i++ {
if arr[i] > maxValue {
maxValue = arr[i]
}
if arr[i] < minValue {
minValue = arr[i]
}
}
return maxValue - minValue
}
# 2
func maxAbsValExpr(arr1 []int, arr2 []int) int {
aMaxValue, bMaxValue, cMaxValue, dMaxValue := math.MinInt32, math.MinInt32, math.MinInt32, math.MinInt32
aMinValue, bMinValue, cMinValue, dMinValue := math.MaxInt32, math.MaxInt32, math.MaxInt32, math.MaxInt32
for i := 0; i < len(arr1); i++ {
aMaxValue = max(aMaxValue, arr1[i]+arr2[i]+i)
aMinValue = min(aMinValue, arr1[i]+arr2[i]+i)
bMaxValue = max(bMaxValue, arr1[i]+arr2[i]-i)
bMinValue = min(bMinValue, arr1[i]+arr2[i]-i)
cMaxValue = max(cMaxValue, arr1[i]-arr2[i]+i)
cMinValue = min(cMinValue, arr1[i]-arr2[i]+i)
dMaxValue = max(dMaxValue, arr1[i]-arr2[i]-i)
dMinValue = min(dMinValue, arr1[i]-arr2[i]-i)
}
a, b := aMaxValue-aMinValue, bMaxValue-bMinValue
c, d := cMaxValue-cMinValue, dMaxValue-dMinValue
return max(a, max(b, max(c, d)))
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
1138.字母板上的路径(1)
我们从一块字母板上的位置(0, 0)出发,该坐标对应的字符为board[0][0]。
在本题里,字母板为board = ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "z"],如下所示。
我们可以按下面的指令规则行动:
如果方格存在,'U'意味着将我们的位置上移一行;
如果方格存在,'D'意味着将我们的位置下移一行;
如果方格存在,'L'意味着将我们的位置左移一列;
如果方格存在,'R'意味着将我们的位置右移一列;
'!'会把在我们当前位置 (r, c) 的字符board[r][c]添加到答案中。
(注意,字母板上只存在有字母的位置。)
返回指令序列,用最小的行动次数让答案和目标target相同。你可以返回任何达成目标的路径。
示例 1:输入:target = "leet" 输出:"DDR!UURRR!!DDD!"
示例 2:输入:target = "code" 输出:"RR!DDRR!UUL!R!"
提示:1 <= target.length <= 100
target仅含有小写英文字母。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
模拟 |
O(n) |
O(n) |
func alphabetBoardPath(target string) string {
res := ""
x, y := 0, 0
for i := 0; i < len(target); i++ {
newX := int(target[i]-'a') / 5
newY := int(target[i]-'a') % 5
if x > newX {
res = res + strings.Repeat("U", x-newX)
}
if y > newY {
res = res + strings.Repeat("L", y-newY)
}
if y < newY {
res = res + strings.Repeat("R", newY-y)
}
if x < newX {
res = res + strings.Repeat("D", newX-x)
}
res = res + "!"
x, y = newX, newY
}
return res
}
1139.最大的以1为边界的正方形(1)
给你一个由若干 0 和 1 组成的二维网格grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。
如果不存在,则返回 0。
示例 1:输入:grid = [[1,1,1],[1,0,1],[1,1,1]] 输出:9
示例 2:输入:grid = [[1,1,0,0]] 输出:1
提示:1 <= grid.length <= 100
1 <= grid[0].length <= 100
grid[i][j] 为0或1
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
前缀和 |
O(n^3) |
O(n^2) |
func largest1BorderedSquare(grid [][]int) int {
res := 0
arr := [100][100][2]int{}
n, m := len(grid), len(grid[0])
for i := 0; i < n; i++ {
for j := 0; j < m; j++ {
if grid[i][j] == 0 {
continue
}
if i == 0 {
arr[i][j][0] = 1
} else {
arr[i][j][0] = arr[i-1][j][0] + 1
}
if j == 0 {
arr[i][j][1] = 1
} else {
arr[i][j][1] = arr[i][j-1][1] + 1
}
minValue := min(arr[i][j][0], arr[i][j][1])
for k := minValue; k > res; k-- {
if arr[i][j-k+1][0] >= k && arr[i-k+1][j][1] >= k {
res = k
break
}
}
}
}
return res * res
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
1140.石子游戏II(2)
亚历克斯和李继续他们的石子游戏。许多堆石子排成一行,每堆都有正整数颗石子piles[i]。
游戏以谁手中的石子最多来决出胜负。
亚历克斯和李轮流进行,亚历克斯先开始。最初,M = 1。
在每个玩家的回合中,该玩家可以拿走剩下的前X堆的所有石子,其中1 <= X <= 2M。
然后,令M = max(M, X)。
游戏一直持续到所有石子都被拿走。
假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。
示例:输入:piles = [2,7,9,4,4] 输出:10
解释:如果亚历克斯在开始时拿走一堆石子,李拿走两堆,接着亚历克斯也拿走两堆。
在这种情况下,亚历克斯可以拿到 2 + 4 + 4 = 10 颗石子。
如果亚历克斯在开始时拿走两堆石子,那么李就可以拿走剩下全部三堆石子。
在这种情况下,亚历克斯可以拿到 2 + 7 = 9 颗石子。
所以我们返回更大的 10。
提示:1 <= piles.length <= 100
1 <= piles[i]<= 10 ^ 4
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
动态规划 |
O(n^3) |
O(n^2) |
02 |
递归 |
O(n^2) |
O(n^2) |
func stoneGameII(piles []int) int {
n := len(piles)
dp := make([][]int, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([]int, n+1)
}
sum := 0
for i := n - 1; i >= 0; i-- {
sum = sum + piles[i]
for M := 1; M <= n; M++ {
if i+2*M >= n {
dp[i][M] = sum
} else {
for j := 1; j <= 2*M; j++ {
dp[i][M] = max(dp[i][M], sum-dp[i+j][max(j, M)])
}
}
}
}
return dp[0][1]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
# 2
var dp [][]int
func stoneGameII(piles []int) int {
n := len(piles)
dp = make([][]int, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([]int, n+1)
}
for i := n - 2; i >= 0; i-- {
piles[i] = piles[i] + piles[i+1]
}
return dfs(piles, 0, 1)
}
func dfs(piles []int, index, M int) int {
if index >= len(piles) {
return 0
}
if index+2*M >= len(piles) {
return piles[index]
}
if dp[index][M] > 0 {
return dp[index][M]
}
res := 0
for i := 1; i <= 2*M; i++ {
res = max(res, piles[index]-dfs(piles, index+i, max(M, i)))
}
dp[index][M] = res
return dp[index][M]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
1143.最长公共子序列(3)
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
一个字符串的 子序列 是指这样一个新的字符串:
它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
示例 1:输入:text1 = "abcde", text2 = "ace" 输出:3
解释:最长公共子序列是 "ace",它的长度为 3。
示例 2:输入:text1 = "abc", text2 = "abc" 输出:3
解释:最长公共子序列是 "abc",它的长度为 3。
示例 3:输入:text1 = "abc", text2 = "def" 输出:0
解释:两个字符串没有公共子序列,返回 0。
提示:
1 <= text1.length <= 1000
1 <= text2.length <= 1000
输入的字符串只含有小写英文字符。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
动态规划-二维 |
O(n^2) |
O(n^2) |
02 |
动态规划-一维 |
O(n^2) |
O(n) |
03 |
动态规划-一维 |
O(n^2) |
O(n) |
func longestCommonSubsequence(text1 string, text2 string) int {
n, m := len(text1), len(text2)
dp := make([][]int, n+1)
for i := 0; i < n+1; i++ {
dp[i] = make([]int, m+1)
}
for i := 1; i <= n; i++ {
for j := 1; j <= m; j++ {
if text1[i-1] == text2[j-1] {
dp[i][j] = dp[i-1][j-1] + 1
} else {
dp[i][j] = max(dp[i][j-1], dp[i-1][j])
}
}
}
return dp[n][m]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
# 2
func longestCommonSubsequence(text1 string, text2 string) int {
n, m := len(text1), len(text2)
prev := make([]int, m+1)
cur := make([]int, m+1)
for i := 1; i <= n; i++ {
for j := 1; j <= m; j++ {
if text1[i-1] == text2[j-1] {
cur[j] = prev[j-1] + 1
} else {
cur[j] = max(prev[j], cur[j-1])
}
}
copy(prev, cur)
}
return cur[m]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
# 3
func longestCommonSubsequence(text1 string, text2 string) int {
n, m := len(text1), len(text2)
cur := make([]int, m+1)
for i := 1; i <= n; i++ {
pre := cur[0]
for j := 1; j <= m; j++ {
temp := cur[j]
if text1[i-1] == text2[j-1] {
cur[j] = pre + 1
} else {
cur[j] = max(cur[j], cur[j-1])
}
pre = temp
}
}
return cur[m]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
1144.递减元素使数组呈锯齿状(2)
给你一个整数数组nums,每次 操作会从中选择一个元素并 将该元素的值减少1。
如果符合下列情况之一,则数组A就是 锯齿数组:
每个偶数索引对应的元素都大于相邻的元素,即A[0] > A[1] < A[2] > A[3] < A[4] > ...
或者,每个奇数索引对应的元素都大于相邻的元素,即A[0] < A[1] > A[2] < A[3] > A[4] < ...
返回将数组nums转换为锯齿数组所需的最小操作次数。
示例 1:输入:nums = [1,2,3] 输出:2
解释:我们可以把 2 递减到 0,或把 3 递减到 1。
示例 2:输入:nums = [9,6,1,6,2] 输出:4
提示:1 <= nums.length <= 1000
1 <= nums[i] <= 1000
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
遍历 |
O(n) |
O(1) |
02 |
遍历 |
O(n) |
O(1) |
func movesToMakeZigzag(nums []int) int {
n := len(nums)
a, b := 0, 0
for i := 0; i < n; i++ {
if i%2 == 0 {
left, right := 0, 0
if i > 0 && nums[i] >= nums[i-1] {
left = nums[i] - nums[i-1] + 1
}
if i < n-1 && nums[i] >= nums[i+1] {
right = nums[i] - nums[i+1] + 1
}
a = a + max(left, right)
} else {
left, right := 0, 0
if nums[i] >= nums[i-1] {
left = nums[i] - nums[i-1] + 1
}
if i < n-1 && nums[i] >= nums[i+1] {
right = nums[i] - nums[i+1] + 1
}
b = b + max(left, right)
}
}
return min(a, b)
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
# 2
func movesToMakeZigzag(nums []int) int {
n := len(nums)
a, b := 0, 0
for i := 0; i < n; i = i + 2 {
left, right := 0, 0
if i > 0 && nums[i] >= nums[i-1] {
left = nums[i] - nums[i-1] + 1
}
if i < n-1 && nums[i] >= nums[i+1] {
right = nums[i] - nums[i+1] + 1
}
a = a + max(left, right)
}
for i := 1; i < n; i = i + 2 {
left, right := 0, 0
if nums[i] >= nums[i-1] {
left = nums[i] - nums[i-1] + 1
}
if i < n-1 && nums[i] >= nums[i+1] {
right = nums[i] - nums[i+1] + 1
}
b = b + max(left, right)
}
return min(a, b)
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
1145.二叉树着色游戏(2)
有两位极客玩家参与了一场「二叉树着色」的游戏。游戏中,
给出二叉树的根节点root,树上总共有 n 个节点,且 n 为奇数,其中每个节点上的值从1 到n各不相同。
游戏从「一号」玩家开始(「一号」玩家为红色,「二号」玩家为蓝色),最开始时,
「一号」玩家从 [1, n]中取一个值x(1 <= x <= n);
「二号」玩家也从[1, n]中取一个值y(1 <= y <= n)且y != x。
「一号」玩家给值为x的节点染上红色,而「二号」玩家给值为y的节点染上蓝色。
之后两位玩家轮流进行操作,每一回合,玩家选择一个他之前涂好颜色的节点,
将所选节点一个 未着色 的邻节点(即左右子节点、或父节点)进行染色。
如果当前玩家无法找到这样的节点来染色时,他的回合就会被跳过。
若两个玩家都没有可以染色的节点时,游戏结束。着色节点最多的那位玩家获得胜利 ✌️。
现在,假设你是「二号」玩家,根据所给出的输入,假如存在一个y值可以确保你赢得这场游戏,则返回true;
若无法获胜,就请返回 false。
示例:输入:root = [1,2,3,4,5,6,7,8,9,10,11], n = 11, x = 3 输出:True
解释:第二个玩家可以选择值为 2 的节点。
提示:二叉树的根节点为root,树上由 n 个节点,节点上的值从 1 到 n 各不相同。
n 为奇数。
1 <= x <= n<= 100
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
递归 |
O(n) |
O(log(n)) |
02 |
递归 |
O(n) |
O(log(n)) |
var targetNode *TreeNode
func btreeGameWinningMove(root *TreeNode, n int, x int) bool {
dfsTarget(root, x)
return dfs(root) > n/2 || dfs(targetNode.Left) > n/2 || dfs(targetNode.Right) > n/2
}
func dfsTarget(root *TreeNode, target int) {
if root != nil {
if root.Val == target {
targetNode = root
return
}
dfsTarget(root.Left, target)
dfsTarget(root.Right, target)
}
}
func dfs(root *TreeNode) int {
if root == nil || root == targetNode {
return 0
}
return 1 + dfs(root.Left) + dfs(root.Right)
}
# 2
var leftSum, rightSum int
func btreeGameWinningMove(root *TreeNode, n int, x int) bool {
leftSum = 0
rightSum = 0
total := dfs(root, x)
return leftSum > n/2 || rightSum > n/2 || (total-1-leftSum-rightSum) > n/2
}
func dfs(root *TreeNode, x int) int {
if root == nil {
return 0
}
left := dfs(root.Left, x)
right := dfs(root.Right, x)
if root.Val == x {
leftSum = left
rightSum = right
}
return 1 + left + right
}
1146.快照数组(3)
实现支持下列接口的「快照数组」-SnapshotArray:
SnapshotArray(int length)- 初始化一个与指定长度相等的 类数组 的数据结构。初始时,每个元素都等于0。
void set(index, val)- 会将指定索引index处的元素设置为val。
int snap()- 获取该数组的快照,并返回快照的编号snap_id(快照号是调用snap()的总次数减去1)。
int get(index, snap_id)- 根据指定的snap_id选择快照,并返回该快照指定索引 index的值。
示例:输入:["SnapshotArray","set","snap","set","get"]
[[3],[0,5],[],[0,6],[0,0]]
输出:[null,null,0,null,5]
解释:SnapshotArray snapshotArr = new SnapshotArray(3); // 初始化一个长度为 3 的快照数组
snapshotArr.set(0,5); // 令 array[0] = 5
snapshotArr.snap(); // 获取快照,返回 snap_id = 0
snapshotArr.set(0,6);
snapshotArr.get(0,0); // 获取 snap_id = 0 的快照中 array[0] 的值,返回 5
提示:1 <= length<= 50000
题目最多进行50000 次set,snap,和get的调用 。
0 <= index<length
0 <=snap_id <我们调用snap()的总次数
0 <=val <= 10^9
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
数组辅助 |
O(n) |
O(n^2) |
02 |
哈希辅助 |
O(n) |
O(n^2) |
03 |
数组辅助+二分查找 |
O(log(n)) |
O(n^2) |
type SnapshotArray struct {
id int
arr [][]Snap
}
type Snap struct {
id int
value int
}
func Constructor(length int) SnapshotArray {
return SnapshotArray{
id: 0,
arr: make([][]Snap, length),
}
}
func (this *SnapshotArray) Set(index int, val int) {
this.arr[index] = append(this.arr[index], Snap{
id: this.id,
value: val,
})
}
func (this *SnapshotArray) Snap() int {
id := this.id
this.id++
return id
}
func (this *SnapshotArray) Get(index int, snap_id int) int {
n := len(this.arr[index])
i := 0
for i < n && this.arr[index][i].id <= snap_id {
i++
}
if i == 0 {
return 0
}
i--
return this.arr[index][i].value
}
# 2
type SnapshotArray struct {
id int
arr []map[int]int
}
func Constructor(length int) SnapshotArray {
return SnapshotArray{
id: 0,
arr: make([]map[int]int, length),
}
}
func (this *SnapshotArray) Set(index int, val int) {
if this.arr[index] == nil {
this.arr[index] = make(map[int]int)
}
this.arr[index][this.id] = val
}
func (this *SnapshotArray) Snap() int {
id := this.id
this.id++
return id
}
func (this *SnapshotArray) Get(index int, snap_id int) int {
if this.arr[index] == nil {
return 0
}
for ; snap_id >= 0; snap_id-- {
if v, ok := this.arr[index][snap_id]; ok {
return v
}
}
return 0
}
# 3
type SnapshotArray struct {
id int
arr [][]Snap
}
type Snap struct {
id int
value int
}
func Constructor(length int) SnapshotArray {
nums := make([][]Snap, length)
for i := 0; i < length; i++ {
nums[i] = []Snap{{
id: 0,
value: 0,
}}
}
return SnapshotArray{
id: 0,
arr: nums,
}
}
func (this *SnapshotArray) Set(index int, val int) {
n := len(this.arr[index])
if this.arr[index][n-1].id == this.id {
this.arr[index][n-1].value = val
return
}
this.arr[index] = append(this.arr[index], Snap{
id: this.id,
value: val,
})
}
func (this *SnapshotArray) Snap() int {
id := this.id
this.id++
return id
}
func (this *SnapshotArray) Get(index int, snap_id int) int {
n := len(this.arr[index])
arr := this.arr[index]
left, right := 0, n-1
for left < right {
mid := left + (right-left)/2
if snap_id <= arr[mid].id {
right = mid
} else {
left = mid + 1
}
}
if left == n || arr[left].id > snap_id {
return arr[left-1].value
}
return arr[left].value
}
1155.掷骰子的N种方法(2)
这里有d个一样的骰子,每个骰子上都有f个面,分别标号为1, 2, ..., f。
我们约定:掷骰子的得到总点数为各骰子面朝上的数字的总和。
如果需要掷出的总点数为target,请你计算出有多少种不同的组合情况(所有的组合情况总共有 f^d 种),
模10^9 + 7后返回。
示例 1:输入:d = 1, f = 6, target = 3 输出:1
示例 2:输入:d = 2, f = 6, target = 7 输出:6
示例 3:输入:d = 2, f = 5, target = 10 输出:1
示例 4:输入:d = 1, f = 2, target = 3 输出:0
示例 5:输入:d = 30, f = 30, target = 500 输出:222616187
提示:1 <= d, f <= 30
1 <= target <= 1000
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
动态规划 |
O(n^3) |
O(n) |
02 |
动态规划 |
O(n^3) |
O(n^2) |
func numRollsToTarget(d int, f int, target int) int {
dp := make([]int, target+1)
dp[0] = 1
for i := 0; i < d; i++ {
next := make([]int, target+1)
for j := 1; j <= f; j++ {
for k := 0; k <= target-j; k++ {
next[k+j] = (next[k+j] + dp[k]) % 1000000007
}
}
dp = next
}
return dp[target]
}
# 2
func numRollsToTarget(d int, f int, target int) int {
dp := make([][]int, d+1)
for i := 0; i <= d; i++ {
dp[i] = make([]int, target+1)
}
dp[0][0] = 1
for i := 1; i <= d; i++ {
for j := i; j <= target; j++ {
for k := 1; k <= f; k++ {
if j >= k {
dp[i][j] = (dp[i][j] + dp[i-1][j-k]) % 1000000007
}
}
}
}
return dp[d][target]
}
1156.单字符重复子串的最大长度(1)
如果字符串中的所有字符都相同,那么这个字符串是单字符重复的字符串。
给你一个字符串text,你只能交换其中两个字符一次或者什么都不做,然后得到一些单字符重复的子串。返回其中最长的子串的长度。
示例 1:输入:text = "ababa" 输出:3
示例 2:输入:text = "aaabaaa" 输出:6
示例 3:输入:text = "aaabbaaa" 输出:4
示例 4:输入:text = "aaaaa" 输出:5
示例 5:输入:text = "abcdef" 输出:1
提示:1 <= text.length <= 20000
text 仅由小写英文字母组成。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
滑动窗口 |
O(n) |
O(1) |
func maxRepOpt1(text string) int {
res := 0
n := len(text)
arr := [26]int{}
for i := 0; i < n; i++ {
v := int(text[i] - 'a')
arr[v]++
}
for i := 0; i < n; {
v := int(text[i] - 'a')
countA := 0
for i+countA < n && text[i] == text[i+countA] {
countA++
}
j := i + countA + 1
countB := 0
for j+countB < n && text[i] == text[j+countB] {
countB++
}
total := min(countA+countB+1, arr[v])
res = max(res, total)
i = i + countA
}
return res
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
1161.最大层内元素和(2)
给你一个二叉树的根节点root。设根节点位于二叉树的第 1 层,而根节点的子节点位于第 2 层,依此类推。
请你找出层内元素之和 最大 的那几层(可能只有一层)的层号,并返回其中最小 的那个。
示例 1:输入:root = [1,7,0,7,-8,null,null] 输出:2
解释:第 1 层各元素之和为 1,
第 2 层各元素之和为 7 + 0 = 7,
第 3 层各元素之和为 7 + -8 = -1,
所以我们返回第 2 层的层号,它的层内元素之和最大。
示例 2:输入:root = [989,null,10250,98693,-89388,null,null,null,-32127] 输出:2
提示:树中的节点数介于1和10^4之间
-10^5 <= node.val <= 10^5
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
层序遍历 |
O(n) |
O(n) |
02 |
递归 |
O(n) |
O(n) |
func maxLevelSum(root *TreeNode) int {
res := 0
maxValue := math.MinInt32
if root == nil {
return 0
}
queue := make([]*TreeNode, 0)
queue = append(queue, root)
level := 1
for len(queue) > 0 {
length := len(queue)
sum := 0
for i := 0; i < length; i++ {
node := queue[i]
sum = sum + node.Val
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
}
if sum > maxValue {
maxValue = sum
res = level
}
queue = queue[length:]
level++
}
return res
}
# 2
var arr [][]int
func maxLevelSum(root *TreeNode) int {
arr = make([][]int, 0)
if root == nil {
return 0
}
dfs(root, 0)
res := 0
maxValue := math.MinInt32
for i := 0; i < len(arr); i++ {
sum := 0
for j := 0; j < len(arr[i]); j++ {
sum = sum + arr[i][j]
}
if sum > maxValue {
maxValue = sum
res = i + 1
}
}
return res
}
func dfs(root *TreeNode, level int) {
if root == nil {
return
}
if level == len(arr) {
arr = append(arr, []int{})
}
arr[level] = append(arr[level], root.Val)
dfs(root.Left, level+1)
dfs(root.Right, level+1)
}
1162.地图分析(2)
你现在手里有一份大小为 N x N 的 网格 grid,上面的每个 单元格 都用 0 和 1 标记好了。
其中 0 代表海洋,1 代表陆地,请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance):
(x0, y0) 和 (x1, y1) 这两个单元格之间的距离是 |x0 - x1| + |y0 - y1| 。
如果网格上只有陆地或者海洋,请返回 -1。
示例 1:输入:[[1,0,1],[0,0,0],[1,0,1]] 输出:2
解释:海洋单元格 (1, 1) 和所有陆地单元格之间的距离都达到最大,最大距离为 2。
示例 2:输入:[[1,0,0],[0,0,0],[0,0,0]] 输出:4
解释: 海洋单元格 (2, 2) 和所有陆地单元格之间的距离都达到最大,最大距离为 4。
提示: 1 <= grid.length == grid[0].length <= 100
grid[i][j] 不是 0 就是 1。
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
广度优先搜索 |
O(n^2) |
O(n^2) |
02 |
动态规划 |
O(n^2) |
O(n^2) |
var dx = []int{-1, 1, 0, 0}
var dy = []int{0, 0, -1, 1}
func maxDistance(grid [][]int) int {
queue := make([][2]int, 0)
for i := 0; i < len(grid); i++ {
for j := 0; j < len(grid[i]); j++ {
if grid[i][j] == 1 {
queue = append(queue, [2]int{i, j})
}
}
}
if len(queue) == 0 || len(queue) == len(grid)*len(grid[0]) {
return -1
}
res := -1
for len(queue) > 0 {
res++
length := len(queue)
for i := 0; i < length; i++ {
x1 := queue[i][0]
y1 := queue[i][1]
for i := 0; i < 4; i++ {
x := x1 + dx[i]
y := y1 + dy[i]
if 0 <= x && x < len(grid) && 0 <= y && y < len(grid[0]) && grid[x][y] == 0 {
queue = append(queue, [2]int{x, y})
grid[x][y] = 2
}
}
}
queue = queue[length:]
}
return res
}
# 2
func maxDistance(grid [][]int) int {
if len(grid) == 0 || len(grid[0]) == 0 {
return -1
}
n := len(grid)
m := len(grid[0])
dp := make([][]int, n)
for i := 0; i < n; i++ {
dp[i] = make([]int, m)
}
for i := 0; i < n; i++ {
for j := 0; j < m; j++ {
if grid[i][j] == 1 {
dp[i][j] = 0
} else {
dp[i][j] = math.MaxInt32
}
}
}
for i := 0; i < n; i++ {
for j := 0; j < m; j++ {
if grid[i][j] == 1 {
continue
}
if i >= 1 {
dp[i][j] = min(dp[i][j], dp[i-1][j]+1)
}
if j >= 1 {
dp[i][j] = min(dp[i][j], dp[i][j-1]+1)
}
}
}
for i := n - 1; i >= 0; i-- {
for j := m - 1; j >= 0; j-- {
if grid[i][j] == 1 {
continue
}
if i < n-1 {
dp[i][j] = min(dp[i][j], dp[i+1][j]+1)
}
if j < m-1 {
dp[i][j] = min(dp[i][j], dp[i][j+1]+1)
}
}
}
res := -1
for i := 0; i < n; i++ {
for j := 0; j < m; j++ {
if grid[i][j] == 1 {
continue
}
res = max(res, dp[i][j])
}
}
if res == math.MaxInt32 {
return -1
}
return res
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
1169.查询无效交易(2)
如果出现下述两种情况,交易 可能无效:
交易金额超过 ¥1000
或者,它和另一个城市中同名的另一笔交易相隔不超过 60 分钟(包含 60 分钟整)
每个交易字符串transactions[i]由一些用逗号分隔的值组成,这些值分别表示交易的名称,时间(以分钟计),金额以及城市。
给你一份交易清单transactions,返回可能无效的交易列表。你可以按任何顺序返回答案。
示例 1:输入:transactions = ["alice,20,800,mtv","alice,50,100,beijing"]
输出:["alice,20,800,mtv","alice,50,100,beijing"]
解释:第一笔交易是无效的,因为第二笔交易和它间隔不超过 60 分钟、名称相同且发生在不同的城市。同样,第二笔交易也是无效的。
示例 2:输入:transactions = ["alice,20,800,mtv","alice,50,1200,mtv"] 输出:["alice,50,1200,mtv"]
示例 3:输入:transactions = ["alice,20,800,mtv","bob,50,1200,mtv"] 输出:["bob,50,1200,mtv"]
提示:transactions.length <= 1000
每笔交易transactions[i]按"{name},{time},{amount},{city}"的格式进行记录
每个交易名称{name}和城市{city}都由小写英文字母组成,长度在1到10之间
每个交易时间{time}由一些数字组成,表示一个0到1000之间的整数
每笔交易金额{amount}由一些数字组成,表示一个0 到2000之间的整数
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
哈希辅助 |
O(n^3) |
O(n) |
02 |
遍历 |
O(n^2) |
O(n) |
type Node struct {
Name string
Time int
Amount int
City string
Index int
Flag bool
}
func invalidTransactions(transactions []string) []string {
res := make([]string, 0)
m := make(map[string][]Node)
n := len(transactions)
for i := 0; i < n; i++ {
arr := strings.Split(transactions[i], ",")
name, city := arr[0], arr[3]
t, _ := strconv.Atoi(arr[1])
amount, _ := strconv.Atoi(arr[2])
m[name] = append(m[name], Node{Name: name, Time: t,
Amount: amount, City: city, Index: i,
})
}
for _, v := range m {
sort.Slice(v, func(i, j int) bool {
return v[i].Time < v[j].Time
})
for i := 0; i < len(v); i++ {
if v[i].Amount > 1000 {
v[i].Flag = true
}
for j := i + 1; j < len(v); j++ {
if v[j].Time-v[i].Time <= 60 {
if v[i].City != v[j].City {
v[i].Flag = true
v[j].Flag = true
}
} else {
break
}
}
}
}
for _, v := range m {
for i := 0; i < len(v); i++ {
if v[i].Flag == true {
res = append(res, transactions[v[i].Index])
}
}
}
return res
}
# 2
type Node struct {
Name string
Time int
Amount int
City string
}
func invalidTransactions(transactions []string) []string {
res := make([]string, 0)
arr := make([]Node, 0)
n := len(transactions)
for i := 0; i < n; i++ {
temp := strings.Split(transactions[i], ",")
name, city := temp[0], temp[3]
t, _ := strconv.Atoi(temp[1])
amount, _ := strconv.Atoi(temp[2])
arr = append(arr, Node{Name: name, Time: t, Amount: amount, City: city})
}
for i := 0; i < n; i++ {
if arr[i].Amount > 1000 {
res = append(res, transactions[i])
continue
}
for j := 0; j < n; j++ {
if i == j {
continue
}
if arr[i].Name == arr[j].Name && arr[i].City != arr[j].City &&
abs(arr[i].Time-arr[j].Time) <= 60 {
res = append(res, transactions[i])
break
}
}
}
return res
}
func abs(a int) int {
if a < 0 {
return -a
}
return a
}
1171.从链表中删去总和值为零的连续节点(4)
给你一个链表的头节点head,请你编写代码,反复删去链表中由 总和值为 0 的连续节点组成的序列,
直到不存在这样的序列为止。
删除完毕后,请你返回最终结果链表的头节点。
你可以返回任何满足题目要求的答案。
(注意,下面示例中的所有序列,都是对ListNode对象序列化的表示。)
示例 1:输入:head = [1,2,-3,3,1] 输出:[3,1]
提示:答案 [1,2,1] 也是正确的。
示例 2:输入:head = [1,2,3,-3,4] 输出:[1,2,4]
示例 3:输入:head = [1,2,3,-3,-2] 输出:[1]
提示:给你的链表中可能有 1 到1000个节点。
对于链表中的每个节点,节点的值:-1000 <= node.val <= 1000.
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
哈希辅助 |
O(n) |
O(n) |
02 |
遍历 |
O(n) |
O(1) |
03 |
哈希辅助 |
O(n) |
O(n) |
04 |
递归 |
O(n^2) |
O(n) |
func removeZeroSumSublists(head *ListNode) *ListNode {
m := make(map[int]*ListNode)
res := head
sum := 0
for cur := head; cur != nil; cur = cur.Next {
sum = sum + cur.Val
if sum == 0 {
res = cur.Next
m = make(map[int]*ListNode)
continue
}
if _, ok := m[sum]; ok == false {
m[sum] = cur
continue
}
first := m[sum]
tempSum := sum
for temp := first.Next; temp != cur; temp = temp.Next {
tempSum = tempSum + temp.Val
delete(m, tempSum)
}
first.Next = cur.Next
}
return res
}
# 2
func removeZeroSumSublists(head *ListNode) *ListNode {
res := &ListNode{Next: head}
for cur := res; cur != nil; {
sum := 0
for temp := cur.Next; temp != nil; temp = temp.Next {
sum = sum + temp.Val
if sum == 0 {
cur.Next = temp.Next
}
}
cur = cur.Next
}
return res.Next
}
# 3
func removeZeroSumSublists(head *ListNode) *ListNode {
res := &ListNode{Next: head}
m := make(map[int]*ListNode)
sum := 0
for cur := res; cur != nil; cur = cur.Next {
sum = sum + cur.Val
m[sum] = cur
}
sum = 0
for cur := res; cur != nil; cur = cur.Next {
sum = sum + cur.Val
cur.Next = m[sum].Next
}
return res.Next
}
# 4
func removeZeroSumSublists(head *ListNode) *ListNode {
if head == nil {
return nil
}
sum := 0
for cur := head; cur != nil; cur = cur.Next {
sum = sum + cur.Val
if sum == 0 {
return removeZeroSumSublists(cur.Next)
}
}
head.Next = removeZeroSumSublists(head.Next)
return head
}
1177.构建回文串检测(1)
给你一个字符串s,请你对s的子串进行检测。
每次检测,待检子串都可以表示为queries[i] = [left, right, k]。
我们可以 重新排列 子串s[left], ..., s[right],并从中选择 最多 k项替换成任何小写英文字母。
如果在上述检测过程中,子串可以变成回文形式的字符串,那么检测结果为true,否则结果为false。
返回答案数组answer[],其中answer[i]是第i个待检子串queries[i]的检测结果。
注意:在替换时,子串中的每个字母都必须作为 独立的 项进行计数,
也就是说,如果s[left..right] = "aaa"且k = 2,我们只能替换其中的两个字母。
(另外,任何检测都不会修改原始字符串 s,可以认为每次检测都是独立的)
示例:输入:s = "abcda", queries = [[3,3,0],[1,2,0],[0,3,1],[0,3,2],[0,4,1]] 输出:[true,false,false,true,true]
解释:queries[0] : 子串 = "d",回文。
queries[1] :子串 = "bc",不是回文。
queries[2] :子串 = "abcd",只替换 1 个字符是变不成回文串的。
queries[3] :子串 = "abcd",可以变成回文的 "abba"。 也可以变成 "baab",先重新排序变成 "bacd",然后把 "cd" 替换为 "ab"。
queries[4] :子串 = "abcda",可以变成回文的 "abcba"。
提示:1 <= s.length,queries.length<= 10^5
0 <= queries[i][0] <= queries[i][1] <s.length
0 <= queries[i][2] <= s.length
s 中只有小写英文字母
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
前缀和+位运算 |
O(n) |
O(n) |
func canMakePaliQueries(s string, queries [][]int) []bool {
n := len(s)
arr := make([]int, n+1)
cur := 0
for i := 0; i < n; i++ {
value := int(s[i] - 'a')
cur = cur ^ (1 << value)
arr[i+1] = cur
}
res := make([]bool, len(queries))
for i := 0; i < len(queries); i++ {
a, b, c := queries[i][0], queries[i][1], queries[i][2]
status := arr[b+1] ^ arr[a]
if bits.OnesCount(uint(status)) <= 2*c+1 {
res[i] = true
}
}
return res
}
1186.删除一次得到子数组最大和(2)
给你一个整数数组,返回它的某个非空 子数组(连续元素)在执行一次可选的删除操作后,所能得到的最大元素总和。
换句话说,你可以从原数组中选出一个子数组,并可以决定要不要从中删除一个元素(只能删一次哦),
(删除后)子数组中至少应当有一个元素,然后该子数组(剩下)的元素总和是所有子数组之中最大的。
注意,删除一个元素后,子数组 不能为空。
请看示例:
示例 1:输入:arr = [1,-2,0,3] 输出:4
解释:我们可以选出 [1, -2, 0, 3],然后删掉 -2,这样得到 [1, 0, 3],和最大。
示例 2:输入:arr = [1,-2,-2,3] 输出:3
解释:我们直接选出 [3],这就是最大和。
示例 3:输入:arr = [-1,-1,-1,-1] 输出:-1
解释:最后得到的子数组不能为空,所以我们不能选择 [-1] 并从中删去 -1 来得到 0。
我们应该直接选择 [-1],或者选择 [-1, -1] 再从中删去一个 -1。
提示:1 <= arr.length <= 10^5
-10^4 <= arr[i] <= 10^4
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
动态规划 |
O(n) |
O(n) |
02 |
动态规划 |
O(n) |
O(1) |
func maximumSum(arr []int) int {
n := len(arr)
dp := make([][2]int, n)
dp[0][0] = arr[0]
dp[0][1] = 0
res := dp[0][0]
for i := 1; i < n; i++ {
dp[i][0] = max(dp[i-1][0]+arr[i], arr[i])
dp[i][1] = max(dp[i-1][1]+arr[i], dp[i-1][0])
res = max(res, max(dp[i][0], dp[i][1]))
}
return res
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
# 2
func maximumSum(arr []int) int {
n := len(arr)
a := arr[0]
b := 0
res := a
for i := 1; i < n; i++ {
a, b = max(a+arr[i], arr[i]), max(b+arr[i], a)
res = max(res, max(a, b))
}
return res
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
1190.反转每对括号间的子串(2)
给出一个字符串s(仅含有小写英文字母和括号)。
请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。
注意,您的结果中 不应 包含任何括号。
示例 1:输入:s = "(abcd)" 输出:"dcba"
示例 2:输入:s = "(u(love)i)" 输出:"iloveu"
示例 3:输入:s = "(ed(et(oc))el)" 输出:"leetcode"
示例 4:输入:s = "a(bcdefghijkl(mno)p)q" 输出:"apmnolkjihgfedcbq"
提示:0 <= s.length <= 2000
s 中只有小写英文字母和括号
我们确保所有括号都是成对出现的
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
栈 |
O(n^2) |
O(n) |
02 |
栈 |
O(n) |
O(n) |
func reverseParentheses(s string) string {
arr := []byte(s)
stack := make([]int, 0)
for i := 0; i < len(s); i++ {
if s[i] == '(' {
stack = append(stack, i)
} else if s[i] == ')' {
reverse(arr, stack[len(stack)-1], i)
stack = stack[:len(stack)-1]
}
}
s = string(arr)
s = strings.ReplaceAll(s, "(", "")
s = strings.ReplaceAll(s, ")", "")
return s
}
func reverse(arr []byte, i, j int) {
for i < j {
arr[i], arr[j] = arr[j], arr[i]
i++
j--
}
}
# 2
func reverseParentheses(s string) string {
stack := make([]int, 0)
m := make(map[int]int)
for i := 0; i < len(s); i++ {
if s[i] == '(' {
stack = append(stack, i)
} else if s[i] == ')' {
last := stack[len(stack)-1]
m[i] = last
m[last] = i
stack = stack[:len(stack)-1]
}
}
dir := 1
res := make([]byte, 0)
for i := 0; i < len(s); i = i + dir {
if s[i] == '(' || s[i] == ')' {
i = m[i]
dir = -dir
} else {
res = append(res, s[i])
}
}
return string(res)
}
1191.K次串联后最大子数组之和(1)
给你一个整数数组 arr 和一个整数 k。
首先,我们要对该数组进行修改,即把原数组 arr 重复 k 次。
举个例子,如果 arr = [1, 2] 且 k = 3,那么修改后的数组就是 [1, 2, 1, 2, 1, 2]。
然后,请你返回修改后的数组中的最大的子数组之和。
注意,子数组长度可以是 0,在这种情况下它的总和也是 0。
由于 结果可能会很大,所以需要 模(mod) 10^9 + 7 后再返回。
示例 1:输入:arr = [1,2], k = 3 输出:9
示例 2:输入:arr = [1,-2,1], k = 5 输出:2
示例 3:输入:arr = [-1,-2], k = 7 输出:0
提示:
1 <= arr.length <= 10^5
1 <= k <= 10^5
-10^4 <= arr[i] <= 10^4
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
遍历 |
O(n) |
O(1) |
func kConcatenationMaxSum(arr []int, k int) int {
cur := 0
sum := 0
res := 0
for i := 0; i < len(arr); i++ {
sum = sum + arr[i]
cur = max(cur+arr[i], arr[i])
res = max(res, cur)
}
for i := 0; i < len(arr); i++ {
cur = max(cur+arr[i], arr[i])
res = max(res, cur)
}
if sum <= 0 {
return res % 1000000007
}
return (res + (k-2)*sum) % 1000000007
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
1101-1200-Hard
1147.段式回文(2)
段式回文 其实与 一般回文 类似,只不过是最小的单位是 一段字符而不是 单个字母。
举个例子,对于一般回文 "abcba" 是回文,而 "volvo" 不是,但如果我们把"volvo" 分为 "vo"、"l"、"vo" 三段,
则可以认为 “(vo)(l)(vo)” 是段式回文(分为 3 段)。
给你一个字符串text,在确保它满足段式回文的前提下,请你返回 段 的最大数量k。
如果段的最大数量为k,那么存在满足以下条件的a_1, a_2, ..., a_k:
每个a_i都是一个非空字符串;
将这些字符串首位相连的结果a_1 + a_2 + ... + a_k和原始字符串text相同;
对于所有1 <= i <= k,都有a_i = a_{k+1 - i}。
示例 1:输入:text = "ghiabcdefhelloadamhelloabcdefghi" 输出:7
解释:我们可以把字符串拆分成 "(ghi)(abcdef)(hello)(adam)(hello)(abcdef)(ghi)"。
示例 2:输入:text = "merchant" 输出:1
解释:我们可以把字符串拆分成 "(merchant)"。
示例 3:输入:text = "antaprezatepzapreanta" 输出:11
解释:我们可以把字符串拆分成 "(a)(nt)(a)(pre)(za)(tpe)(za)(pre)(a)(nt)(a)"。
示例 4:输入:text = "aaa" 输出:3
解释:我们可以把字符串拆分成 "(a)(a)(a)"。
提示:text仅由小写英文字符组成。
1 <= text.length <= 1000
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
递归 |
O(n) |
O(n) |
02 |
双指针 |
O(n) |
O(1) |
func longestDecomposition(text string) int {
n := len(text)
if n <= 1 {
return n
}
for i := 1; i <= n/2; i++ {
if text[:i] == text[n-i:] {
return 2 + longestDecomposition(text[i:n-i])
}
}
return 1
}
# 2
func longestDecomposition(text string) int {
n := len(text)
if n <= 1 {
return n
}
res := 0
left, right := 0, n
for left < right {
for k := 1; ; k++ {
if k > (right-left)/2 {
res++
return res
}
if text[left:left+k] == text[right-k:right] {
res = res + 2
left = left + k
right = right - k
break
}
}
}
return res
}
1187.使数组严格递增
题目
给你两个整数数组arr1 和 arr2,返回使arr1严格递增所需要的最小「操作」数(可能为 0)。
每一步「操作」中,你可以分别从 arr1 和 arr2 中各选出一个索引,
分别为i 和j,0 <=i < arr1.length和0 <= j < arr2.length,然后进行赋值运算arr1[i] = arr2[j]。
如果无法让arr1严格递增,请返回-1。
示例 1:输入:arr1 = [1,5,3,6,7], arr2 = [1,3,2,4] 输出:1
解释:用 2 来替换 5,之后 arr1 = [1, 2, 3, 6, 7]。
示例 2:输入:arr1 = [1,5,3,6,7], arr2 = [4,3,1] 输出:2
解释:用 3 来替换 5,然后用 4 来替换 3,得到 arr1 = [1, 3, 4, 6, 7]。
示例3:输入:arr1 = [1,5,3,6,7], arr2 = [1,6,3,3] 输出:-1
解释:无法使 arr1 严格递增。
提示:1 <= arr1.length, arr2.length <= 2000
0 <= arr1[i], arr2[i] <= 10^9
解题思路
No. |
思路 |
时间复杂度 |
空间复杂度 |
01 |
递归 |
O(n) |
O(n) |
go