This is the multi-page printable view of this section. Click here to print.
0000-0049
- 1: 0001.两数之和
- 2: 0002.两数相加
- 3: 0003.无重复字符的最长子串
- 4: 0005.最长回文子串
- 5: 0011.盛最多水的容器
- 6: 0014.最长公共前缀
- 7: 0015.三数之和
- 8: 0016.最接近的三数之和
- 9: 0017.电话号码的字母组合
- 10: 0018.四数之和
- 11: 0019.删除链表的倒数第 N 个结点
- 12: 0020.有效的括号
- 13: 0021.合并两个有序链表
- 14: 0022.括号生成
- 15: 0023.合并K个升序链表
- 16: 0024.两两交换链表中的节点
- 17: 0025.K 个一组翻转链表
- 18: 0026.删除有序数组中的重复项
- 19: 0027.移除元素
- 20: 0028.找出字符串中第一个匹配项的下标
- 21: 0029.两数相除
- 22: 0030.串联所有单词的子串
- 23: 0031.下一个排列
- 24: 0032.最长有效括号
- 25: 0033.搜索旋转排序数组
- 26: 0034.在排序数组中查找元素的第一个和最后一个位置
- 27: 0035.搜索插入位置
- 28: 0036.有效的数独
- 29: 0037.解数独
- 30: 0038.外观数列
- 31: 0039.组合总和
- 32: 0040.组合总和 II
- 33: 0041.缺失的第一个正数
- 34: 0042.接雨水
- 35: 0043.字符串相乘
- 36: 0044.通配符匹配
- 37: 0045.跳跃游戏 II
- 38: 0046.全排列
- 39: 0047.全排列 II
- 40: 0048.旋转图像
- 41: 0049.字母异位词分组
1 - 0001.两数之和
方法一:哈希表
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
impl Solution {
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
use std::collections::HashMap;
let mut map = HashMap::new();
for (i, num) in nums.iter().enumerate() {
let complement = target - num;
if map.contains_key(&complement) {
return vec![*map.get(&complement).unwrap() as i32, i as i32];
}
map.insert(num, i);
}
vec![]
}
}
2 - 0002.两数相加
方法一:模拟加法
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
impl Solution {
pub fn add_two_numbers(l1: Option<Box<ListNode>>, l2: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
let mut ans = ListNode::new(-1);
let (mut l1, mut l2) = (l1, l2);
let mut remainder = 0;
let mut p = &mut ans;
while l1.is_some() || l2.is_some() || remainder != 0 {
if let Some(a) = l1 {
l1 = a.next;
remainder += a.val;
}
if let Some(b) = l2 {
l2 = b.next;
remainder += b.val;
}
p.next = Some(Box::new(ListNode::new(remainder % 10)));
p = p.next.as_mut().unwrap().as_mut();
remainder /= 10;
}
ans.next
}
}
3 - 0003.无重复字符的最长子串
方法一:滑动窗口
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
impl Solution {
pub fn length_of_longest_substring(s: String) -> i32 {
use std::cmp::max;
use std::collections::HashSet;
let mut dict = HashSet::new();
let s = s.as_bytes();
let (mut ans, mut i) = (0, 0);
for j in 0..s.len() {
while dict.contains(&s[j]) {
dict.remove(&s[i]);
i += 1;
}
dict.insert(s[j]);
ans = max(ans, (j - i + 1) as i32);
}
ans
}
}
4 - 0005.最长回文子串
方法一:动态规划
时间复杂度 $O(n^2)$,空间复杂度 $O(n^2)$。
func longestPalindrome(s string) string {
dp, n, ans := make([][]bool, len(s)), len(s), s[:1]
for i := 0; i < n; i++ {
dp[i] = make([]bool, n)
dp[i][i] = true
}
for end := 1; end < n; end++ {
for start := 0; start < end; start++ {
if s[start] == s[end] && (end-start == 1 || dp[start+1][end-1]) {
dp[start][end] = true
if end-start+1 > len(ans) {
ans = s[start : end+1]
}
}
}
}
return ans
}
impl Solution {
pub fn longest_palindrome(s: String) -> String {
let n = s.len();
let mut dp = vec![vec![false; n]; n];
let mut ans = "".to_string();
let arrays = s.as_bytes();
for end in 0..n {
for start in 0..=end {
let (a, b) = (arrays[start], arrays[end]);
if a == b && (end - start <= 2 || dp[start + 1][end - 1]) {
dp[start][end] = true;
if end - start + 1 > ans.len() {
ans = s.get(start..end + 1).unwrap().to_string();
}
}
}
}
ans
}
}
方法二:中心扩展算法
时间复杂度 $O(n^2)$,空间复杂度 $O(1)$。
func palindrome(s string, i, j int) string {
for i >= 0 && j < len(s) {
if s[i] == s[j] {
i, j = i-1, j+1
} else {
break
}
}
return s[i+1 : j]
}
func longestPalindrome(s string) string {
n, ans := len(s), s[:1]
for i := 0; i < n; i++ {
r1, r2 := palindrome(s, i, i), palindrome(s, i, i+1)
if len(r1) > len(ans) {
ans = r1
}
if len(r2) > len(ans) {
ans = r2
}
}
return ans
}
5 - 0011.盛最多水的容器
方法一:双指针
双指针分别指向数组的首尾,每次移动较短一侧的指针,直到两个指针相遇,同时记录最大面积即可。
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
impl Solution {
pub fn max_area(height: Vec<i32>) -> i32 {
use std::cmp::{max, min};
let mut max_area = 0;
let (mut left, mut right) = (0, height.len() - 1);
while left < right {
let area = ((right - left) as i32) * min(height[left], height[right]);
max_area = max(max_area, area);
match height[left] < height[right] {
true => left += 1,
false => right -= 1,
}
}
max_area
}
}
6 - 0014.最长公共前缀
方法一:纵向扫描
时间复杂度 $O(mn)$,空间复杂度 $O(1)$。
func longestCommonPrefix(strs []string) string {
for i := 0; i < len(strs[0]); i++ {
for j := 1; j < len(strs); j++ {
if len(strs[j]) < i+1 || strs[j][i] != strs[0][i] {
return strs[0][:i]
}
}
}
return strs[0]
}
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
for i in range(len(strs[0])):
for s in strs[1:]:
if len(s) <= i or s[i] != strs[0][i]:
return s[:i]
return strs[0]
7 - 0015.三数之和
方法一:排序 + 双指针
时间复杂度 $O(n^2)$,空间复杂度 $O(1)$。
func threeSum(nums []int) [][]int {
ans, n := make([][]int, 0), len(nums)
sort.Ints(nums)
for i := 0; i < n; i++ {
for i > 0 && i < n && nums[i] == nums[i-1] {
i++
}
for j, k := i+1, n-1; j < k; {
if nums[i]+nums[j]+nums[k] == 0 {
ans = append(ans, []int{nums[i], nums[j], nums[k]})
j, k = j+1, k-1
for j < k && nums[j] == nums[j-1] {
j++
}
for j < k && nums[k+1] == nums[k] {
k--
}
} else if nums[i]+nums[j]+nums[k] > 0 {
k--
} else {
j++
}
}
}
return ans
}
impl Solution {
pub fn three_sum(nums: Vec<i32>) -> Vec<Vec<i32>> {
let mut nums = nums;
nums.sort();
let (mut ans, n) = (Vec::new(), nums.len());
for i in 0..n {
if i > 0 && nums[i - 1] == nums[i] {
continue;
}
let (mut j, mut k) = (i + 1, n - 1);
while j < k {
if nums[i] + nums[j] + nums[k] == 0 {
ans.push(vec![nums[i], nums[j], nums[k]]);
j += 1;
k -= 1;
while j < k && nums[j - 1] == nums[j] {
j += 1;
}
while j < k && nums[k + 1] == nums[k] {
k -= 1;
}
} else if nums[i] + nums[j] + nums[k] > 0 {
k -= 1;
} else {
j += 1;
}
}
}
ans
}
}
8 - 0016.最接近的三数之和
方法一:排序 + 双指针
时间复杂度 $O(n^2)$,空间复杂度 $O(1)$。
func threeSumClosest(nums []int, target int) int {
sum, n := 1<<31-1, len(nums)
sort.Ints(nums)
for i := 0; i < n; i++ {
for left, right := i+1, n-1; left < right; {
if nums[i]+nums[left]+nums[right] >= target {
if nums[i]+nums[left]+nums[right]-target < abs(sum-target) {
sum = nums[i] + nums[left] + nums[right]
}
right--
} else {
if target-(nums[i]+nums[left]+nums[right]) < abs(sum-target) {
sum = nums[i] + nums[left] + nums[right]
}
left++
}
}
}
return sum
}
func abs(x int) int {
if x < 0 {
return -1 * x
}
return x
}
9 - 0017.电话号码的字母组合
方法一:回溯
时间复杂度 $O(3^m×4^n)$,空间复杂度 $O(m+n)$。
func letterCombinations(digits string) []string {
if len(digits) == 0 {
return []string{}
}
d := []string{"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}
var dfs func(digits string) []string
dfs = func(digits string) []string {
ans := []string{}
if len(digits) == 0 {
return []string{""}
}
subLetters := dfs(digits[1:])
digit := int(digits[0] - '2')
for i := 0; i < len(d[digit]); i++ {
x := string(d[digit][i])
for _, letter := range subLetters {
ans = append(ans, x+letter)
}
}
return ans
}
return dfs(digits)
}
方法二:遍历
时间复杂度 $O(3^m×4^n)$,空间复杂度 $O(4^n)$。
func letterCombinations(digits string) []string {
if len(digits) == 0 {
return []string{}
}
ans, d := []string{""}, []string{"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}
for i := 0; i < len(digits); i++ {
digit, t := d[digits[i]-'2'], []string{}
for i := 0; i < len(digit); i++ {
for _, v := range ans {
t = append(t, v+string(digit[i]))
}
}
ans = t
}
return ans
}
10 - 0018.四数之和
方法一:排序 + 双指针
时间复杂度 $O(n^3)$,空间复杂度 $O(1)$。
func fourSum(nums []int, target int) [][]int {
ans, n := [][]int{}, len(nums)
sort.Ints(nums)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
for l, r := j+1, n-1; l < r; {
if nums[i]+nums[j]+nums[l]+nums[r] == target {
ans = append(ans, []int{nums[i], nums[j], nums[l], nums[r]})
l, r = l+1, r-1
for l < r && nums[l] == nums[l-1] {
l++
}
for l < r && nums[r] == nums[r+1] {
r--
}
} else if nums[i]+nums[j]+nums[l]+nums[r] < target {
l++
} else {
r--
}
}
for j+1 < n && nums[j+1] == nums[j] {
j++
}
}
for i+1 < n && nums[i+1] == nums[i] {
i++
}
}
return ans
}
11 - 0019.删除链表的倒数第 N 个结点
方法一:遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
dummy := &ListNode{Next: head}
pre, now := dummy, dummy
for ; n > 0; n-- {
now = now.Next
}
for now.Next != nil {
pre, now = pre.Next, now.Next
}
pre.Next = pre.Next.Next
return dummy.Next
}
12 - 0020.有效的括号
方法一:栈模拟
时间复杂度 $O(n)$,空间复杂度 $O(n)$,$n$ 表示字符串的长度。
func isValid(s string) bool {
stack, parent := []byte{}, map[byte]byte{')': '(', '}': '{', ']': '['}
for i := 0; i < len(s); i++ {
if s[i] == '(' || s[i] == '[' || s[i] == '{' {
stack = append(stack, s[i])
} else {
if len(stack) > 0 && parent[s[i]] == stack[len(stack)-1] {
stack = stack[:len(stack)-1]
} else {
return false
}
}
}
return len(stack) == 0
}
13 - 0021.合并两个有序链表
方法一:遍历
时间复杂度 $O(m+n)$,空间复杂度 $O(1)$,$m$ 和 $n$ 分别表示 $list1$ 和 $list2$ 的长度。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
dummy := &ListNode{}
p := dummy
for ; list1 != nil && list2 != nil; p = p.Next {
if list1.Val < list2.Val {
p.Next, list1 = list1, list1.Next
} else {
p.Next, list2 = list2, list2.Next
}
}
if list1 != nil {
p.Next = list1
} else {
p.Next = list2
}
return dummy.Next
}
14 - 0022.括号生成
方法一:回溯
func generateParenthesis(n int) []string {
ans := []string{}
var backtrack func(left, right int, seqence string)
backtrack = func(left, right int, seqence string) {
if left == 0 && right == 0 {
ans = append(ans, seqence)
return
}
if left > right || left < 0 || right < 0 {
return
}
backtrack(left-1, right, seqence+"(")
backtrack(left, right-1, seqence+")")
}
backtrack(n, n, "")
return ans
}
15 - 0023.合并K个升序链表
方法一:分而治之
时间复杂度 $O(kn \times \log k)$,空间复杂度 $O(\log k)$,其中 $O(k)$ 表示链表的数目,$O(n)$ 表示平均链表长度。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func mergeKLists(lists []*ListNode) *ListNode {
var merge func(left, right int) *ListNode
merge = func(left, right int) *ListNode {
if left > right {
return nil
}
if left == right {
return lists[left]
}
mid := (left + right) / 2
l := merge(left, mid)
r := merge(mid+1, right)
return mergeTwoLists(l, r)
}
return merge(0, len(lists)-1)
}
func mergeTwoLists(l1, l2 *ListNode) *ListNode {
dummy := &ListNode{}
p := dummy
for ; l1 != nil && l2 != nil; p = p.Next {
if l1.Val < l2.Val {
p.Next, l1 = l1, l1.Next
} else {
p.Next, l2 = l2, l2.Next
}
}
if l1 != nil {
p.Next = l1
} else {
p.Next = l2
}
return dummy.Next
}
16 - 0024.两两交换链表中的节点
方法一:递归
时间复杂度 $O(n)$,空间复杂度 $O(n)$,$n$表示链表的长度。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func swapPairs(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
res := swapPairs(head.Next.Next)
p := head.Next
p.Next, head.Next = head, res
return p
}
17 - 0025.K 个一组翻转链表
方法一:递归
时间复杂度 $O(n)$,空间复杂度 $O(\log _k n)$。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseKGroup(head *ListNode, k int) *ListNode {
start, end := head, head
for i := 0; i < k; i++ {
if end == nil {
return head
}
end = end.Next
}
res := reverse(start, end)
start.Next = reverseKGroup(end, k)
return res
}
func reverse(start, end *ListNode) *ListNode {
var pre *ListNode = nil
for start != end {
tmp := start.Next
start.Next, pre = pre, start
start = tmp
}
return pre
}
方法二:迭代
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseKGroup(head *ListNode, k int) *ListNode {
var dummy *ListNode = &ListNode{}
p, cur := dummy, head
for cur != nil {
start := cur
for i := 0; i < k; i++ {
if cur == nil {
p.Next = start
return dummy.Next
}
cur = cur.Next
}
p.Next, p = reverse(start, cur), start
}
return dummy.Next
}
func reverse(start, end *ListNode) *ListNode {
var pre *ListNode = nil
for start != end {
tmp := start.Next
start.Next, pre = pre, start
start = tmp
}
return pre
}
18 - 0026.删除有序数组中的重复项
方法一:双指针
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func removeDuplicates(nums []int) int {
first, last := 0, 0
for ; last < len(nums); last++ {
if nums[last] != nums[first] {
nums[first+1], first = nums[last], first+1
}
}
return first + 1
}
19 - 0027.移除元素
方法一:双指针
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func removeElement(nums []int, val int) int {
first, last := 0, 0
for ; last < len(nums); last++ {
if nums[last] != val {
nums[first], first = nums[last], first+1
}
}
return first
}
20 - 0028.找出字符串中第一个匹配项的下标
方法一:朴素解法
以字符串 haystack
的每一个字符为起点与字符串 needle
进行比较,若发现能够匹配的索引,直接返回即可。
假设字符串 haystack
长度为 n
,字符串 needle
长度为 m
,则时间复杂度为 $O((n-m) \times m)$,空间复杂度 $O(1)$。
func strStr(haystack string, needle string) int {
n, m := len(haystack), len(needle)
for i := 0; i <= n-m; i++ {
if haystack[i:i+m] == needle {
return i
}
}
return -1
}
方法二:Rabin-Karp 字符串匹配算法
Rabin-Karp 算法本质上是利用滑动窗口配合哈希函数对固定长度的字符串哈希之后进行比较,可以将比较两个字符串是否相同的时间复杂度降为 $O(1)$。
假设字符串 haystack
长度为 n
,字符串 needle
长度为 m
,则时间复杂度为 $O(n+m)$,空间复杂度 $O(1)$。
func strStr(haystack string, needle string) int {
n, m := len(haystack), len(needle)
sha, target, left, right, mod := 0, 0, 0, 0, 1<<31-1
multi := 1
for i := 0; i < m; i++ {
target = (target*256%mod + int(needle[i])) % mod
}
for i := 1; i < m; i++ {
multi = multi * 256 % mod
}
for ; right < n; right++ {
sha = (sha*256%mod + int(haystack[right])) % mod
if right-left+1 < m {
continue
}
// 此时 left~right 的长度已经为 needle 的长度 m 了,只需要比对 sha 值与 target 是否一致即可
// 为避免 hash 冲突,还需要确保 haystack[left:right+1] 与 needle 相同
if sha == target && haystack[left:right+1] == needle {
return left
}
// 未匹配成功,left 右移一位
sha = (sha - (int(haystack[left])*multi)%mod + mod) % mod
left++
}
return -1
}
21 - 0029.两数相除
方法一:快速幂
除法本质上就是减法,题目要求我们计算出两个数相除之后的取整结果,其实就是计算被除数是多少个除数加上一个小于除数的数构成的。但是一次循环只能做一次减法,效率太低会导致超时,可借助快速幂的思想进行优化。
需要注意的是,由于题目明确要求最大只能使用 32 位有符号整数,所以需要将除数和被除数同时转换为负数进行计算。因为转换正数可能会导致溢出,如当被除数为 INT32_MIN
时,转换为正数时会大于 INT32_MAX
。
时间复杂度 $O(\log dividend \times \log divisor)$,空间复杂度 $O(1)$。
func divide(a int, b int) int {
sign, ans, INT32_MAX, INT32_MIN, LIMIT := false, 0, 1<<31-1, -1<<31, -1<<31/2
if (a > 0 && b < 0) || (a < 0 && b > 0) {
sign = true
}
a, b = convert(a), convert(b)
for a <= b {
cnt := 0
// (b<<cnt) >= LIMIT 是为了避免 b<<(cnt+1) 发生溢出
for (b<<cnt) >= LIMIT && a <= (b<<(cnt+1)) {
cnt++
}
ans = ans + -1<<cnt
a = a - b<<cnt
}
if sign {
return ans
}
if ans == INT32_MIN {
return INT32_MAX
}
return -ans
}
func convert(v int) int {
if v > 0 {
return -v
}
return v
}
22 - 0030.串联所有单词的子串
方法一:滑动窗口
设字符串 s
长度为 n
,数组 words
长度为 m
,数组中每个单词长度为 w
,则时间复杂度 $O(w \times n)$,空间复杂度 $O(m \times w)$。
func findSubstring(s string, words []string) []int {
ans, exist, n, m, w := []int{}, map[string]int{}, len(s), len(words), len(words[0])
for _, w := range words {
exist[w]++
}
for i := 0; i < w; i++ {
help := map[string]int{}
for j, k := i, i+w-1; k < n; k += w {
sub := s[k-w+1 : k+1]
help[sub]++
if k-j+1 < m*w {
continue
}
cnt := 0
for key := range exist {
if exist[key] == help[key] {
cnt++
}
}
if cnt == len(exist) {
ans = append(ans, j)
}
help[s[j:j+w]]--
j += w
}
}
return ans
}
func findSubstring(s string, words []string) []int {
ans, exists, n, m, w := []int{}, map[string]int{}, len(s), len(words), len(words[0])
for _, b := range words {
exists[b]++
}
for i := 0; i < w; i++ {
cnt, help := 0, map[string]int{}
for l, r := i, i+w-1; r < n; r += w {
pre, post := s[l:l+w], s[r-w+1:r+1]
help[post]++
if exists[post] >= help[post] {
cnt++
}
if r-l+1 < m*w {
continue
}
if cnt == m {
ans = append(ans, l)
}
help[pre]--
l += w
if exists[pre] > help[pre] {
cnt--
}
}
}
return ans
}
23 - 0031.下一个排列
方法一:两边扫描
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func nextPermutation(nums []int) {
l, r := len(nums)-2, len(nums)-1
for l >= 0 && nums[l] >= nums[l+1] {
l--
}
for l >= 0 && r > l && nums[r] <= nums[l] {
r--
}
if l >= 0 && r >= 0 {
nums[l], nums[r] = nums[r], nums[l]
}
for i, j := l+1, len(nums)-1; i < j; i, j = i+1, j-1 {
nums[i], nums[j] = nums[j], nums[i]
}
}
impl Solution {
pub fn next_permutation(nums: &mut Vec<i32>) {
let (l, r) = (nums.len() - 2, nums.len() - 1);
let (mut l, mut r) = (l as i32, r as i32);
while l >= 0 && nums[l as usize] >= nums[l as usize + 1] {
l -= 1;
}
while l >= 0 && r > l && nums[r as usize] <= nums[l as usize] {
r -= 1;
}
if l >= 0 {
nums.swap(l as usize, r as usize);
}
let (mut l, mut r) = (l as usize + 1, nums.len() - 1);
while l < r {
nums.swap(l, r);
l += 1;
r -= 1;
}
}
}
24 - 0032.最长有效括号
方法一:动态规划
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
func longestValidParentheses(s string) int {
dp, ans := make([]int, len(s)), 0
if len(s) > 1 && s[0] == '(' && s[1] == ')' {
ans, dp[1] = 2, 2
}
for i := 2; i < len(s); i++ {
if s[i] == ')' && s[i-1] == '(' {
dp[i] = dp[i-2] + 2
} else if s[i] == ')' && i-dp[i-1]-1 >= 0 && s[i-dp[i-1]-1] == '(' {
dp[i] = dp[i-1] + 2
if i-dp[i-1]-2 >= 0 {
dp[i] += dp[i-dp[i-1]-2]
}
}
if ans < dp[i] {
ans = dp[i]
}
}
return ans
}
25 - 0033.搜索旋转排序数组
方法一:二分搜索
时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。
func search(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
} else if nums[mid] >= nums[left] {
if nums[left] <= target && nums[mid] > target {
right = mid - 1
} else {
left = mid + 1
}
} else {
if nums[mid] < target && nums[right] >= target {
left = mid + 1
} else {
right = mid - 1
}
}
}
return -1
}
26 - 0034.在排序数组中查找元素的第一个和最后一个位置
方法一:二分搜索
时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。
func searchRange(nums []int, target int) []int {
return []int{leftBound(nums, target), rightBound(nums, target)}
}
func leftBound(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)>>1
if nums[mid] >= target {
right = mid - 1
} else {
left = mid + 1
}
}
if left < len(nums) && nums[left] == target {
return left
}
return -1
}
func rightBound(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)>>1
if nums[mid] <= target {
left = mid + 1
} else {
right = mid - 1
}
}
if right >= 0 && nums[right] == target {
return right
}
return -1
}
27 - 0035.搜索插入位置
方法一:二分搜索
时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。
func searchInsert(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)>>1
if nums[mid] >= target {
right = mid - 1
} else {
left = mid + 1
}
}
return left
}
28 - 0036.有效的数独
方法一:遍历
因为数独大小固定为 $O(9 \times 9)$,故时间复杂度 $O(1)$,空间复杂度 $O(1)$,均为常数级。
func isValidSudoku(board [][]byte) bool {
// row, col, sub 分别表示第i行、第j列、第k个 3x3 宫内是否包含数字 1~9 中的某些数字
row, col, sub := [9][9]bool{}, [9][9]bool{}, [9][9]bool{}
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
number := int(board[i][j] - byte('1'))
if number < 0 || number >= 9 {
continue
}
k := i/3*3 + j/3
if row[i][number] || col[j][number] || sub[k][number] {
return false
}
row[i][number], col[j][number], sub[k][number] = true, true, true
}
}
return true
}
29 - 0037.解数独
方法一:回溯
时间复杂度 $O(9^{81})$,空间复杂度 $O(9^2)$。
func solveSudoku(board [][]byte) {
row, col, box, t := [9][9]bool{}, [9][9]bool{}, [9][9]bool{}, [][2]int{}
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
elem, k := int(board[i][j]-'1'), i/3*3+j/3
if board[i][j] != '.' {
row[i][elem], col[j][elem], box[k][elem] = true, true, true
} else {
t = append(t, [2]int{i, j})
}
}
}
var backtrack func(x int) bool
backtrack = func(x int) bool {
if x == len(t) {
return true
}
i, j := t[x][0], t[x][1]
for v := 1; v <= 9; v++ {
if !row[i][v-1] && !col[j][v-1] && !box[i/3*3+j/3][v-1] {
board[i][j] = byte('0' + v)
row[i][v-1], col[j][v-1], box[i/3*3+j/3][v-1] = true, true, true
if backtrack(x + 1) {
return true
}
row[i][v-1], col[j][v-1], box[i/3*3+j/3][v-1] = false, false, false
}
}
return false
}
backtrack(0)
}
func main() {
v := [][]byte{
{'5', '3', '.', '.', '7', '.', '.', '.', '.'},
{'6', '.', '.', '1', '9', '5', '.', '.', '.'},
{'.', '9', '8', '.', '.', '.', '.', '6', '.'},
{'8', '.', '.', '.', '6', '.', '.', '.', '3'},
{'4', '.', '.', '8', '.', '3', '.', '.', '1'},
{'7', '.', '.', '.', '2', '.', '.', '.', '6'},
{'.', '6', '.', '.', '.', '.', '2', '8', '.'},
{'.', '.', '.', '4', '1', '9', '.', '.', '5'},
{'.', '.', '.', '.', '8', '.', '.', '7', '9'},
}
solveSudoku(v)
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
fmt.Printf("%c ", v[i][j])
}
fmt.Println()
}
}
30 - 0038.外观数列
方法一:遍历扫描
时间复杂度 $O(n \times m)$,空间复杂度 $O(m)$,其中 m
表示返回串即第 n
个外观数列的长度。
func countAndSay(n int) string {
ans := "1"
for i := 0; i < n-1; i++ {
t := ""
for l, r := 0, 0; r < len(ans); {
for r < len(ans) && ans[l] == ans[r] {
r++
}
t += fmt.Sprintf("%d%c", r-l, ans[l])
l = r
}
ans = t
}
return ans
}
31 - 0039.组合总和
方法一:回溯
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。
func combinationSum(candidates []int, target int) [][]int {
ans, n := [][]int{}, len(candidates)
var backtrack func(start, now int, data []int)
backtrack = func(start, now int, data []int) {
if now == target {
ans = append(ans, append([]int{}, data...))
return
} else if now > target || start >= n {
return
}
for i := start; i < n; i++ {
data = append(data, candidates[i])
backtrack(i, now+candidates[i], data)
data = data[:len(data)-1]
}
}
backtrack(0, 0, []int{})
return ans
}
32 - 0040.组合总和 II
方法一:排序 + 回溯
时间复杂度 $O(2^n)$,空间复杂度 $O(n)$。
func combinationSum2(candidates []int, target int) [][]int {
sort.Ints(candidates)
ans := [][]int{}
var backtrack func(start, sum int, data []int)
backtrack = func(start, sum int, data []int) {
// base case
if sum == target {
ans = append(ans, append([]int{}, data...))
return
} else if start == len(candidates) || sum > target {
return
}
for i := start; i < len(candidates); i++ {
if i > start && candidates[i] == candidates[i-1] {
continue
}
data = append(data, candidates[i])
backtrack(i+1, sum+candidates[i], data)
data = data[:len(data)-1]
}
}
backtrack(0, 0, []int{})
return ans
}
33 - 0041.缺失的第一个正数
方法一:置换
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
对于每一个位置的数 $nums[i]$, 我们可以将其与 $nums[nums[i]-1]$ 进行交换,直到 $nums[i] = nums[nums[i]-1]$ 或者 $nums[i] \leq 0$ 或者 $nums[i] > n$。
func firstMissingPositive(nums []int) int {
n := len(nums)
for i := 0; i < n; i++ {
for nums[i] > 0 && nums[i] <= n && nums[i] != nums[nums[i]-1] {
nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]
}
}
for i := 0; i < n; i++ {
if nums[i] != i+1 {
return i + 1
}
}
return n + 1
}
impl Solution {
pub fn first_missing_positive(nums: Vec<i32>) -> i32 {
let n = nums.len() as i32;
let mut nums = nums;
for i in 0..nums.len() {
while nums[i] > 0 && nums[i] <= n && nums[i] != nums[(nums[i] - 1) as usize] {
let idx = (nums[i] - 1) as usize;
let tmp = nums[idx];
nums[idx] = nums[i];
nums[i] = tmp;
}
}
for i in 0..nums.len() {
if nums[i] != (i + 1) as i32 {
return (i + 1) as i32;
}
}
n + 1
}
}
方法二:哈希
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func firstMissingPositive(nums []int) int {
n := len(nums)
for i := 0; i < n; i++ {
if nums[i] <= 0 {
nums[i] = n + 1
}
}
for i := 0; i < n; i++ {
v := abs(nums[i])
if v <= n {
nums[v-1] = -abs(nums[v-1])
}
}
for i := 0; i < n; i++ {
if nums[i] > 0 {
return i + 1
}
}
return n + 1
}
func abs(a int) int {
if a < 0 {
return -a
}
return a
}
34 - 0042.接雨水
方法一:双指针
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func trap(height []int) int {
ans, lmax, rmax := 0, height[0], height[len(height)-1]
for i, j := 0, len(height)-1; i <= j; {
if lmax < height[i] {
lmax = height[i]
}
if rmax < height[j] {
rmax = height[j]
}
if lmax > rmax {
ans += rmax - height[j]
j--
} else {
ans += lmax - height[i]
i++
}
}
return ans
}
impl Solution {
pub fn trap(height: Vec<i32>) -> i32 {
let (mut i, mut j) = (0, height.len() - 1);
let (mut ans, mut lmax, mut rmax) = (0, height[0], height[j]);
while i < j {
lmax = std::cmp::max(lmax, height[i]);
rmax = std::cmp::max(rmax, height[j]);
if lmax > rmax {
ans += rmax - height[j];
j -= 1;
} else {
ans += lmax - height[i];
i += 1;
}
}
ans
}
}
35 - 0043.字符串相乘
方法一:模拟加法
时间复杂度 $O((m+n) \times n) = O(mn+n^2)$,空间复杂度 $O(m+n)$,其中 m
,n
分别表示 num1
,num2
的长度。
func multiply(num1, num2 string) string {
if num1 == "0" || num2 == "0" {
return "0"
}
ans := ""
for i := 0; i < len(num2); i++ {
tmp, a, mod := "", int(num2[i]-'0'), 0
for j := len(num1) - 1; j >= 0; j-- {
b := int(num1[j] - '0')
tmp = fmt.Sprintf("%d", (a*b+mod)%10) + tmp
mod = (a*b + mod) / 10
}
if mod != 0 {
tmp = fmt.Sprintf("%d", mod) + tmp
}
ans = add(ans+"0", tmp)
}
return ans
}
func add(num1, num2 string) string {
ans, mod := "", 0
for len(num1) > 0 || len(num2) > 0 || mod > 0 {
a, b := 0, 0
if len(num1) > 0 {
a, num1 = int(num1[len(num1)-1]-'0'), num1[:len(num1)-1]
}
if len(num2) > 0 {
b, num2 = int(num2[len(num2)-1]-'0'), num2[:len(num2)-1]
}
ans = fmt.Sprintf("%d", (a+b+mod)%10) + ans
mod = (a + b + mod) / 10
}
return ans
}
36 - 0044.通配符匹配
方法一:回溯 + 记忆化搜索
暴力搜索,不过可以通过数组进行优化,时间复杂度 $O(m^n)$,空间复杂度 $O(m \times n)$。
func isMatch(s string, p string) bool {
// 1表示匹配, 2表示不匹配
memo, m, n := make([][]int, len(s)+10), len(s), len(p)
for i := 0; i < m+10; i++ {
memo[i] = make([]int, n+10)
}
var backtrack func(p1, p2 int) bool
backtrack = func(p1, p2 int) bool {
if memo[p1][p2] != 0 {
return memo[p1][p2] == 1
}
if p2 >= n {
if p1 >= m {
memo[p1][p2] = 1
} else {
memo[p1][p2] = 2
}
return memo[p1][p2] == 1
}
ans := false
if p1 <= m && p[p2] == '*' {
ans = backtrack(p1, p2+1) || backtrack(p1+1, p2)
}
if p1 < m && p[p2] == '?' {
ans = backtrack(p1+1, p2+1)
} else if p1 < m && s[p1] == p[p2] {
ans = backtrack(p1+1, p2+1)
}
memo[p1][p2] = 2
if ans {
memo[p1][p2] = 1
}
return ans
}
return backtrack(0, 0)
}
方法二:动态规划
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。
func isMatch(s string, p string) bool {
dp, m, n := make([][]bool, 0), len(s), len(p)
for i := 0; i <= m; i++ {
dp = append(dp, make([]bool, n+1))
}
dp[0][0] = true
for i := 1; i <= n; i++ {
if p[i-1] == '*' {
dp[0][i] = true
} else {
break
}
}
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
if s[i-1] == p[j-1] || p[j-1] == '?' {
dp[i][j] = dp[i-1][j-1]
} else if p[j-1] == '*' {
dp[i][j] = dp[i][j-1] || dp[i-1][j]
}
}
}
return dp[m][n]
}
37 - 0045.跳跃游戏 II
方法一:动态规划
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。
func jump(nums []int) int {
dp, n := make([]int, len(nums)), len(nums)
for i := 1; i < n; i++ {
dp[i] = 10000
for j := 0; j < i; j++ {
if i-j <= nums[j] {
if dp[i] > dp[j]+1 {
dp[i] = dp[j] + 1
}
}
}
}
return dp[n-1]
}
方法二:贪心算法
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func jump(nums []int) int {
maxDistence, step, end := 0, 0, 0
for i := 0; i < len(nums)-1; i++ {
if i+nums[i] > maxDistence {
maxDistence = i + nums[i]
}
if i == end {
step, end = step+1, maxDistence
}
}
return step
}
38 - 0046.全排列
方法一:回溯
时间复杂度 $O(n \times n!)$,空间复杂度 $O(n)$。
func permute(nums []int) [][]int {
ans, used, n := make([][]int, 0), make([]bool, len(nums)), len(nums)
var backtrack func(vals []int)
backtrack = func(vals []int) {
if len(vals) == n {
ans = append(ans, append([]int{}, vals...))
return
}
for i := 0; i < n; i++ {
if used[i] {
continue
}
vals, used[i] = append(vals, nums[i]), true
backtrack(vals)
vals, used[i] = vals[:len(vals)-1], false
}
}
backtrack([]int{})
return ans
}
39 - 0047.全排列 II
方法一:回溯
时间复杂度 $O(n \times n!)$,空间复杂度 $O(n)$。
func permuteUnique(nums []int) [][]int {
ans, n := [][]int{}, len(nums)
sort.Ints(nums)
var backtrack func(vals []int, used []bool)
backtrack = func(vals []int, used []bool) {
if len(vals) == n {
ans = append(ans, append([]int{}, vals...))
return
}
i := 0
for ; i < n; i++ {
if !used[i] {
break
}
}
for start := i; i < n; i++ {
if used[i] || (i != start && nums[i] == nums[start]) {
continue
}
start = i
vals, used[i] = append(vals, nums[i]), true
backtrack(vals, used)
vals, used[i] = vals[:len(vals)-1], false
}
}
backtrack([]int{}, make([]bool, n))
return ans
}
40 - 0048.旋转图像
方法一:遍历
时间复杂度 $O(n^2)$,空间复杂度 $O(1)$。
func rotate(matrix [][]int) {
n := len(matrix)
for i := 0; i < n; i++ {
for j := 0; j < i; j++ {
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
}
}
for i := 0; i < n; i++ {
for j, k := 0, n-1; j < k; j, k = j+1, k-1 {
matrix[i][j], matrix[i][k] = matrix[i][k], matrix[i][j]
}
}
}
impl Solution {
pub fn rotate(matrix: &mut Vec<Vec<i32>>) {
let n = matrix.len();
for i in 0..n {
for j in i..n {
let tmp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = tmp;
}
}
for i in 0..n {
let (mut j, mut k) = (0, n - 1);
while j < k {
matrix[i].swap(j, k);
j += 1;
k -= 1;
}
}
}
}
41 - 0049.字母异位词分组
https://leetcode.cn/problems/group-anagrams/
这道题目的主要是希望把传入的字符串数组按照字母异位词分组,所谓字母异位词,就是两个字符串中的字母出现的次数都是一样的,只是顺序不一样。
方法一:暴力搜索
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。
func groupAnagrams(strs []string) [][]string {
ans, used, n := [][]string{}, make([]bool, len(strs)), len(strs)
for i := 0; i < n; i++ {
if used[i] {
continue
}
tmp := []string{strs[i]}
used[i] = true
for j := i + 1; j < n; j++ {
if isAnagrams(strs[j], strs[i]) {
tmp, used[j] = append(tmp, strs[j]), true
}
}
ans = append(ans, tmp)
}
return ans
}
func isAnagrams(now, target string) bool {
if len(now) != len(target) {
return false
}
nc, tc := [26]int{}, [26]int{}
for i := 0; i < len(now); i++ {
nc[int(now[i]-'a')] += 1
tc[int(target[i]-'a')] += 1
}
for i := 0; i < 26; i++ {
if nc[i] != tc[i] {
return false
}
}
return true
}
impl Solution {
pub fn group_anagrams(strs: Vec<String>) -> Vec<Vec<String>> {
fn is_anagrams(a: &str, b: &str) -> bool {
if a.len() != b.len() {
return false;
}
let a = a.as_bytes();
let b = b.as_bytes();
let (mut ac, mut bc) = (vec![0; 26], vec![0; 26]);
for i in 0..a.len() {
let ac_idx = (a[i] - ('a' as u8)) as usize;
ac[ac_idx] += 1;
let bc_idx = (b[i] - ('a' as u8)) as usize;
bc[bc_idx] += 1;
}
for i in 0..26 {
if ac[i] != bc[i] {
return false;
}
}
true
}
let (mut ans, mut used, n) = (vec![], vec![false; strs.len()], strs.len());
for i in 0..n {
if used[i] {
continue;
}
used[i] = true;
let mut tmp = vec![strs[i].clone()];
for j in i + 1..n {
if is_anagrams(&strs[i], &strs[j]) {
tmp.push(strs[j].clone());
used[j] = true;
}
}
ans.push(tmp);
}
ans
}
}
方法二:排序 + 哈希
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
func groupAnagrams(strs []string) [][]string {
ans, help := [][]string{}, make(map[string][]string)
for _, str := range strs {
s := []byte(str)
sort.Slice(s, func(i, j int) bool { return s[i] < s[j] })
help[string(s)] = append(help[string(s)], str)
}
for _, v := range help {
ans = append(ans, v)
}
return ans
}
impl Solution {
pub fn group_anagrams(strs: Vec<String>) -> Vec<Vec<String>> {
use std::collections::HashMap;
let mut map: HashMap<Vec<u8>, Vec<String>> = HashMap::new();
for s in strs {
let mut v = s.as_bytes().to_vec();
v.sort();
map.entry(v).or_insert(Vec::new()).push(s);
}
map.into_iter().map(|(_, v)| v).collect()
}
}