This is the multi-page printable view of this section. Click here to print.
算法
- 1: 0000-0049
- 1.1: 0001.两数之和
- 1.2: 0002.两数相加
- 1.3: 0003.无重复字符的最长子串
- 1.4: 0005.最长回文子串
- 1.5: 0011.盛最多水的容器
- 1.6: 0014.最长公共前缀
- 1.7: 0015.三数之和
- 1.8: 0016.最接近的三数之和
- 1.9: 0017.电话号码的字母组合
- 1.10: 0018.四数之和
- 1.11: 0019.删除链表的倒数第 N 个结点
- 1.12: 0020.有效的括号
- 1.13: 0021.合并两个有序链表
- 1.14: 0022.括号生成
- 1.15: 0023.合并K个升序链表
- 1.16: 0024.两两交换链表中的节点
- 1.17: 0025.K 个一组翻转链表
- 1.18: 0026.删除有序数组中的重复项
- 1.19: 0027.移除元素
- 1.20: 0028.找出字符串中第一个匹配项的下标
- 1.21: 0029.两数相除
- 1.22: 0030.串联所有单词的子串
- 1.23: 0031.下一个排列
- 1.24: 0032.最长有效括号
- 1.25: 0033.搜索旋转排序数组
- 1.26: 0034.在排序数组中查找元素的第一个和最后一个位置
- 1.27: 0035.搜索插入位置
- 1.28: 0036.有效的数独
- 1.29: 0037.解数独
- 1.30: 0038.外观数列
- 1.31: 0039.组合总和
- 1.32: 0040.组合总和 II
- 1.33: 0041.缺失的第一个正数
- 1.34: 0042.接雨水
- 1.35: 0043.字符串相乘
- 1.36: 0044.通配符匹配
- 1.37: 0045.跳跃游戏 II
- 1.38: 0046.全排列
- 1.39: 0047.全排列 II
- 1.40: 0048.旋转图像
- 1.41: 0049.字母异位词分组
- 2: 0050-0099
- 2.1: 0050.Pow(x, n)
- 2.2: 0051.N 皇后
- 2.3: 0052.N皇后 II
- 2.4: 0053.最大子数组和
- 2.5: 0054.螺旋矩阵
- 2.6: 0055.跳跃游戏
- 2.7: 0056.合并区间
- 2.8: 0057.插入区间
- 2.9: 0058.最后一个单词的长度
- 2.10: 0059.螺旋矩阵 II
- 2.11: 0060.排列序列
- 2.12: 0061.旋转链表
- 2.13: 0062.不同路径
- 2.14: 0063.不同路径 II
- 2.15: 0064.最小路径和
- 2.16: 0065.有效数字
- 2.17: 0066.加一
- 2.18: 0067.二进制求和
- 2.19: 0068.文本左右对齐
- 2.20: 0069.x 的平方根
- 2.21: 0070.爬楼梯
- 2.22: 0071.简化路径
- 2.23: 0072.编辑距离
- 2.24: 0073.矩阵置零
- 2.25: 0074.搜索二维矩阵
- 2.26: 0075.颜色分类
- 2.27: 0076.最小覆盖子串
- 2.28: 0077.组合
- 2.29: 0078.子集
- 2.30: 0079.单词搜索
- 2.31: 0080.删除有序数组中的重复项 II
- 2.32: 0081.搜索旋转排序数组 II
- 2.33: 0082.删除排序链表中的重复元素 II
- 2.34: 0083.删除排序链表中的重复元素
- 2.35: 0084.柱状图中最大的矩形
- 2.36: 0085.最大矩形
- 2.37: 0086.分隔链表
- 2.38: 0087.扰乱字符串
- 2.39: 0088.合并两个有序数组
- 2.40: 0089.格雷编码
- 2.41: 0090.子集 II
- 2.42: 0091.解码方法
- 2.43: 0092.反转链表 II
- 2.44: 0093.复原 IP 地址
- 2.45: 0094.二叉树的中序遍历
- 2.46: 0095.不同的二叉搜索树 II
- 2.47: 0096.不同的二叉搜索树
- 2.48: 0097.交错字符串
- 2.49: 0098.验证二叉搜索树
- 2.50: 0099.恢复二叉搜索树
- 3: 0100-0149
- 3.1: 0128.最长连续序列
- 3.2: 0129.求根节点到叶节点数字之和
- 3.3: 0132.分割回文串 II
- 4: 0150-0199
- 4.1: 0187.重复的DNA序列
- 4.2: 0189.轮转数组
- 5: 0200-0249
- 5.1: 0206.反转链表
- 5.2: 0234.回文链表
- 5.3: 0238.除自身以外数组的乘积
- 5.4: 0239.滑动窗口最大值
- 5.5: 0240.搜索二维矩阵 II
- 6: 0250-0299
- 6.1: 0283.移动零
- 6.2: 0287.寻找重复数
- 7: 0400-0449
- 7.1: 0438.找到字符串中所有字母异位词
- 8: 0450-0499
- 8.1: 0454.四数相加 II
- 9: 0550-0599
- 9.1: 0560.和为 K 的子数组
- 10: 0750-0999
- 10.1: 0792.匹配子序列的单词数
- 10.2: 0813.最大平均值和的分组
- 10.3: 0895.最大频率栈
- 11: 1000-1499
- 12: 1500-1999
- 12.1: 1758.生成交替二进制字符串的最少操作数
- 12.2: 1774.最接近目标价格的甜点成本
- 12.3: 1779.找到最近的有相同 X 或 Y 坐标的点
- 12.4: 1796.字符串中第二大的数字
- 13: 2000-2499
1 - 0000-0049
1.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![]
}
}
1.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
}
}
1.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
}
}
1.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
}
1.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
}
}
1.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]
1.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
}
}
1.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
}
1.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
}
1.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
}
1.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
}
1.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
}
1.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
}
1.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
}
1.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
}
1.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
}
1.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
}
1.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
}
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
}
1.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
}
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
}
1.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
}
1.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;
}
}
}
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
}
1.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
}
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
}
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
}
1.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
}
1.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()
}
}
1.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
}
1.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
}
1.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
}
1.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
}
1.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
}
}
1.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
}
1.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]
}
1.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
}
1.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
}
1.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
}
1.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;
}
}
}
}
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()
}
}
2 - 0050-0099
2.1 - 0050.Pow(x, n)
方法一:递归
时间复杂度 $O(\log n)$,空间复杂度 $O(\log n)$。
func myPow(x float64, n int) float64 {
if n == 0 {
return 1.0
} else if n < 0 {
return myPow(1/x, -n)
}
ans := myPow(x, n>>1)
if n&1 == 1 {
return x * ans * ans
}
return ans * ans
}
方法二:快速乘
时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。
func myPow(x float64, n int) float64 {
if n < 0 {
x, n = 1/x, -n
}
ans, c := 1.0, x
for n != 0 {
if n&1 == 1 {
ans *= c
}
c *= c
n >>= 1
}
return ans
}
2.2 - 0051.N 皇后
方法一:回溯
时间复杂度 $O(n^3)$,空间复杂度 $O(n)$。
func hasConflict(vals []string, x, y int) bool {
n := len(vals)
// 上方
for i := x - 1; i >= 0; i-- {
if vals[i][y] == 'Q' {
return false
}
}
// 右上方
for i, j := x-1, y+1; i >= 0 && j < n; i, j = i-1, j+1 {
if vals[i][j] == 'Q' {
return false
}
}
// 左上方
for i, j := x-1, y-1; i >= 0 && j >= 0; i, j = i-1, j-1 {
if vals[i][j] == 'Q' {
return false
}
}
return true
}
func solveNQueens(n int) [][]string {
ans, vals := [][]string{}, make([]string, n)
for i := 0; i < n; i++ {
vals[i] = strings.Repeat(".", n)
}
var backtrack func(row int)
backtrack = func(row int) {
if row == n {
ans = append(ans, append([]string{}, vals...))
return
}
for i := 0; i < n; i++ {
if hasConflict(vals, row, i) {
tmp := []byte(vals[row])
tmp[i] = 'Q'
vals[row] = string(tmp)
backtrack(row + 1)
tmp[i] = '.'
vals[row] = string(tmp)
}
}
}
backtrack(0)
return ans
}
2.3 - 0052.N皇后 II
方法一:回溯
时间复杂度 $O(n^3)$,空间复杂度 $O(n^2)$。
func totalNQueens(n int) int {
ans, vals := 0, make([][]byte, n)
for i := 0; i < n; i++ {
vals[i] = make([]byte, n)
for j := 0; j < n; j++ {
vals[i][j] = '.'
}
}
var backtrack func(row int)
backtrack = func(row int) {
if row == n {
ans++
return
}
for i := 0; i < n; i++ {
if isValid(vals, row, i) {
vals[row][i] = 'Q'
backtrack(row + 1)
vals[row][i] = '.'
}
}
}
backtrack(0)
return ans
}
func isValid(vals [][]byte, x, y int) bool {
n := len(vals)
for i := x - 1; i >= 0; i-- {
if vals[i][y] == 'Q' {
return false
}
}
for i, j := x-1, y+1; i >= 0 && j < n; i, j = i-1, j+1 {
if vals[i][j] == 'Q' {
return false
}
}
for i, j := x-1, y-1; i >= 0 && j >= 0; i, j = i-1, j-1 {
if vals[i][j] == 'Q' {
return false
}
}
return true
}
2.4 - 0053.最大子数组和
方法一:遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func maxSubArray(nums []int) int {
ans := nums[0]
for sum, i := 0, 0; i < len(nums); i++ {
sum += nums[i]
if sum > ans {
ans = sum
}
if sum < 0 {
sum = 0
}
}
return ans
}
impl Solution {
pub fn max_sub_array(nums: Vec<i32>) -> i32 {
let mut ans = nums[0];
let mut sum = 0;
for v in nums {
sum += v;
if sum > ans {
ans = sum;
}
if sum < 0 {
sum = 0;
}
}
ans
}
}
2.5 - 0054.螺旋矩阵
方法一:按层模拟
时间复杂度 $O(m \times n)$,空间复杂度 $O(1)$,m
和 n
表示输入矩阵的行数和列数。
func spiralOrder(matrix [][]int) []int {
ans := []int{}
top, bottom, left, right := 0, len(matrix)-1, 0, len(matrix[0])-1
for top <= bottom && left <= right {
for i := left; i <= right; i++ {
ans = append(ans, matrix[top][i])
}
top += 1
for i := top; i <= bottom; i++ {
ans = append(ans, matrix[i][right])
}
right -= 1
if top > bottom || left > right {
break
}
for i := right; i >= left; i-- {
ans = append(ans, matrix[bottom][i])
}
bottom -= 1
for i := bottom; i >= top; i-- {
ans = append(ans, matrix[i][left])
}
left += 1
}
return ans
}
impl Solution {
pub fn spiral_order(matrix: Vec<Vec<i32>>) -> Vec<i32> {
let (mut i, mut j, mut m, mut n) = (0, 0, matrix.len(), matrix[0].len());
let mut ans = Vec::new();
while i < m && j < n {
for y in j..n {
ans.push(matrix[i][y]);
}
i += 1;
for x in i..m {
ans.push(matrix[x][n - 1]);
}
n -= 1;
if i >= m || j >= n {
break;
}
for y in (j..n).rev() {
ans.push(matrix[m - 1][y]);
}
m -= 1;
for x in (i..m).rev() {
ans.push(matrix[x][j]);
}
j += 1;
}
ans
}
}
2.6 - 0055.跳跃游戏
方法一:贪心
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func canJump(nums []int) bool {
maxStep, end := 0, 0
for i := 0; i < len(nums)-1; i++ {
if i+nums[i] > maxStep {
maxStep = i + nums[i]
}
if end == i && maxStep > end {
end = maxStep
} else if end == i && maxStep <= end {
return false
}
}
return true
}
2.7 - 0056.合并区间
方法一:排序 + 遍历
时间复杂度 $O(n \times \log n)$,空间复杂度 $O(\log n)$。
func merge(intervals [][]int) [][]int {
sort.Slice(intervals, func(i, j int) bool { return intervals[i][0] < intervals[j][0] })
ans := [][]int{intervals[0]}
for i := 1; i < len(intervals); i++ {
x, y := intervals[i][0], intervals[i][1]
if x > ans[len(ans)-1][1] {
ans = append(ans, []int{x, y})
} else if y > ans[len(ans)-1][1] {
ans[len(ans)-1][1] = y
}
}
return ans
}
impl Solution {
pub fn merge(intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
let mut intervals = intervals;
intervals.sort_by(|a, b| a[0].cmp(&b[0]));
let mut ans = Vec::new();
for v in intervals {
if ans.is_empty() {
ans.push(v);
continue;
}
let last = ans.last_mut().unwrap();
if v[0] > last[1] {
ans.push(v);
} else if v[1] > last[1] {
last[1] = v[1];
}
}
ans
}
}
2.8 - 0057.插入区间
方法一:模拟
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func insert(intervals [][]int, newInterval []int) [][]int {
ans, first := [][]int{}, false
for _, interval := range intervals {
if interval[1] < newInterval[0] {
ans = append(ans, interval)
} else if interval[0] > newInterval[1] {
if !first {
ans, first = append(ans, newInterval), true
}
ans = append(ans, interval)
} else {
if interval[0] < newInterval[0] {
newInterval[0] = interval[0]
}
if interval[1] > newInterval[1] {
newInterval[1] = interval[1]
}
}
}
if !first {
ans = append(ans, newInterval)
}
return ans
}
2.9 - 0058.最后一个单词的长度
方法一:扫描
时间复杂度 $O(n)$,空间复杂度 $O(1)$,n
表示字符串的长度。
func lengthOfLastWord(s string) int {
start, end := len(s)-1, len(s)-1
for s[start] == ' ' {
start, end = start-1, end-1
}
for start >= 0 && s[start] != ' ' {
start--
}
return end - start
}
2.10 - 0059.螺旋矩阵 II
方法一:模拟
时间复杂度 $O(n^2)$,空间复杂度 $O(1)$。
func generateMatrix(n int) [][]int {
ans := make([][]int, n)
for i := 0; i < n; i++ {
ans[i] = make([]int, n)
}
top, bottom, left, right, v := 0, n-1, 0, n-1, 1
for top <= bottom && left <= right {
for i := left; i <= right; i++ {
ans[top][i], v = v, v+1
}
top += 1
for i := top; i <= bottom; i++ {
ans[i][right], v = v, v+1
}
right -= 1
if top > bottom || left > right {
break
}
for i := right; i >= left; i-- {
ans[bottom][i], v = v, v+1
}
bottom -= 1
for i := bottom; i >= top; i-- {
ans[i][left], v = v, v+1
}
left += 1
}
return ans
}
2.11 - 0060.排列序列
方法一:模拟
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。
func getPermutation(n int, k int) string {
ans, visable := "", make([]bool, n+1)
for i := 0; i < n; i++ {
factor := 1
for j := 1; j < n-i; j++ {
factor *= j
}
for j := 1; j <= n; j++ {
if !visable[j] {
if k > factor {
k -= factor
} else {
ans += fmt.Sprintf("%c", '0'+j)
visable[j] = true
break
}
}
}
}
return ans
}
2.12 - 0061.旋转链表
方法一:快慢指针
时间复杂度 $O(n)$,空间复杂度 $O(1)$,n
表示链表的长度。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func rotateRight(head *ListNode, k int) *ListNode {
if head == nil || head.Next == nil {
return head
}
n, p, q, ans := 0, head, head, head
for ; p != nil; p = p.Next {
n++
}
k, p = k%n, head
for i := 0; i < k; i++ {
p = p.Next
}
for p != nil && p.Next != nil {
p = p.Next
q = q.Next
}
if k != 0 {
ans = q.Next
p.Next, q.Next = head, p.Next
}
return ans
}
2.13 - 0062.不同路径
方法一:动态规划
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。
func uniquePaths(m int, n int) int {
dp := make([][]int, m)
for i := 0; i < m; i++ {
dp[i] = make([]int, n)
dp[i][0] = 1
}
for i := 0; i < n; i++ {
dp[0][i] = 1
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
dp[i][j] = dp[i-1][j] + dp[i][j-1]
}
}
return dp[m-1][n-1]
}
方法二 :数学
易知解法数目为:$C_{m+n-2}^{m-1} = \frac{(m+n-2)(m+n-3)···n}{(m-1)!}$,故时间复杂度 $O(m)$,空间复杂度 $O(1)$。
func uniquePaths(m int, n int) int {
ans := 1
for i, v := 1, n; i < m; i, v = i+1, v+1 {
ans = ans * v / i
}
return ans
}
2.14 - 0063.不同路径 II
方法一:动态规划
时间复杂度 $O(m \times n)$,空间复杂度 $O(n)$,其中 m
和 n
分别表示 obstacleGrid
数组的行数和列数。
func uniquePathsWithObstacles(obstacleGrid [][]int) int {
dp, m, n := make([]int, len(obstacleGrid[0])), len(obstacleGrid), len(obstacleGrid[0])
for i := 0; i < n && obstacleGrid[0][i] != 1; i++ {
dp[i] = 1
}
for i := 1; i < m; i++ {
for j := 0; j < n; j++ {
if obstacleGrid[i][j] == 0 {
if j > 0 {
dp[j] += dp[j-1]
}
} else {
dp[j] = 0
}
}
}
return dp[n-1]
}
2.15 - 0064.最小路径和
方法一:动态规划
时间复杂度 $O(m \times n)$,空间复杂度 $O(n)$,其中 m
和 n
分别表示 grid
数组的行数和列数。
func min(a, b int) int {
if a < b {
return a
}
return b
}
func minPathSum(grid [][]int) int {
dp, m, n := make([]int, len(grid[0])), len(grid), len(grid[0])
dp[0] = grid[0][0]
for i := 1; i < n; i++ {
dp[i] = grid[0][i] + dp[i-1]
}
for i := 1; i < m; i++ {
for j := 0; j < n; j++ {
if j > 0 {
dp[j] = min(dp[j], dp[j-1]) + grid[i][j]
} else {
dp[j] += grid[i][j]
}
}
}
return dp[n-1]
}
2.16 - 0065.有效数字
方法一:分类讨论
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func isNumber(s string) bool {
if len(s) > 0 && (s[0] == '+' || s[0] == '-') {
s = s[1:]
}
if len(s) == 0 {
return false
}
point := false
for i := 0; i < len(s); i++ {
if s[i] == '.' && !point {
if (i > 0 && s[i-1] >= '0' && s[i-1] <= '9') || (i+1 < len(s) && s[i+1] >= '0' && s[i+1] <= '9') {
point = true
continue
}
return false
} else if strings.ToUpper(s[i:i+1]) == "E" && i+1 < len(s) && i > 0 {
for j := i + 1; j < len(s); j++ {
if s[j] == '.' || strings.ToUpper(s[j:j+1]) == "E" {
return false
}
}
return isNumber(s[i+1:])
} else if s[i] < '0' || s[i] > '9' {
return false
}
}
return true
}
2.17 - 0066.加一
方法一:找出最长的后缀 9
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func plusOne(digits []int) []int {
for i := len(digits) - 1; i >= 0; i-- {
if digits[i] != 9 {
digits[i] += 1
for j := i + 1; j < len(digits); j++ {
digits[j] = 0
}
return digits
}
}
ans := make([]int, len(digits)+1)
ans[0] = 1
return ans
}
2.18 - 0067.二进制求和
方法一:模拟加法
时间复杂度 $O(max(len(a), len(b)))$,空间复杂度 $O(1)$。
func addBinary(a string, b string) string {
ans, pa, pb := "", len(a)-1, len(b)-1
for mod := 0; pa >= 0 || pb >= 0 || mod != 0; {
x, y := 0, 0
if pa >= 0 {
x, pa = int(a[pa]-'0'), pa-1
}
if pb >= 0 {
y, pb = int(b[pb]-'0'), pb-1
}
ans = fmt.Sprintf("%d", (x+y+mod)%2) + ans
mod = (x + y + mod) / 2
}
return ans
}
2.19 - 0068.文本左右对齐
方法一:字符串模拟
时间复杂度 $O(m)$,空间复杂度 $O(1)$,其中 m
表示所有字符串的总长度。
// 放置以 index 结尾的 num 个单词,确保字符串长度为 maxWidth
func placestr(words []string, index, num, length, maxWidth int) string {
if num == 1 {
return words[index] + strings.Repeat(" ", maxWidth-length)
}
space, mod, substr := (maxWidth-length)/(num-1), (maxWidth-length)%(num-1), ""
if index == len(words)-1 {
space, mod = 1, maxWidth-length-num+1
}
for i := index - num + 1; i < index; i++ {
numspace := space
if mod > 0 && index != len(words)-1 {
numspace, mod = numspace+1, mod-1
}
substr += words[i] + strings.Repeat(" ", numspace)
}
substr += words[index] + strings.Repeat(" ", mod)
return substr
}
func fullJustify(words []string, maxWidth int) []string {
ans, length, num := []string{}, 0, 0
for i := 0; i < len(words); i++ {
if length+len(words[i])+num <= maxWidth {
length, num = length+len(words[i]), num+1
continue
}
ans = append(ans, placestr(words, i-1, num, length, maxWidth))
length, num = len(words[i]), 1
}
ans = append(ans, placestr(words, len(words)-1, num, length, maxWidth))
return ans
}
2.20 - 0069.x 的平方根
方法一:二分查找
时间复杂度 $O(\log x)$,空间复杂度 $O(1)$。
func mySqrt(x int) int {
a, b, ans := 0, x, 0
for m := a + (b-a)/2; a <= b; m = a + (b-a)/2 {
if m*m <= x {
ans, a = m, m+1
} else {
b = m - 1
}
}
return ans
}
2.21 - 0070.爬楼梯
方法一:动态规划
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func climbStairs(n int) int {
if n < 3 {
return n
}
first, second, three := 1, 2, 0
for i := 3; i <= n; i++ {
three = first + second
first, second = second, three
}
return three
}
2.22 - 0071.简化路径
方法一:模拟
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
func simplifyPath(path string) string {
ans, start, end := []string{}, 0, 0
for i := 0; i < len(path); i = end + 1 {
for start = i; start < len(path) && path[start] == '/'; {
start++
}
for end = start; end < len(path) && path[end] != '/'; {
end++
}
subpath := path[start:end]
if subpath == ".." && len(ans) > 0 {
ans = ans[:len(ans)-1]
} else if subpath == "." || subpath == ".." {
continue
} else if subpath != "" {
ans = append(ans, subpath)
}
}
return "/" + strings.Join(ans, "/")
}
2.23 - 0072.编辑距离
方法一:动态规划
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。
func min(a, b int) int {
if a < b {
return a
}
return b
}
func minDistance(word1 string, word2 string) int {
dp, m, n := make([][]int, len(word1)+1), len(word1), len(word2)
for i := 0; i <= m; i++ {
dp[i] = make([]int, n+1)
}
for i := 1; i <= m; i++ {
dp[i][0] = i
}
for i := 1; i <= n; i++ {
dp[0][i] = i
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if word1[i] == word2[j] {
dp[i+1][j+1] = dp[i][j]
} else {
dp[i+1][j+1] = min(dp[i][j+1], min(dp[i][j], dp[i+1][j])) + 1
}
}
}
return dp[m][n]
}
2.24 - 0073.矩阵置零
方法一:使用两个标记变量
时间复杂度 $O(m \times n)$,空间复杂度 $O(1)$,m
,n
分别表示 matrix
的行数和列数。
func setZeroes(matrix [][]int) {
m, n, zero_r, zero_c := len(matrix), len(matrix[0]), false, false
for i := 0; i < m; i++ {
if matrix[i][0] == 0 {
zero_r = true
break
}
}
for i := 0; i < n; i++ {
if matrix[0][i] == 0 {
zero_c = true
break
}
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
if matrix[i][j] == 0 {
matrix[i][0], matrix[0][j] = 0, 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
}
}
}
for i := 0; i < m && zero_r; i++ {
matrix[i][0] = 0
}
for i := 0; i < n && zero_c; i++ {
matrix[0][i] = 0
}
}
impl Solution {
pub fn set_zeroes(matrix: &mut Vec<Vec<i32>>) {
use std::cmp::{max, min};
let (m, n, mut zero_r, mut zero_c) = (matrix.len(), matrix[0].len(), false, false);
for i in 0..max(m, n) {
if matrix[min(i, m - 1)][0] == 0 {
zero_r = true;
}
if matrix[0][min(i, n - 1)] == 0 {
zero_c = true;
}
}
for i in 1..m {
for j in 1..n {
if matrix[i][j] == 0 {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}
for i in 1..m {
for j in 1..n {
if matrix[i][0] == 0 || matrix[0][j] == 0 {
matrix[i][j] = 0;
}
}
}
for i in 0..m {
if zero_r {
matrix[i][0] = 0;
}
}
for j in 0..n {
if zero_c {
matrix[0][j] = 0;
}
}
}
}
2.25 - 0074.搜索二维矩阵
方法一:二分搜索
时间复杂度 $O(\log mn)$,空间复杂度 $O(1)$。
func searchMatrix(matrix [][]int, target int) bool {
m, n := len(matrix), len(matrix[0])
for left, right := 0, m*n-1; left <= right; {
mid := left + (right-left)/2
row, col := mid/n, mid%n
if matrix[row][col] == target {
return true
} else if matrix[row][col] > target {
right = mid - 1
} else {
left = mid + 1
}
}
return false
}
2.26 - 0075.颜色分类
方法一:双指针
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func sortColors(nums []int) {
n, p0, p2 := len(nums), -1, len(nums)
for i := 0; i < n && i < p2; i++ {
if nums[i] == 0 {
p0 += 1
nums[p0], nums[i] = 0, nums[p0]
} else if nums[i] == 2 {
p2 -= 1
nums[p2], nums[i] = 2, nums[p2]
i -= 1
}
}
}
2.27 - 0076.最小覆盖子串
方法一:滑动窗口
时间复杂度 $O(n+m)$,空间复杂度 $O(1)$,n
和 m
分别表示字符串 s
和 t
的长度。
func minWindow(s string, t string) string {
need, window, cond := [256]int{}, [256]int{}, 0
for i := 0; i < len(t); i++ {
if need[t[i]] == 0 {
cond++
}
need[t[i]]++
}
ans, m := s+" ", 0
for i, j := 0, 0; j < len(s); j++ {
window[s[j]]++
if window[s[j]] == need[s[j]] {
m++
}
if m < cond {
continue
}
for m == cond {
if len(ans) > j-i+1 {
ans = s[i : j+1]
}
window[s[i]]--
if window[s[i]] < need[s[i]] {
m--
}
i++
}
}
if len(ans) > len(s) {
return ""
}
return ans
}
impl Solution {
pub fn min_window(s: String, t: String) -> String {
let (mut need, mut window) = ([0; 128], [0; 128]);
let (s, t) = (s.as_bytes(), t.as_bytes());
t.iter().for_each(|&v| {
need[v as usize] += 1;
});
fn equal(need: &[i32; 128], window: &[i32; 128]) -> bool {
for i in 0..128 {
if need[i] != 0 && need[i] > window[i] {
return false;
}
}
true
}
let mut ans: String = std::iter::repeat("0").take(s.len() + 1).collect();
let mut pre = 0;
for i in 0..s.len() {
window[s[i] as usize] += 1;
if !equal(&need, &window) {
continue;
}
while equal(&need, &window) {
if ans.len() > i - pre + 1 {
ans = String::from_utf8_lossy(&s[pre..=i]).to_string();
}
window[s[pre] as usize] -= 1;
pre += 1;
}
}
if ans.len() > s.len() {
return "".to_string();
}
ans
}
}
2.28 - 0077.组合
方法一:回溯
时间复杂度 $O(n \times k)$,空间复杂度 $O(n \times k)$。
func combine(n int, k int) [][]int {
ans := [][]int{}
var backtrack func(values []int, started int)
backtrack = func(values []int, started int) {
if len(values) == k {
ans = append(ans, append([]int{}, values...))
return
}
for i := started; i <= n; i++ {
values = append(values, i)
backtrack(values, i+1)
values = values[:len(values)-1]
}
}
backtrack([]int{}, 1)
return ans
}
2.29 - 0078.子集
方法一:回溯
时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(n)$。
func subsets(nums []int) [][]int {
ans, n := [][]int{}, len(nums)
var backtrack func(values []int, start int)
backtrack = func(values []int, start int) {
ans = append(ans, append([]int{}, values...))
for i := start; i < n; i++ {
values = append(values, nums[i])
backtrack(values, i+1)
values = values[:len(values)-1]
}
}
backtrack([]int{}, 0)
return ans
}
2.30 - 0079.单词搜索
方法一:回溯
时间复杂度 $O(mn \times 3^L)$,空间复杂度 $O(mn)$,其中 m
和 n
表示网格的长度与宽度 L
为字符串 word
的长度。
func exist(board [][]byte, word string) bool {
visited, m, n := make([][]bool, len(board)), len(board), len(board[0])
for i := 0; i < m; i++ {
visited[i] = make([]bool, n)
}
var dfs func(x, y, idx int) bool
dfs = func(x, y, idx int) bool {
if board[x][y] != word[idx] {
return false
}
if idx == len(word)-1 {
return true
}
// judege top bottom left right
dirx, diry := []int{-1, 1, 0, 0}, []int{0, 0, -1, 1}
for i := 0; i < 4; i++ {
j, k := x+dirx[i], y+diry[i]
if j >= 0 && j < m && k >= 0 && k < n && !visited[j][k] {
visited[j][k] = true
if dfs(j, k, idx+1) {
return true
}
visited[j][k] = false
}
}
return false
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if board[i][j] == word[0] {
visited[i][j] = true
if dfs(i, j, 0) {
return true
}
visited[i][j] = false
}
}
}
return false
}
2.31 - 0080.删除有序数组中的重复项 II
方法一:双指针
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func removeDuplicates(nums []int) int {
p, p1, p2 := 0, 0, 0
for p2 < len(nums) {
for p2 < len(nums) && nums[p1] == nums[p2] {
p2++
}
nums[p], p = nums[p1], p+1
if p2-p1 >= 2 {
nums[p], p = nums[p1], p+1
}
p1 = p2
}
return p
}
2.32 - 0081.搜索旋转排序数组 II
方法一:二分搜索
时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。
func search(nums []int, target int) bool {
for l, r := 0, len(nums)-1; l <= r; {
m := l + (r-l)>>1
if nums[m] == target {
return true
} else if nums[m] > nums[l] || nums[m] > nums[r] {
if target >= nums[l] && target < nums[m] {
r = m - 1
} else {
l = m + 1
}
} else if nums[m] < nums[l] || nums[m] < nums[r] {
if target > nums[m] && target <= nums[r] {
l = m + 1
} else {
r = m - 1
}
} else {
r -= 1
}
}
return false
}
2.33 - 0082.删除排序链表中的重复元素 II
方法一:一次遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
dummy := &ListNode{}
p := dummy
for head != nil {
p1, p2 := head, head
for p2 != nil && p1.Val == p2.Val {
p2 = p2.Next
}
if p1.Next == p2 {
p.Next, p = p1, p1
}
head = p2
}
p.Next = nil
return dummy.Next
}
2.34 - 0083.删除排序链表中的重复元素
方法一:一次遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteDuplicates(head *ListNode) *ListNode {
if head == nil {
return head
}
dummy := &ListNode{Next: head}
p := dummy.Next
for ; head != nil; head = head.Next {
if p.Val != head.Val {
p.Next, p = head, head
}
}
p.Next = nil
return dummy.Next
}
2.35 - 0084.柱状图中最大的矩形
方法一:单调栈
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
func largestRectangleArea(heights []int) int {
left, right, stack, n := make([]int, len(heights)), make([]int, len(heights)), []int{}, len(heights)
for i := 0; i < n; i++ {
for len(stack) > 0 && heights[stack[len(stack)-1]] >= heights[i] {
stack = stack[:len(stack)-1]
}
left[i] = -1
if len(stack) > 0 {
left[i] = stack[len(stack)-1]
}
stack = append(stack, i)
}
stack = []int{}
for i := n - 1; i >= 0; i-- {
for len(stack) > 0 && heights[stack[len(stack)-1]] >= heights[i] {
stack = stack[:len(stack)-1]
}
right[i] = n
if len(stack) > 0 {
right[i] = stack[len(stack)-1]
}
stack = append(stack, i)
}
ans := 0
for i := 0; i < n; i++ {
area := (right[i] - left[i] - 1) * heights[i]
if area > ans {
ans = area
}
}
return ans
}
方法二:单调栈 + 常数优化
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
func largestRectangleArea(heights []int) int {
left, right, stack, n := make([]int, len(heights)), make([]int, len(heights)), []int{}, len(heights)
for i := 0; i < n; i++ {
for len(stack) > 0 && heights[stack[len(stack)-1]] >= heights[i] {
right[stack[len(stack)-1]] = i
stack = stack[:len(stack)-1]
}
left[i] = -1
if len(stack) > 0 {
left[i] = stack[len(stack)-1]
}
stack = append(stack, i)
}
for i := 0; i < len(stack); i++ {
right[stack[i]] = n
}
ans := 0
for i := 0; i < n; i++ {
area := (right[i] - left[i] - 1) * heights[i]
if area > ans {
ans = area
}
}
return ans
}
2.36 - 0085.最大矩形
方法一:单调栈
时间复杂度 $O(m \times n)$,空间复杂度 $O(n)$,其中 m
,n
表示 matrix
矩阵的长和宽。
func maximalRectangle(matrix [][]byte) int {
ans, m, n := 0, len(matrix), len(matrix[0])
maxArea := func(heights []int) int {
ans, left, right, stack, n := 0, make([]int, len(heights)), make([]int, len(heights)), []int{}, len(heights)
for i := 0; i < n; i++ {
for len(stack) > 0 && heights[stack[len(stack)-1]] >= heights[i] {
right[stack[len(stack)-1]], stack = i, stack[:len(stack)-1]
}
left[i] = -1
if len(stack) > 0 {
left[i] = stack[len(stack)-1]
}
stack = append(stack, i)
}
for _, v := range stack {
right[v] = n
}
for i := 0; i < n; i++ {
area := (right[i] - left[i] - 1) * heights[i]
if area > ans {
ans = area
}
}
return ans
}
heights := make([]int, n)
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if matrix[i][j] == '1' {
heights[j] += 1
} else {
heights[j] = 0
}
}
area := maxArea(heights)
if area > ans {
ans = area
}
}
return ans
}
2.37 - 0086.分隔链表
方法一:遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func partition(head *ListNode, x int) *ListNode {
dummy1, dummy2 := &ListNode{}, &ListNode{}
p1, p2 := dummy1, dummy2
for ; head != nil; head = head.Next {
if head.Val < x {
p1.Next, p1 = head, head
} else {
p2.Next, p2 = head, head
}
}
p1.Next, p2.Next = dummy2.Next, nil
return dummy1.Next
}
2.38 - 0087.扰乱字符串
方法一:递归 + 记忆化搜索
时间复杂度 $O(n^4)$,空间复杂度 $O(n^3)$。
func isScramble(s1 string, s2 string) bool {
cache := make([][][]int, len(s1))
for i := 0; i < len(s1); i++ {
cache[i] = make([][]int, len(s2))
for j := 0; j < len(s2); j++ {
cache[i][j] = make([]int, len(s1)+1)
}
}
check := func(a, b, length int) bool {
cnt_a, cnt_b := [26]int{}, [26]int{}
for i := a; i < a+length; i++ {
cnt_a[s1[i]-'a'] += 1
}
for i := b; i < b+length; i++ {
cnt_b[s2[i]-'a'] += 1
}
for i := 0; i < 26; i++ {
if cnt_a[i] != cnt_b[i] {
return false
}
}
return true
}
var dfs func(a, b, length int) bool
dfs = func(a, b, length int) bool {
if cache[a][b][length] != 0 {
return cache[a][b][length] == 1
}
if s1[a:a+length] == s2[b:b+length] {
cache[a][b][length] = 1
return true
} else if !check(a, b, length) {
cache[a][b][length] = -1
return false
}
for i := 1; i < length; i++ {
if dfs(a, b, i) && dfs(a+i, b+i, length-i) {
cache[a][b][length] = 1
return true
}
if dfs(a, b+length-i, i) && dfs(a+i, b, length-i) {
cache[a][b][length] = 1
return true
}
}
cache[a][b][length] = -1
return false
}
return dfs(0, 0, len(s1))
}
2.39 - 0088.合并两个有序数组
方法一:遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func merge(nums1 []int, m int, nums2 []int, n int) {
idx, i, j := m+n-1, m-1, n-1
for i >= 0 && j >= 0 {
if nums1[i] >= nums2[j] {
nums1[idx], idx, i = nums1[i], idx-1, i-1
} else {
nums1[idx], idx, j = nums2[j], idx-1, j-1
}
}
for ; j >= 0; j-- {
nums1[idx], idx = nums2[j], idx-1
}
}
2.40 - 0089.格雷编码
方法一:对称生成
时间复杂度 $O(2^n)$,空间复杂度 $O(n)$。
func grayCode(n int) []int {
if n == 1 {
return []int{0, 1}
}
ans := grayCode(n - 1)
for i := len(ans) - 1; i >= 0; i-- {
ans = append(ans, ans[i]+1<<(n-1))
}
return ans
}
2.41 - 0090.子集 II
方法一:回溯
时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(n)$。
func subsetsWithDup(nums []int) [][]int {
sort.Ints(nums)
ans, n := [][]int{}, len(nums)
var backtrack func(values []int, start int)
backtrack = func(values []int, start int) {
ans = append(ans, append([]int{}, values...))
for i := start; i < n; i++ {
if i > start && nums[i-1] == nums[i] {
continue
}
values = append(values, nums[i])
backtrack(values, i+1)
values = values[:len(values)-1]
}
}
backtrack([]int{}, 0)
return ans
}
2.42 - 0091.解码方法
方法一:动态规划
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func numDecodings(s string) int {
if s[0] == '0' {
return 0
}
a, b, c := 1, 1, 1
for i := 1; i < len(s); i++ {
pv, v := int(s[i-1]-'0'), int(s[i]-'0')
c = 0
if v != 0 {
c = b
}
if pv != 0 && pv*10+v >= 1 && pv*10+v <= 26 {
c += a
}
a, b = b, c
}
return c
}
2.43 - 0092.反转链表 II
方法一:递归
时间复杂度 $O(right)$,空间复杂度 $O(right)$。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseBetween(head *ListNode, left int, right int) *ListNode {
var poster *ListNode
var reverseN func(head *ListNode, n int) *ListNode
reverseN = func(head *ListNode, n int) *ListNode {
if n == 1 {
poster = head.Next
return head
}
ans := reverseN(head.Next, n-1)
head.Next.Next, head.Next = head, poster
return ans
}
if left == 1 {
return reverseN(head, right)
}
head.Next = reverseBetween(head.Next, left-1, right-1)
return head
}
2.44 - 0093.复原 IP 地址
方法一:回溯
时间复杂度 $O(3^4 \times n)$,空间复杂度 $O(4)$。
func restoreIpAddresses(s string) []string {
ans, n := []string{}, len(s)
var backtrack func(seg []int, start int)
backtrack = func(seg []int, start int) {
if len(seg) == 4 && start == n {
fmt.Println(seg)
ipv4 := s[:seg[0]] + "." + s[seg[0]:seg[1]] + "." + s[seg[1]:seg[2]] + "." + s[seg[2]:]
ans = append(ans, ipv4)
return
} else if start == n || len(seg) == 4 {
return
}
if s[start] == '0' {
seg = append(seg, start+1)
backtrack(seg, start+1)
return
}
digit := 0
for i := start; i < n; i++ {
digit = digit*10 + int(s[i]-'0')
if digit > 0 && digit <= 255 {
seg = append(seg, i+1)
backtrack(seg, i+1)
seg = seg[:len(seg)-1]
} else {
break
}
}
}
backtrack([]int{}, 0)
return ans
}
2.45 - 0094.二叉树的中序遍历
方法一:迭代法
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func inorderTraversal(root *TreeNode) []int {
ans, stack := []int{}, []*TreeNode{}
for len(stack) > 0 || root != nil {
for root != nil {
stack, root = append(stack, root), root.Left
}
top := stack[len(stack)-1]
stack, ans, root = stack[:len(stack)-1], append(ans, top.Val), top.Right
}
return ans
}
2.46 - 0095.不同的二叉搜索树 II
方法一:回溯
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func generateTrees(n int) []*TreeNode {
var dfs func(l, r int) []*TreeNode
dfs = func(l, r int) []*TreeNode {
res := []*TreeNode{}
if l > r {
res = append(res, nil)
return res
}
for i := l; i <= r; i++ {
left := dfs(l, i-1)
right := dfs(i+1, r)
for _, lnode := range left {
for _, rnode := range right {
root := &TreeNode{Left: lnode, Right: rnode, Val: i}
res = append(res, root)
}
}
}
return res
}
return dfs(1, n)
}
2.47 - 0096.不同的二叉搜索树
方法一:回溯
func numTrees(n int) int {
memo := make([][]int, n+1)
for i := 0; i <= n; i++ {
memo[i] = make([]int, n+1)
}
var dfs func(l, r int) int
dfs = func(l, r int) int {
if l > r {
return 1
}
if memo[l][r] != 0 {
return memo[l][r]
}
ans := 0
for i := l; i <= r; i++ {
left, right := dfs(l, i-1), dfs(i+1, r)
ans += left * right
}
memo[l][r] = ans
return ans
}
return dfs(1, n)
}
2.48 - 0097.交错字符串
方法一:动态规划
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。
func isInterleave(s1 string, s2 string, s3 string) bool {
m, n, l, dp := len(s1), len(s2), len(s3), make([][]bool, len(s1)+1)
for i := 0; i <= m; i++ {
dp[i] = make([]bool, n+1)
}
dp[0][0] = true
if m+n != l {
return false
}
for i := 0; i <= m; i++ {
for j := 0; j <= n; j++ {
k := i + j - 1
if i > 0 {
dp[i][j] = dp[i-1][j] && s1[i-1] == s3[k]
}
if j > 0 {
dp[i][j] = dp[i][j] || (dp[i][j-1] && s2[j-1] == s3[k])
}
}
}
return dp[m][n]
}
2.49 - 0098.验证二叉搜索树
方法一:递归
时间复杂度 $O(n)$,空间复杂度 $O(n)$,之所以空间复杂度为 $O(n)$ 是考虑到最坏情况下二叉树为一条链,树的高度为 $n$,递归最深达到 $n$ 层。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func isValidBST(root *TreeNode) bool {
var dfs func(node *TreeNode, min, max int) bool
dfs = func(node *TreeNode, min, max int) bool {
if node == nil {
return true
}
if node.Val <= min || node.Val >= max {
return false
}
return dfs(node.Left, min, node.Val) && dfs(node.Right, node.Val, max)
}
return dfs(root, -1<<31-1, 1<<31)
}
2.50 - 0099.恢复二叉搜索树
方法一:隐式中序遍历
时间复杂度 $O(N)$,空间复杂度 $O(H)$,其中 $N$ 为二叉搜索树的节点个数,$H$ 为二叉搜索树的高度。
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func recoverTree(root *TreeNode) {
var pre *TreeNode = &TreeNode{Val: -1<<31 - 1}
var first, last *TreeNode = nil, nil
var middle func(n *TreeNode)
middle = func(n *TreeNode) {
if n == nil {
return
}
middle(n.Left)
if n.Val < pre.Val {
last = n
if first == nil {
first = pre
} else {
return
}
}
pre = n
middle(n.Right)
}
middle(root)
first.Val, last.Val = last.Val, first.Val
}
3 - 0100-0149
3.1 - 0128.最长连续序列
方法一:HashSet
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
impl Solution {
pub fn longest_consecutive(nums: Vec<i32>) -> i32 {
let mut set = std::collections::HashSet::new();
for (_, &num) in nums.iter().enumerate() {
set.insert(num);
}
let mut longest = 0;
for num in nums {
if !set.contains(&(num - 1)) {
let mut current = num;
let mut current_longest = 1;
while set.contains(&(current + 1)) {
current += 1;
current_longest += 1;
}
longest = std::cmp::max(longest, current_longest);
}
}
longest
}
}
3.2 - 0129.求根节点到叶节点数字之和
方法一:前序遍历
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func sumNumbers(root *TreeNode) int {
var dfs func(root *TreeNode, value int) int
dfs = func(root *TreeNode, value int) int {
if root == nil {
return 0
}
preSum := value*10 + root.Val
if root.Left == nil && root.Right == nil {
return preSum
}
return dfs(root.Left, preSum) + dfs(root.Right, preSum)
}
return dfs(root, 0)
}
3.3 - 0132.分割回文串 II
方法一:动态规划
时间复杂度 $O(n^2)$,空间复杂度 $O(n^2)$。
class Solution {
public:
int minCut(string s) {
int n = s.size();
vector<vector<bool>> g(n, vector<bool>(n, false));
// construct palindrome map
for (int end = 0; end < n; end++) {
for (int start = 0; start <= end; start++) {
if (s[start] == s[end] && (end - start < 2 || g[start + 1][end - 1])) {
g[start][end] = true;
}
}
}
// define dp[n], dp[x] means min Cut
vector<int> dp(n);
iota(dp.begin(), dp.end(), 0);
for (int end = 1; end < n; end++) {
for (int start = 0; start <= end; start++) {
if (g[start][end]) {
dp[end] = min(dp[end], start ? dp[start - 1] + 1 : 0);
}
}
}
return dp[n - 1];
}
};
4 - 0150-0199
4.1 - 0187.重复的DNA序列
方法一:哈希表
朴素解法,用哈希表保存所有长度为 10 的子序列出现的次数,当子序列出现次数大于 1 时,把该子序列作为结果之一。
假设字符串 s
长度为 n
,则时间复杂度 $O(n \times 10)$,空间复杂度 $O(n)$。
func findRepeatedDnaSequences(s string) []string {
ans, cnt := []string{}, map[string]int{}
for i := 0; i <= len(s)-10; i++ {
sub := s[i : i+10]
cnt[sub]++
if cnt[sub] == 2 {
ans = append(ans, sub)
}
}
return ans
}
方法二:Rabin-Karp 字符串匹配算法
和0028.找出字符串中第一个匹配项的下标类似,本题可以借助哈希函数将子序列计数的时间复杂度降低到 $O(1)$。
假设字符串 s
长度为 n
,则时间复杂度为 $O(n)$,空间复杂度 $O(n)$。
func findRepeatedDnaSequences(s string) []string {
hashCode := map[byte]int{'A': 0, 'C': 1, 'G': 2, 'T': 3}
ans, cnt, left, right := []string{}, map[int]int{}, 0, 0
sha, multi := 0, int(math.Pow(4, 9))
for ; right < len(s); right++ {
sha = sha*4 + hashCode[s[right]]
if right-left+1 < 10 {
continue
}
cnt[sha]++
if cnt[sha] == 2 {
ans = append(ans, s[left:right+1])
}
sha, left = sha-multi*hashCode[s[left]], left+1
}
return ans
}
4.2 - 0189.轮转数组
方法一:反转数组
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
先反转整个数组,再反转前 $k$ 个元素,最后反转剩余元素。
impl Solution {
pub fn rotate(nums: &mut Vec<i32>, k: i32) {
let k = k as usize % nums.len();
nums.reverse();
nums[..k].reverse();
nums[k..].reverse();
}
}
5 - 0200-0249
5.1 - 0206.反转链表
方法一:链表遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
在遍历解法中, rust
Box<ListNode>
由于只用到了所有权的转移,所以实现起来显得较为简洁。
struct Solution;
// Definition for singly-linked list.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {
pub val: i32,
pub next: Option<Box<ListNode>>,
}
impl ListNode {
#[inline]
fn new(val: i32) -> Self {
ListNode { next: None, val }
}
}
impl Solution {
pub fn reverse_list(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
let mut pre = None;
let mut head = head;
while let Some(mut p) = head {
head = p.next;
p.next = pre;
pre = Some(p);
}
pre
}
}
方法二:递归链表
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
impl Solution {
pub fn reverse_list(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
match head {
None => None,
Some(mut p) => {
if p.next.is_none() {
return Some(p);
}
let next = p.next.take();
let mut ret = Solution::reverse_list(next);
let mut q = ret.as_mut().unwrap();
while q.next.is_some() {
q = q.next.as_mut().unwrap();
}
q.as_mut().next = Some(p);
ret
}
}
}
}
5.2 - 0234.回文链表
一次遍历
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
在写该题目的 rust 解法时发现凭此时的代码水平只能借助数组辅助比较,具体代码见 rust tab。想要尽力实现类似其他语言一样在找到中间节点之后,反转后半部分链表的逻辑,但 rust 不可变性这一原则的存在限制了我的下一步工作。完美的解法让 copilot 来给我演示吧!
// Definition for singly-linked list.
// #[derive(PartialEq, Eq, Clone, Debug)]
// pub struct ListNode {
// pub val: i32,
// pub next: Option<Box<ListNode>>
// }
//
// impl ListNode {
// #[inline]
// fn new(val: i32) -> Self {
// ListNode {
// next: None,
// val
// }
// }
// }
impl Solution {
pub fn is_palindrome(head: Option<Box<ListNode>>) -> bool {
// find middle
let mut fast = &head;
let mut slow = &head;
let mut nums1 = vec![];
while fast.is_some() && fast.as_ref().unwrap().next.is_some() {
let nt = &fast.as_ref().unwrap().next;
fast = &nt.as_ref().unwrap().next;
nums1.push(slow.as_ref().unwrap().val);
slow = &slow.as_ref().unwrap().next;
}
let mut nums2 = vec![];
while slow.is_some() {
nums2.push(slow.as_ref().unwrap().val);
slow = &slow.as_ref().unwrap().next;
}
nums2.reverse();
for i in 0..nums1.len() {
if nums1[i] != nums2[i] {
return false;
}
}
true
}
}
impl Solution {
pub fn is_palindrome(head: Option<Box<ListNode>>) -> bool {
let mut values = Vec::new();
let mut current = head;
while let Some(node) = current {
values.push(node.val);
current = node.next;
}
let mut left = 0;
let mut right = values.len() - 1;
while left < right {
if values[left] != values[right] {
return false;
}
left += 1;
right -= 1;
}
true
}
}
显然copilot 的代码(第二个rust tab)比我写的好看且简洁,但是空间复杂度并没有降低,仍旧需要调整。
5.3 - 0238.除自身以外数组的乘积
方法一:左右乘积列表
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
impl Solution {
pub fn product_except_self(nums: Vec<i32>) -> Vec<i32> {
let (mut ans, n) = (Vec::new(), nums.len());
let (mut left, mut right) = (vec![1; n], vec![1; n]);
for i in 1..n {
left[i] = left[i - 1] * nums[i - 1];
}
for i in (0..n - 1).rev() {
right[i] = right[i + 1] * nums[i + 1];
}
for i in 0..n {
ans.push(left[i] * right[i]);
}
ans
}
}
5.4 - 0239.滑动窗口最大值
方法一:滑动窗口+双端队列
时间复杂度 $O(n)$,空间复杂度 $O(k)$。
impl Solution {
pub fn max_sliding_window(nums: Vec<i32>, k: i32) -> Vec<i32> {
let mut ans = Vec::new();
let mut deque = std::collections::VecDeque::new();
for i in 0..nums.len() {
while !deque.is_empty() && nums[*deque.back().unwrap()] < nums[i] {
deque.pop_back();
}
deque.push_back(i);
if i >= (k - 1) as usize {
let elem = nums[*deque.front().unwrap()];
ans.push(elem);
if elem == nums[i + 1 - k as usize] {
deque.pop_front();
}
}
}
ans
}
}
5.5 - 0240.搜索二维矩阵 II
方法一:Z 字形查找
时间复杂度 $O(m+n)$,空间复杂度 $O(1)$。
impl Solution {
pub fn search_matrix(matrix: Vec<Vec<i32>>, target: i32) -> bool {
let (m, n) = (matrix.len(), matrix[0].len());
let (mut x, mut y) = (0, (n - 1) as i32);
while x < m && y >= 0 {
if matrix[x][y as usize] > target {
y -= 1;
} else if matrix[x][y as usize] < target {
x += 1;
} else {
return true;
}
}
false
}
}
6 - 0250-0299
6.1 - 0283.移动零
方法一:双指针
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
impl Solution {
pub fn move_zeroes(nums: &mut Vec<i32>) {
let (mut i, mut j) = (0, 0);
while j < nums.len() {
if nums[j] != 0 {
nums[i] = nums[j];
i += 1;
}
j += 1;
}
while i < nums.len() {
nums[i] = 0;
i += 1;
}
}
}
6.2 - 0287.寻找重复数
方法一:快慢指针
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
impl Solution {
pub fn find_duplicate(nums: Vec<i32>) -> i32 {
let mut slow = nums[0] as usize;
let mut fast = nums[nums[0] as usize] as usize;
while slow != fast {
slow = nums[slow] as usize;
fast = nums[nums[fast] as usize] as usize;
}
let mut slow = 0 as usize;
let mut fast = fast as usize;
while slow != fast {
slow = nums[slow] as usize;
fast = nums[fast] as usize;
}
slow as i32
}
}
7 - 0400-0449
7.1 - 0438.找到字符串中所有字母异位词
方法一:滑动窗口
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
impl Solution {
pub fn find_anagrams(s: String, p: String) -> Vec<i32> {
let mut ans = Vec::new();
if p.len() > s.len() {
return ans;
}
let (s, p) = (s.as_bytes(), p.as_bytes());
let (mut s_cnt, mut p_cnt) = ([0; 26], [0; 26]);
for (i, &c) in p.iter().enumerate() {
s_cnt[(s[i] - b'a') as usize] += 1;
p_cnt[(c - b'a') as usize] += 1;
}
if s_cnt == p_cnt {
ans.push(0);
}
for i in p.len()..s.len() {
let (pre_idx, idx) = ((s[i - p.len()] - b'a') as usize, (s[i] - b'a') as usize);
s_cnt[idx] += 1;
s_cnt[pre_idx] -= 1;
if s_cnt == p_cnt {
ans.push((i - p.len() + 1) as i32);
}
}
ans
}
}
8 - 0450-0499
8.1 - 0454.四数相加 II
方法一:分组 + 哈希表
时间复杂度 $O(n^2)$,空间复杂度 $O(n^2)$。
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
ans, count12 := 0, make(map[int]int)
for _, v1 := range nums1 {
for _, v2 := range nums2 {
count12[v1+v2]++
}
}
for _, v3 := range nums3 {
for _, v4 := range nums4 {
ans += count12[-(v3 + v4)]
}
}
return ans
}
9 - 0550-0599
9.1 - 0560.和为 K 的子数组
方法一:前缀和数组
时间复杂度 $O(n)$,空间复杂度 $O(n)$。
impl Solution {
pub fn subarray_sum(nums: Vec<i32>, k: i32) -> i32 {
let mut count = 0;
let mut sum = 0;
let mut map = std::collections::HashMap::new();
map.insert(0, 1);
for num in nums {
sum += num;
if let Some(&v) = map.get(&(sum - k)) {
count += v;
}
*map.entry(sum).or_insert(0) += 1;
}
count
}
}
10 - 0750-0999
10.1 - 0792.匹配子序列的单词数
方法一:二分搜索
时间复杂度 $O(m \log n)$,空间复杂度 $O(n)$。
func numMatchingSubseq(s string, words []string) int {
ans := 0
index := [256][]int{}
for i, c := range s {
index[c] = append(index[c], i)
}
var isSubseq = func(s, word string) bool {
target := 0
for _, c := range word {
if len(index[c]) == 0 {
return false
}
bound := leftBound(index[c], target)
if bound == -1 {
return false
}
target = index[c][bound] + 1
}
return true
}
for _, word := range words {
if isSubseq(s, word) {
ans += 1
}
}
return ans
}
func leftBound(nums []int, target int) int {
l, r := 0, len(nums)-1
for mid := (l + r) / 2; l <= r; mid = (l + r) / 2 {
if nums[mid] >= target {
r = mid - 1
} else {
l = mid + 1
}
}
if l >= len(nums) {
return -1
}
return l
}
10.2 - 0813.最大平均值和的分组
方法一:前缀和 + 记忆化搜索
时间复杂度 $O(n^2 \times k)$,空间复杂度 $O(n \times k)$。
func largestSumOfAverages(nums []int, k int) float64 {
preSum, n := []int{0}, len(nums)
for i, v := range nums {
preSum = append(preSum, preSum[i]+v)
}
memo := [101][101]float64{}
var dfs func(i, k int) float64
dfs = func(i, k int) float64 {
if i == n {
return 0
} else if k == 1 {
return float64(preSum[n]-preSum[i]) / float64(n-i)
} else if memo[i][k] > 0 {
return memo[i][k]
}
ans := 0.0
for j := i; j < n; j++ {
t := float64(preSum[j+1]-preSum[i])/float64(j-i+1) + dfs(j+1, k-1)
if ans < t {
ans = t
}
}
memo[i][k] = ans
return ans
}
return dfs(0, k)
}
10.3 - 0895.最大频率栈
方法一:哈希表模拟
时间复杂度 $O(1)$,空间复杂度 $O(n \times 2)$。
type FreqStack struct {
maxFreq int
valMapFreq map[int]int
freqMapVals map[int][]int
}
func Constructor() FreqStack {
return FreqStack{valMapFreq: map[int]int{}, freqMapVals: map[int][]int{}}
}
func (this *FreqStack) Push(val int) {
this.valMapFreq[val]++
this.freqMapVals[this.valMapFreq[val]] = append(this.freqMapVals[this.valMapFreq[val]], val)
if this.valMapFreq[val] > this.maxFreq {
this.maxFreq = this.valMapFreq[val]
}
}
func (this *FreqStack) Pop() int {
mlen := len(this.freqMapVals[this.maxFreq])
res := this.freqMapVals[this.maxFreq][mlen-1]
this.freqMapVals[this.maxFreq] = this.freqMapVals[this.maxFreq][:mlen-1]
this.valMapFreq[res]--
if len(this.freqMapVals[this.maxFreq]) == 0 {
this.maxFreq--
}
return res
}
/**
* Your FreqStack object will be instantiated and called as such:
* obj := Constructor();
* obj.Push(val);
* param_2 := obj.Pop();
*/
11 - 1000-1499
12 - 1500-1999
12.1 - 1758.生成交替二进制字符串的最少操作数
方法一:简单模拟
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func minOperations(s string) int {
cnt := 0
for i, c := range s {
cnt += int(c-'0') ^ (i & 1)
}
if cnt < len(s)-cnt {
return cnt
}
return len(s) - cnt
}
12.2 - 1774.最接近目标价格的甜点成本
方法一:回溯
暴力回溯,设 baseCosts
长度为 n
,toppingCosts
长度为 m
,则时间复杂度 $O(n \times 3^m)$,空间复杂度 $O(m)$。
func min(a, b int) int {
if a < b {
return a
}
return b
}
func abs(a int) int {
if a < 0 {
return -a
}
return a
}
func closestCost(baseCosts []int, toppingCosts []int, target int) int {
ans, m := 10001, len(toppingCosts)
// 此处使 ans 为 baseCosts 的最小值的原因是:
// 若 ans ≥ target,则无论我们是否添加配料都不会使甜品制作的开销与目标价格 target 的距离缩小,所以此时直接返回此最小的基料开销即可
for _, v := range baseCosts {
ans = min(v, ans)
}
var backtrack func(start, sum int)
backtrack = func(start, sum int) {
if abs(ans-target) >= abs(sum-target) {
if abs(ans-target) > abs(sum-target) {
ans = sum
} else {
ans = min(ans, sum)
}
} else if abs(ans-target) < sum-target {
return
}
if start >= m {
return
}
backtrack(start+1, sum)
backtrack(start+1, sum+toppingCosts[start])
backtrack(start+1, sum+toppingCosts[start]*2)
}
for _, v := range baseCosts {
backtrack(0, v)
}
return ans
}
12.3 - 1779.找到最近的有相同 X 或 Y 坐标的点
方法一:遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$,n
表示数组 points
的长度。
func nearestValidPoint(x int, y int, points [][]int) int {
ans, minLen := -1, 1<<31-1
for i := 0; i < len(points); i++ {
a, b := points[i][0], points[i][1]
if (a == x || b == y) && abs(x, y, a, b) < minLen {
ans, minLen = i, abs(x, y, a, b)
}
}
return ans
}
func abs(x, y, a, b int) int {
ans1, ans2 := x-a, y-b
if ans1 < 0 {
ans1 = -ans1
}
if ans2 < 0 {
ans2 = -ans2
}
return ans1 + ans2
}
12.4 - 1796.字符串中第二大的数字
方法一:遍历
时间复杂度 $O(n)$,空间复杂度 $O(1)$。
func secondHighest(s string) int {
nums := [10]bool{}
for i := range s {
if s[i] >= '0' && s[i] <= '9' {
nums[s[i]-'0'] = true
}
}
c := 0
for i := 9; i >= 0; i-- {
if nums[i] {
c++
}
if c == 2 {
return i
}
}
return -1
}