// 使用符号标记,转成正数,循环得到%10的余数,再加上符号func reverse(x int) int { flag := 1if x < 0 { flag = -1 x = -1 * x } result := 0for x > 0 { temp := x % 10 x = x / 10 result = result*10 + temp } result = flag * resultif result > math.MaxInt32 || result < math.MinInt32 { result = 0 }return result}// 对x进行逐个%10取个位,一旦溢出,直接跳出循环func reverse(x int) int { result := 0for x != 0 { temp := x % 10 result = result*10 + tempif result > math.MaxInt32 || result < math.MinInt32 {return0 } x = x / 10 }return result}
// 数学解法,取出后半段数字进行翻转,然后判断是否相等func isPalindrome(x int) bool {if x < 0 || (x%10 == 0 && x != 0) {returnfalse } revertedNumber := 0for x > revertedNumber { temp := x % 10 revertedNumber = revertedNumber*10 + temp x = x / 10 }// for example:// x = 1221 => x = 12 revertedNumber = 12// x = 12321 => x = 12 revertedNumber = 123return x == revertedNumber || x == revertedNumber/10}// 转成字符串,依次判断func isPalindrome(x int) bool {if x < 0 {returnfalse } s := strconv.Itoa(x)for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {if s[i] != s[j] {returnfalse } }returntrue}// 转成byte数组,依次判断,同2func isPalindrome(x int) bool {if x < 0 {returnfalse } arrs := []byte(strconv.Itoa(x)) Len := len(arrs)for i := 0; i < Len/2; i++ {if arrs[i] != arrs[Len-i-1] {returnfalse } }returntrue}
13.罗马数字转整数(2)
题目
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。字符 数值I 1V 5X 10L 50C 100D 500M 1000例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况: I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。示例 1:输入: "III" 输出: 3示例 2: 输入: "IV" 输出: 4示例 3: 输入: "IX" 输出: 9示例 4: 输入: "LVIII" 输出: 58 解释: L = 50, V= 5, III = 3.示例 5: 输入: "MCMXCIV" 输出: 1994 解释: M = 1000, CM = 900, XC = 90, IV = 4.
解答思路
No.
思路
时间复杂度
空间复杂度
01
本质上其实就是全部累加,然后遇到特殊的就做判断。使用一个字段记录递增
O(n)
O(1)
02(最优)
从右到左遍历字符串,如果当前字符代表的值不小于其右边,就加上该值;否则就减去该值。
O(n)
O(1)
// 带标记位func romanToInt(s string) int { m := map[byte]int{'I': 1,'V': 5,'X': 10,'L': 50,'C': 100,'D': 500,'M': 1000, } result := 0 last := 0for i := len(s) - 1; i >= 0; i-- { current := m[s[i]] flag := 1if current < last { flag = -1 } result = result + flag*current last = current }return result}// 不带标记位,小于则减去2倍数func romanToInt(s string) int { m := map[byte]int{'I': 1,'V': 5,'X': 10,'L': 50,'C': 100,'D': 500,'M': 1000, } result := 0 last := 0for i := len(s) - 1; i >= 0; i-- { current := m[s[i]]if current < last { result = result - current }else { result = result + current } last = current }return result}
给定两个二进制字符串,返回他们的和(用二进制表示)。输入为非空字符串且只包含数字 1 和 0。示例 1: 输入: a = "11", b = "1" 输出: "100"示例 2:输入: a = "1010", b = "1011" 输出: "10101"
解题思路
No.
思路
时间复杂度
空间复杂度
01
数组
O(n)
O(n)
02(最优)
模拟
O(n)
O(n)
// 转换成数组模拟func addBinary(a string, b string) string {iflen(a) < len(b) { a, b = b, a } length := len(a) A := transToInt(a, length) B := transToInt(b, length)return makeString(add(A, B))}func transToInt(s string, length int) []int { result := make([]int, length) ls := len(s)for i, b := range s { result[length-ls+i] = int(b - '0') }return result}func add(a, b []int) []int { length := len(a) + 1 result := make([]int, length)for i := length - 1; i >= 1; i-- { temp := result[i] + a[i-1] + b[i-1] result[i] = temp % 2 result[i-1] = temp / 2 } i := 0for i < length-1 && result[i] == 0 { i++ }return result[i:]}func makeString(nums []int) string { bytes := make([]byte, len(nums))for i := range bytes { bytes[i] = byte(nums[i]) + '0' }returnstring(bytes)}// 直接模拟func addBinary(a string, b string) string { i := len(a) - 1 j := len(b) - 1 result := "" flag := 0 current := 0for i >= 0 || j >= 0 { intA, intB := 0, 0if i >= 0 { intA = int(a[i] - '0') }if j >= 0 { intB = int(b[j] - '0') } current = intA + intB + flag flag = 0if current >= 2 { flag = 1 current = current - 2 } cur := strconv.Itoa(current) result = cur + result i-- j-- }if flag == 1 { result = "1" + result }return result}
69.x的平方根 (5)
题目
实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。示例 1:输入: 4 输出: 2示例 2:输入: 8 输出: 2说明: 8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
解题思路
No.
思路
时间复杂度
空间复杂度
01
系统函数
O(log(n))
O(1)
02
系统函数
O(log(n))
O(1)
03(最优)
牛顿迭代法
O(log(n))
O(1)
04
二分查找法
O(log(n))
O(1)
05
暴力法:遍历
O(n)
O(1)
// 系统函数func mySqrt(x int) int { result := int(math.Sqrt(float64(x)))return result}// 系统函数func mySqrt(x int) int { result := math.Floor(math.Sqrt(float64(x)))returnint(result)}// 牛顿迭代法func mySqrt(x int) int { result := xfor result*result > x { result = (result + x/result) / 2 }return result}// 二分查找法func mySqrt(x int) int { left := 1 right := xfor left <= right { mid := (left + right) / 2if mid == x/mid {return mid } elseif mid < x/mid { left = mid + 1 } else { right = mid - 1 } }if left * left <= x{return left }else {return left-1 }}// 暴力法:遍历func mySqrt(x int) int { result := 0for i := 1; i <= x/i; i++ {if i*i == x {return i } result = i }return result}
// 递归var arr []intfunc climbStairs(n int) int { arr = make([]int, n+1)return climbStart(0, n)}func climbStart(i, n int) int {if i > n {return0 }if i == n {return1 }if arr[i] > 0 {return arr[i] } arr[i] = climbStart(i+1, n) + climbStart(i+2, n)return arr[i]}// 动态规划func climbStairs(n int) int {if n == 1 {return1 }if n == 2 {return2 } dp := make([]int, n+1) dp[1] = 1 dp[2] = 2for i := 3; i <= n; i++ { dp[i] = dp[i-1] + dp[i-2] }return dp[n]}// 斐波那契func climbStairs(n int) int {if n == 1 {return1 } first := 1 second := 2for i := 3; i <= n; i++ { third := first + second first = second second = third }return second}
func lengthOfLongestSubstring(s string) int { arr := [256]int{}for i := range arr { arr[i] = -1 } max, j := 0, 0for i := 0; i < len(s); i++ {if arr[s[i]] >= j { j = arr[s[i]] + 1 } elseif i+1-j > max { max = i + 1 - j } arr[s[i]] = i }return max}# 2func lengthOfLongestSubstring(s string) int { max, j := 0, 0for i := 0; i < len(s); i++ { index := strings.Index(s[j:i], string(s[i]))if index == -1 {continue }if i-j > max { max = i - j } j = j + index + 1 }iflen(s)-j > max { max = len(s) - j }return max}# 3func lengthOfLongestSubstring(s string) int { m := make(map[uint8]int) max, j := 0, 0for i := 0; i < len(s); i++ {if v, ok := m[s[i]]; ok && v >= j { j = v + 1 } elseif i+1-j > max { max = i + 1 - j } m[s[i]] = i }return max}# 4func lengthOfLongestSubstring(s string) int {iflen(s) < 1 {return0 } dp := make([]int, len(s)) dp[0] = 1 res := 1 m := make(map[byte]int) m[s[0]] = 0for i := 1; i < len(s); i++ { index := -1if value, ok := m[s[i]]; ok { index = value }if i-index > dp[i-1] { dp[i] = dp[i-1] + 1 } else { dp[i] = i - index } m[s[i]] = iif dp[i] > res { res = dp[i] } }return res}# 5func lengthOfLongestSubstring(s string) int { arr := [256]int{}for i := range arr { arr[i] = -1 } res, j := 0, -1for i := 0; i < len(s); i++ {if arr[s[i]] > j { // 出现重复了,更新下标 j = arr[s[i]] } else { res = max(res, i-j) // 没有重复,更新长度 } arr[s[i]] = i }return res}func max(a, b int) int {if a > b {return a }return b}
5.最长回文子串(5)
题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。示例 1:输入: "babad"输出: "bab" 注意: "aba" 也是一个有效答案。示例 2:输入: "cbbd" 输出: "bb"
解题思路
No.
思路
时间复杂度
空间复杂度
01
动态规划
O(n^2)
O(n^2)
02
中心扩展
O(n^2)
O(1)
03
暴力法
O(n^3)
O(1)
04
Manacher算法
O(n^2)
O(n)
05
Manacher算法
O(n)
O(n)
// dp(l,r)=dp(l+1,r−1)&&(s[l]==s[r])// dp[l,r]:字符串s从索引l到r的子串是否是回文串func longestPalindrome(s string) string {iflen(s) <= 1 {return s } dp := make([][]bool, len(s)) start := 0 max := 1for r := 0; r < len(s); r++ { dp[r] = make([]bool, len(s)) dp[r][r] = truefor l := 0; l < r; l++ {if s[l] == s[r] && (r-l <= 2 || dp[l+1][r-1] == true) { dp[l][r] = true } else { dp[l][r] = false }if dp[l][r] == true {if r-l+1 > max { max = r - l + 1 start = l } } } }return s[start : start+max]}# 2func longestPalindrome(s string) string {iflen(s) <= 1 {return s } start := 0 end := 0for i := 0; i < len(s); i++ { left1, right1 := find(s, i, i) left2, right2 := find(s, i, i+1)if right1-left1 > end-start { start, end = left1, right1 }if right2-left2 > end-start { start, end = left2, right2 } }return s[start : end+1]}func find(s string, left, right int) (int, int) {for ; 0 <= left && right < len(s) && s[left] == s[right]; left, right = left-1, right+1 { }return left + 1, right - 1}# 3func longestPalindrome(s string) string {iflen(s) <= 1 {return s } res := ""for i := 0; i < len(s); i++ {for j := i; j < len(s); j++ { str := s[i : j+1]iflen(str) < len(res) && res != "" {continue }if judge(str) == true && len(res) < len(str) { res = str } } }return res}func judge(s string) bool {for i := 0; i < len(s)/2; i++ {if s[i] != s[len(s)-1-i] {returnfalse } }returntrue}# 4func longestPalindrome(s string) string {iflen(s) <= 1 {return s } str := add(s) length := len(str) max := 1 begin := 0for i := 0; i < length; i++ { curLength := search(str, i)if curLength > max { max = curLength begin = (i - max) / 2 } }return s[begin : begin+max]}func search(s string, center int) int { i := center - 1 j := center + 1 step := 0for ; i >= 0 && j < len(s) && s[i] == s[j]; i, j = i-1, j+1 { step++ }return step}func add(s string) string {var res []runefor _, v := range s { res = append(res, '#') res = append(res, v) } res = append(res, '#')returnstring(res)}#func longestPalindrome(s string) string {iflen(s) <= 1 {return s } str := add(s) length := len(str) temp := make([]int, length) maxRight := 0 center := 0 max := 1 begin := 0for i := 0; i < length; i++ {if i < maxRight { mirror := 2*center - i temp[i] = min(maxRight-i, temp[mirror]) } left := i - (1 + temp[i]) right := i + (1 + temp[i])for left >= 0 && right < len(str) && str[left] == str[right] { temp[i]++ left-- right++ }if i+temp[i] > maxRight { maxRight = i + temp[i] center = i }if temp[i] > max { max = temp[i] begin = (i - max) / 2 } }return s[begin : begin+max]}func add(s string) string {var res []runefor _, v := range s { res = append(res, '#') res = append(res, v) } res = append(res, '#')returnstring(res)}func min(a, b int) int {if a > b {return b }return a}
6.Z字形变换(2)
题目
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:L C I RE T O E S I I GE D H N之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。请你实现这个将字符串进行指定行数变换的函数:string convert(string s, int numRows);示例 1:输入: s = "LEETCODEISHIRING", numRows = 3 输出: "LCIRETOESIIGEDHN"示例 2:输入: s = "LEETCODEISHIRING", numRows = 4 输出: "LDREOEIIECIHNTSG"解释:L D RE O E I IE C I H NT S G
解题思路
No.
思路
时间复杂度
空间复杂度
01
遍历
O(n)
O(n)
02
遍历
O(n)
O(n)
func convert(s string, numRows int) string {if numRows == 1 {return s } arr := []rune(s) total := numRows*2 - 2 res := make([]string, numRows)for i := 0; i < len(arr); i++ { index := i % totalif index < numRows { res[index] = res[index] + string(arr[i]) } else { res[total-index] = res[total-index] + string(arr[i]) } }return strings.Join(res, "")}#func convert(s string, numRows int) string {if numRows == 1 {return s } arr := []rune(s) res := make([]string, numRows) flag := -1 index := 0for i := 0; i < len(arr); i++ { res[index] = res[index] + string(arr[i])if index == 0 || index == numRows-1 { flag = -flag } index = index + flag }return strings.Join(res, "")}
func myAtoi(str string) int { i := 0for i < len(str) && str[i] == ' ' { i++ } str = str[i:] arr := make([]byte, 0) isFlag := byte(' ')for j := 0; j < len(str); j++ {if str[j] >= '0' && str[j] <= '9' { arr = append(arr, str[j]) } else {iflen(arr) > 0 {break }if str[j] != ' ' && str[j] != '+' && str[j] != '-' {return0 }if isFlag != ' ' {return0 } isFlag = str[j] } } res := 0for i := 0; i < len(arr); i++ { value := int(arr[i] - '0') res = res*10 + valueif isFlag == '-' {if-1*res < math.MinInt32 {return math.MinInt32 } } elseif isFlag == ' ' || isFlag == '+' {if res > math.MaxInt32 {return math.MaxInt32 } } }if isFlag == '-' {return-1 * res }return res}#func myAtoi(str string) int { re := regexp.MustCompile(`^[+-]?\d+`) arrS := re.FindAllString(strings.Trim(str, " "), -1)iflen(arrS) == 0{return0 } arr := arrS[0] res := 0 isFlag := byte(' ')if !(arr[0] >= '0' && arr[0] <= '9') { isFlag = arr[0] arr = arr[1:] }for i := 0; i < len(arr); i++ { value := int(arr[i] - '0')if isFlag == '-' {if res > 214748364 || (res==214748364 && value >= 8) {return math.MinInt32 } } elseif isFlag == ' ' || isFlag == '+' {if res > 214748364 || (res==214748364 && value >= 7) {return math.MaxInt32 } } res = res*10 + value }if isFlag == '-' {return-1 * res }return res}#func myAtoi(str string) int { str = strings.TrimSpace(str) result := 0 flag := 1for i, v := range str {if v >= '0' && v <= '9' { result = result*10 + int(v-'0') } elseif v == '-' && i == 0 { flag = -1 } elseif v == '+' && i == 0 { flag = 1 } else {break }if result > math.MaxInt32 {if flag == -1 {return math.MinInt32 }return math.MaxInt32 } }return flag * result}
11.盛最多水的容器(2)
题目
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。说明:你不能倾斜容器,且 n 的值至少为 2。图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。示例:输入:[1,8,6,2,5,4,8,3,7] 输出:49
解题思路
No.
思路
时间复杂度
空间复杂度
01
遍历-双指针
O(n)
O(1)
02
遍历-暴力法
O(n^2)
O(1)
func maxArea(height []int) int { i := 0 j := len(height) - 1 res := 0for i < j { area := (j - i) * min(height[i], height[j])if area > res { res = area }// 移动较小的指针,尝试获取更大的面积if height[i] > height[j] { j-- } else { i++ } }return res}func min(a, b int) int {if a > b {return b }return a}#func maxArea(height []int) int { res := 0for i := 0; i < len(height); i++ {for j := i + 1; j < len(height); j++ { area := (j - i) * min(height[i], height[j])if area > res { res = area } } }return res}func min(a, b int) int {if a > b {return b }return a}
12.整数转罗马数字(2)
题目
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。字符 数值I 1V 5X 10L 50C 100D 500M 1000例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况: I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。示例 1:输入: 3输出: "III"示例 2:输入: 4 输出: "IV"示例 3:输入: 9 输出: "IX"示例 4:输入: 58 输出: "LVIII"解释: L = 50, V = 5, III = 3.示例 5:输入: 1994 输出: "MCMXCIV"解释: M = 1000, CM = 900, XC = 90, IV = 4.
func threeSumClosest(nums []int, target int) int { sort.Ints(nums) res := nums[0] + nums[1] + nums[2]for i := 0; i < len(nums); i++ { left := i + 1 right := len(nums) - 1for left < right { sum := nums[i] + nums[left] + nums[right]if sum > target { right-- } elseif sum < target { left++ } else {return target }if abs(sum, target) < abs(res, target) { res = sum } } }return res}func abs(a, b int) int {if a > b {return a - b }return b - a}#func threeSumClosest(nums []int, target int) int { res := nums[0] + nums[1] + nums[2]for i := 0; i < len(nums); i++ {for j := i + 1; j < len(nums); j++ {for k := j + 1; k < len(nums); k++ { sum := nums[i] + nums[j] + nums[k]if abs(sum, target) < abs(res, target) { res = sum } } } }return res}func abs(a, b int) int {if a > b {return a - b }return b - a}
func myPow(x float64, n int) float64 {if n == 0 {return1 }if n < 0 {return1 / myPow(x, -n) }if n%2 == 1 {return x * myPow(x, n-1) }return myPow(x*x, n/2)}#func myPow(x float64, n int) float64 {if n < 0 { x = 1 / x n = -n } res := float64(1)for n > 0 {if n%2 == 1 { res = res * x } x = x * x n = n / 2 }return res}#func myPow(x float64, n int) float64 {return math.Pow(x, float64(n))}#func myPow(x float64, n int) float64 {if n == 0 {return1 }if n == 1 {return x } res := 1.0if n > 0 { res = myPow(x, n/2)return res * res * myPow(x, n%2) } else { res = myPow(x, -n/2) res = res * res * myPow(x, -n%2)return1 / res }}
54.螺旋矩阵(2)
题目
给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。示例 1:输入:[ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ]]输出: [1,2,3,6,9,8,7,4,5]示例 2:输入:[ [1, 2, 3, 4], [5, 6, 7, 8], [9,10,11,12]]输出: [1,2,3,4,8,12,11,10,9,5,6,7]
解题思路
No.
思路
时间复杂度
空间复杂度
01
遍历
O(n^2)
O(n^2)
02
遍历
O(n^2)
O(n^2)
var res []intfunc spiralOrder(matrix [][]int) []int { res = make([]int, 0) rows := len(matrix)if rows == 0 {return res } cols := len(matrix[0])if cols == 0 {return res } start := 0for cols > start*2 && rows > start*2 { printCircle(matrix, cols, rows, start) start++ }return res}func printCircle(matrix [][]int, cols, rows, start int) { x := cols - 1 - start y := rows - 1 - start// 左到右for i := start; i <= x; i++ { res = append(res, matrix[start][i]) }// 上到下if start < y {for i := start + 1; i <= y; i++ { res = append(res, matrix[i][x]) } }// 右到左if start < x && start < y {for i := x - 1; i >= start; i-- { res = append(res, matrix[y][i]) } }// 下到上if start < x && start < y-1 {for i := y - 1; i >= start+1; i-- { res = append(res, matrix[i][start]) } }}#func spiralOrder(matrix [][]int) []int { res := make([]int, 0) rows := len(matrix)if rows == 0 {return res } cols := len(matrix[0])if cols == 0 {return res } x1, x2, y1, y2 := 0, rows-1, 0, cols-1 direct := 0for x1 <= x2 && y1 <= y2 { direct = (direct + 4) % 4if direct == 0 {for i := y1; i <= y2; i++ { res = append(res, matrix[x1][i]) } x1++ } elseif direct == 1 {for i := x1; i <= x2; i++ { res = append(res, matrix[i][y2]) } y2-- } elseif direct == 2 {for i := y2; i >= y1; i-- { res = append(res, matrix[x2][i]) } x2-- } elseif direct == 3 {for i := x2; i >= x1; i-- { res = append(res, matrix[i][y1]) } y1++ } direct++ }return res}
func generateMatrix(n int) [][]int { res := make([][]int, n)for i := 0; i < n; i++ { res[i] = make([]int, n) } count := 1 level := 1for count <= n*n { top, bottom, left, right := level-1, n-level, level-1, n-level// 左到右for i := left; i <= right && left <= right; i++ { res[top][i] = count count++ }// 上到下for i := top + 1; i <= bottom && top <= bottom; i++ { res[i][right] = count count++ }// 右到左for i := right - 1; i >= left && left <= right; i-- { res[bottom][i] = count count++ }// 下到上for i := bottom - 1; i >= top+1 && top <= bottom; i-- { res[i][left] = count count++ } level++ }return res}#func generateMatrix(n int) [][]int { res := make([][]int, n)for i := 0; i < n; i++ { res[i] = make([]int, n) } count := 1 top, bottom, left, right := 0, n-1, 0, n-1for count <= n*n {for i := left; i <= right; i++ { res[top][i] = count count++ } top++for i := top; i <= bottom; i++ { res[i][right] = count count++ } right--for i := right; i >= left; i-- { res[bottom][i] = count count++ } bottom--for i := bottom; i >= top; i-- { res[i][left] = count count++ } left++ }return res}
60.第k个排列(1)
题目
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下: "123" "132" "213" "231" "312" "321"给定 n 和 k,返回第 k 个排列。说明: 给定 n 的范围是 [1, 9]。 给定 k 的范围是[1, n!]。示例 1:输入: n = 3, k = 3 输出: "213"示例 2:输入: n = 4, k = 9 输出: "2314"
解题思路
No.
思路
时间复杂度
空间复杂度
01
遍历计算
O(n)
O(n)
func getPermutation(n int, k int) string { res := "" arr := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9"} times := make([]int, 0) times = append(times, 1) value := 1for i := 1; i <= 9; i++ { times = append(times, value*i) value = value * i } k--for n > 0 { i := k / times[n-1] k = k % times[n-1] n-- res = res + arr[i] arr = append(arr[:i], arr[i+1:]...) }return res}
61.旋转链表(2)
题目
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。示例 1:输入: 1->2->3->4->5->NULL, k = 2输出: 4->5->1->2->3->NULL解释:向右旋转 1 步: 5->1->2->3->4->NULL向右旋转 2 步: 4->5->1->2->3->NULL示例 2:输入: 0->1->2->NULL, k = 4输出: 2->0->1->NULL解释:向右旋转 1 步: 2->0->1->NULL向右旋转 2 步: 1->2->0->NULL向右旋转 3 步: 0->1->2->NULL向右旋转 4 步: 2->0->1->NULL
解题思路
No.
思路
时间复杂度
空间复杂度
01
统计遍历
O(n)
O(1)
02
数组辅助
O(n)
O(n)
func rotateRight(head *ListNode, k int) *ListNode {if head == nil || k == 0 {return head } temp := head count := 1for temp.Next != nil { temp = temp.Next count++ } temp.Next = head k = k % countfor i := 0; i < count-k; i++ { temp = temp.Next } head, temp.Next = temp.Next, nilreturn head}#func rotateRight(head *ListNode, k int) *ListNode {if head == nil || k == 0 {return head } temp := head count := 0 arr := make([]*ListNode, 0)for temp != nil { arr = append(arr, temp) temp = temp.Next count++ } k = k % countif k == 0 {return head } arr[count-1].Next = head temp = arr[count-1-k] head, temp.Next = temp.Next, nilreturn head}
62.不同路径(4)
题目
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。问总共有多少条不同的路径?例如,上图是一个7 x 3 的网格。有多少可能的路径?示例 1:输入: m = 3, n = 2 输出: 3解释:从左上角开始,总共有 3 条路径可以到达右下角。1. 向右 -> 向右 -> 向下2. 向右 -> 向下 -> 向右3. 向下 -> 向右 -> 向右示例 2:输入: m = 7, n = 3 输出: 28提示: 1 <= m, n <= 100 题目数据保证答案小于等于 2 * 10 ^ 9
解题思路
No.
思路
时间复杂度
空间复杂度
01
动态规划
O(n^2)
O(n^2)
02
动态规划
O(n^2)
O(n)
03
数学
O(n)
O(1)
04
递归
O(n^2)
O(n^2)
// dp[i][j] = dp[i-1][j] + dp[i][j-1]func uniquePaths(m int, n int) int {if m <= 0 || n <= 0 {return0 } dp := make([][]int, n)for i := 0; i < n; i++ { dp[i] = make([]int, m) dp[i][0] = 1 }for i := 0; i < m; i++ { dp[0][i] = 1 }for i := 1; i < n; i++ {for j := 1; j < m; j++ { dp[i][j] = dp[i-1][j] + dp[i][j-1] } }return dp[n-1][m-1]}#// dp[i]= dp[i-1] + dp[i]func uniquePaths(m int, n int) int {if m <= 0 || n <= 0 {return0 } dp := make([]int, n)for i := 0; i < n; i++ { dp[i] = 1 }for i := 1; i < m; i++ {for j := 1; j < n; j++ { dp[j] = dp[j] + dp[j-1] } }return dp[n-1]}# 3func uniquePaths(m int, n int) int {if m == 1 || n == 1 {return1 }if m > n { m, n = n, m } a := 1for i := 1; i <= m-1; i++ { a = a * i } b := 1for i := n; i <= m+n-2; i++ { b = b * i }return b / a}# 4var arr [][]intfunc uniquePaths(m int, n int) int { arr = make([][]int, n+1)for i := 0; i <= n; i++ { arr[i] = make([]int, m+1) }return dfs(m, n)}func dfs(m, n int) int {if m <= 0 || n <= 0 {return0 }if m == 1 || n == 1 {return1 }if arr[n][m] > 0 {return arr[n][m] } arr[n][m] = dfs(m, n-1) + dfs(m-1, n)return arr[n][m]}
63.不同路径II(3)
题目
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物和空位置分别用 1 和 0 来表示。说明:m 和 n 的值均不超过 100。示例 1:输入:[ [0,0,0], [0,1,0], [0,0,0]]输出: 2解释:3x3 网格的正中间有一个障碍物。从左上角到右下角一共有 2 条不同的路径:1. 向右 -> 向右 -> 向下 -> 向下2. 向下 -> 向下 -> 向右 -> 向右
解题思路
No.
思路
时间复杂度
空间复杂度
01
动态规划
O(n^2)
O(n^2)
02
动态规划
O(n^2)
O(n)
03
动态规划
O(n^2)
O(1)
func uniquePathsWithObstacles(obstacleGrid [][]int) int { n := len(obstacleGrid)if n < 1 {return0 } m := len(obstacleGrid[0])if m < 1 {return0 }if obstacleGrid[0][0] == 1 {return0 } dp := make([][]int, n)for i := 0; i < n; i++ { dp[i] = make([]int, m)for j := 0; j < m; j++ {if i == 0 && j == 0 { dp[i][j] = 1 } elseif i == 0 && j != 0 {if obstacleGrid[i][j] == 0 { dp[i][j] = dp[i][j-1] } } elseif i != 0 && j == 0 {if obstacleGrid[i][j] == 0 { dp[i][j] = dp[i-1][j] } } else {if obstacleGrid[i][j] == 0 { dp[i][j] = dp[i-1][j] + dp[i][j-1] } } } }return dp[n-1][m-1]}# 2// dp[j] = dp[j] + dp[j-1]func uniquePathsWithObstacles(obstacleGrid [][]int) int { n := len(obstacleGrid)if n < 1 {return0 } m := len(obstacleGrid[0])if m < 1 {return0 }if obstacleGrid[0][0] == 1 {return0 } dp := make([]int, m) dp[0] = 1for i := 0; i < n; i++ {for j := 0; j < m; j++ {if obstacleGrid[i][j] == 1 { dp[j] = 0continue }if j >= 1 && obstacleGrid[i][j-1] == 0 { dp[j] = dp[j] + dp[j-1] } } }return dp[m-1]}# 3func uniquePathsWithObstacles(obstacleGrid [][]int) int { n := len(obstacleGrid)if n < 1 {return0 } m := len(obstacleGrid[0])if m < 1 {return0 }if obstacleGrid[0][0] == 1 {return0 }for i := 0; i < n; i++ {for j := 0; j < m; j++ {if obstacleGrid[i][j] == 1 { obstacleGrid[i][j] = 0continue }if i == 0 {if j == 0 { obstacleGrid[i][j] = 1 } else { obstacleGrid[i][j] += obstacleGrid[i][j-1] } } else {if j == 0 { obstacleGrid[i][j] += obstacleGrid[i-1][j] } else { obstacleGrid[i][j] += obstacleGrid[i][j-1] + obstacleGrid[i-1][j] } } } }return obstacleGrid[n-1][m-1]}
64.最小路径和(4)
题目
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。说明:每次只能向下或者向右移动一步。示例:输入:[ [1,3,1], [1,5,1], [4,2,1]]输出: 7解释: 因为路径 1→3→1→1→1 的总和最小。
解题思路
No.
思路
时间复杂度
空间复杂度
01
动态规划
O(n^2)
O(n^2)
02
动态规划
O(n^2)
O(1)
03
动态规划
O(n^2)
O(n)
04
递归
O(n^2)
O(n^2)
func minPathSum(grid [][]int) int { n := len(grid)if n == 0 {return0 } m := len(grid[0]) dp := make([][]int, n)for i := 0; i < n; i++ { dp[i] = make([]int, m) } dp[0][0] = grid[0][0]for i := 0; i < n; i++ {for j := 0; j < m; j++ {if i == 0 && j != 0 { dp[i][j] = dp[i][j-1] + grid[i][j] } elseif i != 0 && j == 0 { dp[i][j] = dp[i-1][j] + grid[i][j] } elseif i != 0 && j != 0 { dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j] } } }return dp[n-1][m-1]}func min(a, b int) int {if a > b {return b }return a}#func minPathSum(grid [][]int) int { n := len(grid)if n == 0 {return0 } m := len(grid[0])for i := 0; i < n; i++ {for j := 0; j < m; j++ {if i == 0 && j != 0 { grid[i][j] = grid[i][j-1] + grid[i][j] } elseif i != 0 && j == 0 { grid[i][j] = grid[i-1][j] + grid[i][j] } elseif i != 0 && j != 0 { grid[i][j] = min(grid[i-1][j], grid[i][j-1]) + grid[i][j] } } }return grid[n-1][m-1]}func min(a, b int) int {if a > b {return b }return a}# 3func minPathSum(grid [][]int) int { n := len(grid)if n == 0 {return0 } m := len(grid[0]) dp := make([]int, m) dp[0] = grid[0][0]for i := 1; i < m; i++ { dp[i] = dp[i-1] + grid[0][i] }for i := 1; i < n; i++ { dp[0] = dp[0] + grid[i][0]for j := 1; j < m; j++ { dp[j] = min(dp[j-1], dp[j]) + grid[i][j] } }return dp[m-1]}func min(a, b int) int {if a > b {return b }return a}# 4var arr [][]intfunc minPathSum(grid [][]int) int { n := len(grid)if n == 0 {return0 } m := len(grid[0]) arr = make([][]int, n)for i := 0; i < n; i++ { arr[i] = make([]int, m) }return dfs(grid, n-1, m-1)}func dfs(grid [][]int, n, m int) int {if m == 0 && n == 0 { arr[0][0] = grid[0][0]return grid[0][0] }if n == 0 {return grid[0][m] + dfs(grid, 0, m-1) }if m == 0 {return grid[n][0] + dfs(grid, n-1, 0) }if arr[n][m] > 0 {return arr[n][m] } arr[n][m] = min(dfs(grid, n-1, m), dfs(grid, n, m-1)) + grid[n][m]return arr[n][m]}func min(a, b int) int {if a > b {return b }return a}
func deleteDuplicates(head *ListNode) *ListNode {if head == nil || head.Next == nil {return head } temp := &ListNode{Next: head} cur := temp value := 0for cur.Next != nil && cur.Next.Next != nil {if cur.Next.Val == cur.Next.Next.Val { value = cur.Next.Valfor cur.Next != nil && cur.Next.Val == value { cur.Next = cur.Next.Next } } else { cur = cur.Next } }return temp.Next}#func deleteDuplicates(head *ListNode) *ListNode {if head == nil || head.Next == nil {return head } flag := falsefor head.Next != nil && head.Val == head.Next.Val{ head = head.Next flag = true } head.Next = deleteDuplicates(head.Next)if flag{return head.Next }return head}#func deleteDuplicates(head *ListNode) *ListNode {if head == nil || head.Next == nil {return head } flag := falsefor head.Next != nil && head.Val == head.Next.Val{ head = head.Next flag = true } head.Next = deleteDuplicates(head.Next)if flag{return head.Next }return head}
86.分隔链表(2)
题目
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。你应当保留两个分区中每个节点的初始相对位置。示例:输入: head = 1->4->3->2->5->2, x = 3输出: 1->2->2->4->3->5
解题思路
No.
思路
时间复杂度
空间复杂度
01
双指针
O(n)
O(1)
02
数组辅助
O(n)
O(n)
func partition(head *ListNode, x int) *ListNode { first := &ListNode{} second := &ListNode{} a := first b := secondfor head != nil {if head.Val < x { a.Next = head a = head } else { b.Next = head b = head } head = head.Next } b.Next = nil a.Next = second.Nextreturn first.Next}#func partition(head *ListNode, x int) *ListNode { a := make([]*ListNode, 0) b := make([]*ListNode, 0)for head != nil {if head.Val < x { a = append(a, head) } else { b = append(b, head) } head = head.Next } temp := &ListNode{} node := tempfor i := 0; i < len(a); i++ { node.Next = a[i] node = node.Next }for i := 0; i < len(b); i++ { node.Next = b[i] node = node.Next } node.Next = nilreturn temp.Next}
func numDecodings(s string) int {if s[0] == '0' {return0 } pre := 1 cur := 1for i := 1; i < len(s); i++ { temp := curif s[i] == '0' {if s[i-1] == '1' || s[i-1] == '2' { cur = pre } else {return0 } } elseif s[i-1] == '1' || (s[i-1] == '2' && s[i] >= '1' && s[i] <= '6') { cur = cur + pre } pre = temp }return cur}# 2func numDecodings(s string) int {if s[0] == '0' {return0 } dp := make([]int, len(s)+1) dp[0] = 1for i := 0; i < len(s); i++{if s[i] == '0' {if i == 0 || s[i-1] == '1' || s[i-1] == '2' {return0 } dp[i+1] = dp[i-1] } else{if i > 0 && (s[i-1] == '2' && s[i] >= '1' && s[i] <= '6') { dp[i+1] = dp[i-1]+dp[i] }else { dp[i+1] = dp[i] } } }return dp[len(s)]}# 3var m map[string]intfunc numDecodings(s string) int { m = make(map[string]int)return dfs(s)}func dfs(s string) int {if m[s] > 0 {return m[s] }iflen(s) == 0 {return1 }if s[0] == '0' {return0 }iflen(s) == 1 {return1 }if (s[0]-'0')*10+s[1]-'0' > 26 {return dfs(s[1:]) } m[s] = dfs(s[1:]) + dfs(s[2:])return m[s]}
92.反转链表II(2)
题目
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。说明: 1 ≤ m ≤ n ≤ 链表长度。示例:输入: 1->2->3->4->5->NULL, m = 2, n = 4 输出: 1->4->3->2->5->NULL
解题思路
No.
思路
时间复杂度
空间复杂度
01
遍历
O(n)
O(1)
02
递归
O(n)
O(n)
func reverseBetween(head *ListNode, m int, n int) *ListNode {if m == n || head == nil {return head } temp := &ListNode{Next: head} prev := tempfor i := 1; i < m; i++ { prev = prev.Next } head = prev.Nextfor i := m; i < n; i++ { next := head.Next head.Next = next.Next next.Next = prev.Next prev.Next = next }return temp.Next}# 2func reverseBetween(head *ListNode, m int, n int) *ListNode {if m == 1 {return reverseN(head, n) } head.Next = reverseBetween(head.Next, m-1, n-1)return head}var next *ListNodefunc reverseN(head *ListNode, n int) *ListNode {if n == 1 { next = head.Nextreturn head } prev := reverseN(head.Next, n-1) head.Next.Next = head head.Next = nextreturn prev}
93.复原IP地址(2)
题目
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 '.' 分隔。示例:输入: "25525511135" 输出: ["255.255.11.135", "255.255.111.35"]
解题思路
No.
思路
时间复杂度
空间复杂度
01
回溯
O(1)
O(1)
02
暴力法
O(1)
O(1)
var res []stringfunc restoreIpAddresses(s string) []string { res = make([]string, 0)iflen(s) < 4 || len(s) > 12 {returnnil } dfs(s, make([]string, 0), 0)return res}func dfs(s string, arr []string, level int) {if level == 4 {iflen(s) == 0 { str := strings.Join(arr, ".") res = append(res, str) }return }for i := 1; i <= 3; i++ {if i <= len(s) { value, _ := strconv.Atoi(s[:i])if value <= 255 { str := s[i:] dfs(str, append(arr, s[:i]), level+1) }if value == 0 {// 避免出现001,01这种情况break } } }}# 2func restoreIpAddresses(s string) []string { res := make([]string, 0)iflen(s) < 4 || len(s) > 12 {returnnil }for i := 1; i <= 3 && i < len(s)-2; i++ {for j := i + 1; j <= i+3 && j < len(s)-1; j++ {for k := j + 1; k <= j+3 && k < len(s); k++ {if judge(s[:i]) && judge(s[i:j]) && judge(s[j:k]) && judge(s[k:]) { res = append(res, s[:i]+"."+s[i:j]+"."+s[j:k]+"."+s[k:]) } } } }return res}func judge(s string) bool {iflen(s) > 1 && s[0] == '0' {returnfalse } value, _ := strconv.Atoi(s)if value > 255 {returnfalse }returntrue}
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。示例:给你这个链表:1->2->3->4->5当 k = 2 时,应当返回: 2->1->4->3->5当 k = 3 时,应当返回: 3->2->1->4->5说明: 你的算法只能使用常数的额外空间。 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
解题思路
No.
思路
时间复杂度
空间复杂度
01
递归
O(n)
O(n)
02
递归
O(n)
O(n)
03
遍历
O(n)
O(1)
04
遍历
O(n)
O(1)
func reverseKGroup(head *ListNode, k int) *ListNode { length := getLength(head)if length < k || k <= 1 {return head } pre := &ListNode{} cur := headfor i := 0; i < k; i++ { temp := cur cur = cur.Next temp.Next = pre pre = temp } head.Next = reverseKGroup(cur, k)return pre}func getLength(head *ListNode) int {if head == nil {return0 } temp := head res := 0for temp != nil { res++ temp = temp.Next }return res}# 2func reverseKGroup(head *ListNode, k int) *ListNode { res := 0 temp := headfor temp != nil { res++ temp = temp.Next }if res < k || k <= 1 {return head } pre := &ListNode{} cur := headfor i := 0; i < k; i++ { next := cur.Next cur.Next = pre pre = cur cur = next } head.Next = reverseKGroup(cur, k)return pre}# 3func reverseKGroup(head *ListNode, k int) *ListNode { res := &ListNode{Next: head} prev := resfor head != nil { tail := prevfor i := 0; i < k; i++ { tail = tail.Nextif tail == nil {return res.Next } } next := tail.Next head, tail = reverse(head, tail) prev.Next = head tail.Next = next prev = tail head = tail.Next }return res.Next}func reverse(head, tail *ListNode) (*ListNode, *ListNode) { prev := tail.Next temp := headfor prev != tail { next := temp.Next temp.Next = prev prev = temp temp = next }return tail, head}# 4func reverseKGroup(head *ListNode, k int) *ListNode { res := &ListNode{Next: head} prev, end := res, resfor end.Next != nil {for i := 0; i < k && end != nil; i++ { end = end.Next }if end == nil {break } start := prev.Next // 开始的位置 next := end.Next // 结束的下一个位置 end.Next = nil// 断开尾部连接 prev.Next = reverse(start) // 反转后接到prev.Next start.Next = next // start的指针指向下一个开头(此时start已经是反转的最后一个节点) prev = start // 已经处理后的最后一个节点 end = prev // end也移动到prev }return res.Next}func reverse(head *ListNode) *ListNode {var result *ListNodefor head != nil { temp := head.Next head.Next = result result = head head = temp }return result}
30.串联所有单词的子串(2)
题目
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。示例 1:输入:s = "barfoothefoobarman",words = ["foo","bar"] 输出:[0,9]解释:从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。输出的顺序不重要, [9,0] 也是有效答案。示例 2:输入:s = "wordgoodgoodgoodbestword",words = ["word","good","best","word"]输出:[]
解题思路
No.
思路
时间复杂度
空间复杂度
01
遍历
O(n^2)
O(n)
02
滑动窗口
O(n^2)
O(n)
func findSubstring(s string, words []string) []int { res := make([]int, 0) length,n := len(s),len(words)if length == 0 || n == 0 || len(words[0]) == 0 {return res } single := len(words[0]) m := make(map[string]int)for i := 0; i < len(words); i++ { m[words[i]]++ }for i := 0; i <= length-n*single; i++ { temp := make(map[string]int)for j := 0; j < n; j++ { l := i + j*single str := s[l : l+single]if _, ok := m[str]; !ok {break } temp[str]++if temp[str] > m[str] {break }if compare(m, temp) == true { res = append(res, i)break } } }return res}func compare(m1, m2 map[string]int) bool {iflen(m1) != len(m2) {returnfalse }for k, v := range m1 {if m2[k] != v {returnfalse } }returntrue}# 2func findSubstring(s string, words []string) []int { res := make([]int, 0) length := len(s) n := len(words)if length == 0 || n == 0 || len(words[0]) == 0 {return res } single := len(words[0]) m := make(map[string]int)for i := 0; i < len(words); i++ { m[words[i]]++ }for i := 0; i < single; i++ { left, right, count := i, i, 0 temp := make(map[string]int)for right+single <= length { str := s[right : right+single] right = right + singleif m[str] > 0 { temp[str]++if temp[str] == m[str] { count++ } }if right-left == n*single {if count == len(m) { res = append(res, left) } leftStr := s[left : left+single] left = left + singleif m[leftStr] > 0 {if temp[leftStr] == m[leftStr] { count-- } temp[leftStr]-- } } } }return res}
func trap(height []int) int { res := 0for i := 0; i < len(height); i++ { left, right := 0, 0for j := i; j >= 0; j-- { left = max(left, height[j]) }for j := i; j < len(height); j++ { right = max(right, height[j]) }// 当前坐标形成的面积=(min(左边最高,右边最高)-当前高度) * 宽度(1,可省略) area := min(left, right) - height[i] res = res + area }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}# 2func trap(height []int) int { res := 0iflen(height) == 0{return0 } left := make([]int, len(height)) right := make([]int, len(height)) left[0] = height[0] right[len(right)-1] = height[len(height)-1]for i := 1; i < len(height); i++ { left[i] = max(height[i], left[i-1]) }for i := len(height) - 2; i >= 0; i-- { right[i] = max(height[i], right[i+1]) }for i := 0; i < len(height); i++ {// 当前坐标形成的面积=(min(左边最高,右边最高)-当前高度) * 宽度(1,可省略) area := min(left[i], right[i]) - height[i] res = res + area }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}# 3func trap(height []int) int { res := 0 stack := make([]int, 0)for i := 0; i < len(height); i++ {forlen(stack) > 0 && height[i] > height[stack[len(stack)-1]] { bottom := height[stack[len(stack)-1]] stack = stack[:len(stack)-1]iflen(stack) > 0 { prev := stack[len(stack)-1]// 横着的面积=长(min(height[i], height[prev])-bottom)*宽(i-prev-1) h := min(height[i], height[prev]) - bottom w := i - prev - 1 area := h * w res = res + area } } stack = append(stack, i) }return res}func min(a, b int) int {if a > b {return b }return a}# 4func trap(height []int) int { res := 0iflen(height) == 0 {return0 } left := 0 right := len(height) - 1 leftMax := 0// 左边的最大值 rightMax := 0// 右边的最大值for left < right {// 当前坐标形成的面积=(min(左边最高,右边最高)-当前高度) * 宽度(1,可省略)// 选择高度低的一边处理并求最大值, 说明当前侧最大值小于另一侧if height[left] < height[right] {// 也可以写成这样// leftMax = max(leftMax, height[left])// res = res + leftMax - height[left]if height[left] >= leftMax { // 递增无法蓄水 leftMax = height[left] } else { res = res + leftMax - height[left] } left++ } else {// 也可以写成这样// rightMax = max(rightMax, height[right])// res = res + rightMax - height[right]if height[right] >= rightMax { // 递减无法蓄水 rightMax = height[right] } else { res = res + rightMax - height[right] } right-- } }return res}
44.通配符匹配(3)
题目
给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。'?' 可以匹配任何单个字符。'*' 可以匹配任意字符串(包括空字符串)。两个字符串完全匹配才算匹配成功。说明:s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。示例 1: 输入:s = "aa" p = "a" 输出: false解释: "a" 无法匹配 "aa" 整个字符串。示例 2: 输入: s = "aa" p = "*" 输出: true解释: '*' 可以匹配任意字符串。示例 3:输入: s = "cb" p = "?a" 输出: false解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。示例 4:输入:s = "adceb" p = "*a*b" 输出: true解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".示例 5:输入:s = "acdcb" p = "a*c?b" 输出: false
解题思路
No.
思路
时间复杂度
空间复杂度
01
动态规划
O(n^2)
O(n^2)
02
递归
O(n^2)
O(n^2)
03
贪心
O(n^2)
O(1)
func isMatch(s string, p string) bool { n, m := len(s), len(p) dp := make([][]bool, n+1)for i := 0; i <= n; i++ { dp[i] = make([]bool, m+1) } dp[0][0] = truefor i := 1; i <= m; i++ {if p[i-1] == '*' { // 可以匹配任意字符串(包括空字符串) dp[0][i] = true } else {break } }for i := 1; i <= n; i++ {for j := 1; j <= m; j++ {if p[j-1] == '*' {// dp[i][j-1]=>不使用这个*,dp[i-1][j]=>使用这个* dp[i][j] = dp[i][j-1] || dp[i-1][j] } elseif p[j-1] == '?' || s[i-1] == p[j-1] { dp[i][j] = dp[i-1][j-1] } } }return dp[n][m]}# 2var dp [][]intfunc isMatch(s string, p string) bool { n, m := len(s), len(p) dp = make([][]int, n+1)for i := 0; i <= n; i++ { dp[i] = make([]int, m+1) }return dfs(s, p, 0, 0)}func dfs(s, p string, i, j int) bool {if i == len(s) && j == len(p) {returntrue }if dp[i][j] > 0 {if dp[i][j] == 1 {returnfalse } else {returntrue } }if i >= len(s) {return p[j] == '*' && dfs(s, p, i, j+1) }if j >= len(p) {returnfalse } res := falseif p[j] == '*' { res = dfs(s, p, i+1, j) || dfs(s, p, i, j+1) } else { res = (s[i] == p[j] || p[j] == '?') && dfs(s, p, i+1, j+1) }if res == true { dp[i][j] = 2 } else { dp[i][j] = 1 }return res}# 3func isMatch(s string, p string) bool { i, j := 0, 0 start, last := 0, 0for i = 0; i < len(s); {if j < len(p) && (s[i] == p[j] || p[j] == '?') { i++ j++ } elseif j < len(p) && p[j] == '*' { last = i // 记录s的位置 j++ start = j // 记录*的位置 } elseif start != 0 { last++ i = last // 更新到记录位置的下一个 j = start } else {returnfalse } }for ; j < len(p) && p[j] == '*'; j++ { }return j == len(p)}
func minDistance(word1 string, word2 string) int { n1 := len(word1) n2 := len(word2)// dp[i][j]代表 word1的i位置转换成word2的j位置需要最少步数 dp := make([][]int, n1+1)for i := 0; i < n1+1; i++ { dp[i] = make([]int, n2+1) } dp[0][0] = 0// 到word2[0]需要全部删除,有多少删除多少for i := 1; i <= n1; i++ { dp[i][0] = i }// 到word2[i]需要添加,有多少添加多少for i := 1; i <= n2; i++ { dp[0][i] = i }for i := 1; i <= n1; i++ {for j := 1; j <= n2; j++ {if word1[i-1] == word2[j-1] { dp[i][j] = dp[i-1][j-1] // 相同不需要操作 } else { dp[i][j] = dp[i-1][j-1] + 1// 替换 dp[i][j] = min(dp[i][j], dp[i][j-1]+1) // 插入 dp[i][j] = min(dp[i][j], dp[i-1][j]+1) // 删除 } } }return dp[n1][n2]}func min(a, b int) int {if a > b {return b }return a}# 2var dp [][]intfunc minDistance(word1 string, word2 string) int { dp = make([][]int, len(word1)+1)for i := 0; i < len(word1)+1; i++ { dp[i] = make([]int, len(word2)+1) }return helper(word1, word2, 0, 0)}func helper(word1, word2 string, i, j int) int {if dp[i][j] > 0 {return dp[i][j] }if i == len(word1) || j == len(word2) {returnlen(word1) - i + len(word2) - j }if word1[i] == word2[j] {return helper(word1, word2, i+1, j+1) } inserted := helper(word1, word2, i, j+1) deleted := helper(word1, word2, i+1, j) replaced := helper(word1, word2, i+1, j+1) dp[i][j] = min(inserted, min(deleted, replaced)) + 1return dp[i][j]}func min(a, b int) int {if a > b {return b }return a}
76.最小覆盖子串(2)
题目
给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。示例:输入:S = "ADOBECODEBANC", T = "ABC" 输出:"BANC"提示:如果 S 中不存这样的子串,则返回空字符串 ""。 如果 S 中存在这样的子串,我们保证它是唯一的答案。
解题思路
No.
思路
时间复杂度
空间复杂度
01
滑动窗口
O(n^2)
O(n)
02
滑动窗口
O(n)
O(n)
func minWindow(s string, t string) string {iflen(s) < len(t) {return"" } window := make(map[byte]int) need := make(map[byte]int)for i := 0; i < len(t); i++ { need[t[i]]++ } left, right := -1, -1 minLength := math.MaxInt32for l, r := 0, 0; r < len(s); r++ {if r < len(s) && need[s[r]] > 0 { window[s[r]]++ }// 找到,然后left往右移for check(need, window) == true && l <= r {if r-l+1 < minLength { minLength = r - l + 1 left, right = l, r+1 }if _, ok := need[s[l]]; ok { window[s[l]]-- } l++ } }if left == -1 {return"" }return s[left:right]}func check(need, window map[byte]int) bool {for k, v := range need {if window[k] < v {returnfalse } }returntrue}# 2func minWindow(s string, t string) string {iflen(s) < len(t) {return"" } arr := make(map[byte]int)for i := 0; i < len(t); i++ { arr[t[i]]++ } l, count := 0, 0 res := "" minLength := math.MaxInt32for r := 0; r < len(s); r++ { arr[s[r]]--if arr[s[r]] >= 0 { count++ }// left往右边移动for count == len(t) {if minLength > r-l+1 { minLength = r - l + 1 res = s[l : r+1] } arr[s[l]]++if arr[s[l]] > 0 { count-- } l++ } }return res}