leetcode热题100-GO-解-2025.3.24 23/100
今天的刷题就到这里,晚上复习下Java
238.除自身以外数组的乘积 给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 **不要使用除法,**且在 O(n)
时间复杂度内完成此题。
示例 1:
1 2 输入: nums = [1,2,3,4] 输出: [24,12,8,6]
示例 2:
1 2 输入: nums = [-1,1,0,-3,3] 输出: [0,0,9,0,0]
解一 第一思路解
分别计算前缀积和后缀积数组,再遍历相乘得出结果数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 func productExceptSelf (nums []int ) []int { if len (nums) == 2 { return []int {nums[1 ], nums[0 ]} } n := len (nums) result := make ([]int , n) prefixProduct := make ([]int , n) suffixProduct := make ([]int , n) prefixProduct[0 ] = 1 suffixProduct[n-1 ] = 1 for i := 1 ; i < n; i++ { prefixProduct[i] = prefixProduct[i-1 ] * nums[i-1 ] } for i := n - 2 ; i >= 0 ; i-- { suffixProduct[i] = suffixProduct[i+1 ] * nums[i+1 ] } for i := 0 ; i < n; i++ { result[i] = prefixProduct[i] * suffixProduct[i] } return result }
解二 依然是采用前缀积和后缀积
但是优化空间复杂度,直接用结果数组存储前缀积,再另用变量存储当前后缀积,直接乘到结果数组上
空间复杂度优化到O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func productExceptSelf2 (nums []int ) []int { if len (nums) == 2 { return []int {nums[1 ], nums[0 ]} } n := len (nums) result := make ([]int , n) result[0 ] = 1 for i := 1 ; i < n; i++ { result[i] = result[i-1 ] * nums[i-1 ] } right := 1 for i := n - 1 ; i >= 0 ; i-- { result[i] *= right right *= nums[i] } return result }
41.缺失的第一个正数 给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
示例 1:
1 2 3 输入:nums = [1,2,0] 输出:3 解释:范围 [1,2] 中的数字都在数组中。
示例 2:
1 2 3 输入:nums = [3,4,-1,1] 输出:2 解释:1 在数组中,但 2 没有。
示例 3:
1 2 3 输入:nums = [7,8,9,11,12] 输出:1 解释:最小的正数 1 没有出现。
忍不了了,直接哈希
但实际上空间复杂度不符合要求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 func firstMissingPositive (nums []int ) int { if len (nums) == 1 { if nums[0 ] == 1 { return 2 } else { return 1 } } n := len (nums) m := make (map [int ]int , n) maxN := 0 for _, v := range nums { m[v]++ if v > maxN { maxN = v } } maxN = min(maxN, n) for i := 0 ; i < maxN; i++ { if _, ok := m[i+1 ]; !ok { return i + 1 } } if maxN < 0 { return 1 } return maxN + 1 }
解二 标准流程解
依照leetcode标准题解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 func firstMissingPositive (nums []int ) int { n := len (nums) if n == 1 { if nums[0 ] == 1 { return 2 } else { return 1 } } for i := 0 ; i < n; i++ { for nums[i] > 0 && nums[i] <= n && nums[nums[i]-1 ] != nums[i] { nums[nums[i]-1 ], nums[i] = nums[i], nums[nums[i]-1 ] } } for i := 0 ; i < n; i++ { if nums[i] != i+1 { return i + 1 } } return n + 1 }
解三 解二的变式
解二中是将有效正数的值放置在应有的位置上
即将nums[i] = x
放放置在nums[x-1]
解三则为先处理所有无效数值
将所有原本的非正值和大于n的值置为n+1
即取最大的情况,缺少对应nums[n-1]
的值n
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 func firstMissingPositive (nums []int ) int { n := len (nums) for i := 0 ; i < n; i++ { if nums[i] <= 0 || nums[i] > n { nums[i] = n + 1 } } for i := 0 ; i < n; i++ { num := abs(nums[i]) if num <= n { nums[num-1 ] = -abs(nums[num-1 ]) } } for i := 0 ; i < n; i++ { if nums[i] > 0 { return i + 1 } } return n + 1 }
73.矩阵置零 给定一个*m x n
* 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用**原地 **算法。
示例 1:
1 2 输入:matrix = [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]
示例 2:
1 2 输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]] 输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
提示:
m == matrix.length
n == matrix[0].length
1 <= m, n <= 200
-231 <= matrix[i][j] <= 231 - 1
解一 空间复杂度O(m+n) 使用第一行和第一列来记录该行或该列是否有0 但是这样会导致第一行和第一列的信息被覆盖 所以需要额外的变量来记录第一行和第一列是否有0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 func setZeroes (matrix [][]int ) { m := len (matrix) n := len (matrix[0 ]) if m == 1 && n == 1 { return } row := make ([]bool , m) col := make ([]bool , n) for i := 0 ; i < m; i++ { for j := 0 ; j < n; j++ { if matrix[i][j] == 0 { row[i] = true col[j] = true } } } for i := 0 ; i < m; i++ { for j := 0 ; j < n; j++ { if row[i] || col[j] { matrix[i][j] = 0 } } } }
解二 解一的优化 空间复杂度O(1)
实际上,我们并不关心第一行和第一列具体是哪个位置有0,我们只需要关注它是否有原生的0,即第一行和第一列本身是否需要置零
使用两个变量来单独存储此信息即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 func setZeroes (matrix [][]int ) { m := len (matrix) n := len (matrix[0 ]) if m == 1 && n == 1 { return } firstRowZero := false firstColZero := false for j := 0 ; j < n; j++ { if matrix[0 ][j] == 0 { firstRowZero = true break } } for i := 0 ; i < m; i++ { if matrix[i][0 ] == 0 { firstColZero = true break } } for i := 1 ; i < m; i++ { for j := 1 ; j < n; j++ { if matrix[i][j] == 0 { matrix[i][0 ] = 0 matrix[0 ][j] = 0 } } } for i := 1 ; i < m; i++ { for j := 1 ; j < n; j++ { if matrix[i][0 ] == 0 || matrix[0 ][j] == 0 { matrix[i][j] = 0 } } } if firstRowZero { for j := 0 ; j < n; j++ { matrix[0 ][j] = 0 } } if firstColZero { for i := 0 ; i < m; i++ { matrix[i][0 ] = 0 } } }
54.螺旋矩阵 给你一个 m
行 n
列的矩阵 matrix
,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
1 2 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]
示例 2:
1 2 输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] 输出:[1,2,3,4,8,12,11,10,9,5,6,7]
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100
解法没什么好说的,直接向四方向遍历,再更新四方向边界即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 func spiralOrder (matrix [][]int ) []int { m := len (matrix) n := len (matrix[0 ]) if m == 1 && n == 1 { return []int {matrix[0 ][0 ]} } result := make ([]int , 0 , m*n) top, bottom, left, right := 0 , m-1 , 0 , n-1 for top <= bottom && left <= right { for j := left; j <= right; j++ { result = append (result, matrix[top][j]) } top++ for i := top; i <= bottom; i++ { result = append (result, matrix[i][right]) } right-- if top <= bottom { for j := right; j >= left; j-- { result = append (result, matrix[bottom][j]) } bottom-- } if left <= right { for i := bottom; i >= top; i-- { result = append (result, matrix[i][left]) } left++ } } return result }
48.旋转矩阵 给定一个 n × n 的二维矩阵 matrix
表示一个图像。请你将图像顺时针旋转 90 度。
你必须在** 原地 ** 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
1 2 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
1 2 输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]] 输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
提示:
n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000
抛开原题目约束,最方便也是最常规的方法是直接从数学上求解,直接乘个矩阵让其实现旋转90°的效果
或者对n*n
矩阵,(i,j)
元素,旋转后的位置为(j,n-1-i)
解一 可得出正确结果,但不满足题目要求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func rotateImage (matrix [][]int ) { n := len (matrix) if n == 1 { return } newMatrix := make ([][]int , n) for i := range newMatrix { newMatrix[i] = make ([]int , n) } for i := 0 ; i < n; i++ { for j := 0 ; j < n; j++ { newMatrix[j][n-1 -i] = matrix[i][j] } } for i := 0 ; i < n; i++ { for j := 0 ; j < n; j++ { matrix[i][j] = newMatrix[i][j] } } }
解二 满足题目要求的标准解
先转置,再水平翻转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func rotateImage2 (matrix [][]int ) { n := len (matrix) if n == 1 { return } for i := 0 ; i < n; i++ { for j := i + 1 ; j < n; j++ { matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] } } for i := 0 ; i < n; i++ { for j := 0 ; j < n/2 ; j++ { matrix[i][j], matrix[i][n-1 -j] = matrix[i][n-1 -j], matrix[i][j] } } }
240.搜索二维矩阵 II 编写一个高效的算法来搜索 m x n
矩阵 matrix
中的一个目标值 target
。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
1 2 输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5 输出:true
示例 2:
1 2 输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20 输出:false
提示:
m == matrix.length
n == matrix[i].length
1 <= n, m <= 300
-109 <= matrix[i][j] <= 109
每行的所有元素从左到右升序排列
每列的所有元素从上到下升序排列
-109 <= target <= 109
关键点:从哪里入手能产生区分度?
左上:向右或向下,matrix
值都变大,无法区分
右下:向左或向上,matrix
值都变小,无法区分
左下:向右变大,向上变小,可考虑
右上:向左变小,向下变大,可以区分
实质上是将其理解为类似二叉搜索树
采取从右上开始遍历搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func searchMatrix (matrix [][]int , target int ) bool { m := len (matrix) n := len (matrix[0 ]) if m == 1 && n == 1 { return matrix[0 ][0 ] == target } i, j := 0 , n-1 for i < m && j >= 0 { if matrix[i][j] == target { return true } else if matrix[i][j] < target { i++ } else { j-- } } return false }
160.相交链表 给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
图示两个链表在节点 c1
开始相交**:**
题目数据 保证 整个链式结构中不存在环。
注意 ,函数返回结果后,链表必须 保持其原始结构 。
直接考虑最佳方案 时间复杂度 O(m + n)
、仅用 O(1)
内存
采用双指针同时遍历两个链表
这两个链表分别为A+B 和B+A
假设两个链表
1 2 A = a, b, c, x, d, e B = f, g, h, i, j, k, x, d, e
则可以理解为我们在遍历
1 2 l1 = a, b, c, x, d, e, f, g, h, i, j, k, x, d, e l2 = f, g, h, i, j, k, x, d, e, a, b, c, x, d, e
在存在相交的情况下,len(A) + offsetB == len(b) + offsetA
是恒成立的
依照此逻辑,有以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func getIntersectionNode (headA, headB *ListNode) *ListNode { if headA == nil || headB == nil { return nil } pA, pB := headA, headB for pA != pB { if pA == nil { pA = headB } else { pA = pA.Next } if pB == nil { pB = headA } else { pB = pB.Next } } return pA }
206.反转链表 给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
1 2 输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
1 2 输入:head = [1,2] 输出:[2,1]
示例 3:
解一 迭代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 func reverseList (head *ListNode) *ListNode { if head == nil || head.Next == nil { return head } if head.Next.Next == nil { head.Next.Next = head newHead := head.Next head.Next = nil return newHead } prev := new (ListNode) tmp := head.Next once := true for tmp != nil { head.Next = prev if once { head.Next = nil once = false } prev = head head = tmp tmp = tmp.Next } head.Next = prev return head }
解二 递归
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func reverseList (head *ListNode) *ListNode { if head == nil || head.Next == nil { return head } newHead := reverseList(head.Next) head.Next.Next = head head.Next = nil return newHead }