文章目录
- 两数相加
- 无重复字符的最长子串
- 最长回文子串
- 盛最多水的容器
- 三数之和
- 电话号码的字母组合
- 括号生成
- 下一个排序
- 旋转数组的最小数字
- 搜索旋转排序数组
- 在排序数组中查找元素的第一个和最后一个位置
- 组合总和
- 旋转图像
- 字母异位词分组
- 跳跃游戏
- 合并区间
- 不同路径
- 最小路径和
- 颜色分类
- 子集
- 单词搜索
- 不同的二叉搜索树
- 验证二叉搜索树
- 二叉树的层次遍历
- 从前序与中序遍历序列构造二叉树
- 二叉树展开为链表
- 最长连续序列
- 单词拆分
- 环形链表Ⅱ
- LRU缓存
- 排序链表
- 乘积最大子数组
- 打家劫舍
- 分割数组以得到最大和
- 找出最长的半重复字符串
- 移动机器人
- 剪绳子
- 剪绳子Ⅱ
- 拆分成最多数目的正偶数之和
- 最接近的三数之和
- 数组的最大美丽值
- 合法分割的最小下标
- 构造最长非递减子数组
- 使数组中的所有元素都等于零
- 访问数组中的位置使分数最大
- 将一个数字表示成幂的和的方案数
- 合并后数组的最大元素
- 统计完全子数组的数目
- 使循环数组所有元素相等的最少秒数
- 判断是否能拆分数组
- 树的子结构
- 数组中第K大的最大元素
- 找出美丽数组的最小和
- 统计趣味子数组的数目
- 二叉搜索树的后序遍历序列
- 实现Trie(前缀树)
- 最大正方形
- 解码方法
- 打家劫舍Ⅱ
- 打家劫舍Ⅲ
- 打家劫舍Ⅳ
- 搜索二维矩阵Ⅱ
- 完全平方数
- 寻找重复数
- 将钱分给最多的儿童
- 树上的操作
- 餐厅过滤器
- 买卖股票的最佳时机含冷冻期
- 买卖股票的最佳时机含手续费
- 股票价格跨度
- 只出现一次的数字Ⅱ
- 执行K次操作后的最大分数
- 统计无向图中无法互相到达点对数
- 掷骰子等于目标和的方法数
- 求一个整数的惩罚数
- 数组中两个数的最大异或值
- 重复的DNA序列
- 最大单词长度乘积
- 区域和检索
- 避免洪水泛滥
- 子数组的最小值之和
- 可获得的最大点数
- 到达首都的最小油耗
- 重新规划路线
- 出租车的最大利润
- 环形链表Ⅱ
- 删除链表的倒数第n个节点
- 寻找峰值
- 美丽塔Ⅱ
- 数组的逆序对
- 统计结果概率
- 收集巧克力
- 插入区间
- 从链表中移除节点
- 被列覆盖的最多行数
- 蛇梯棋
- 字符串中的额外字符
- 构造有效字符串的最少插入数
- 数字范围按位与
- 拿出最少数目的魔法豆
- 交错字符串
- 编辑距离
两数相加
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
add := 0
var cur *ListNode
var head *ListNode
x, y := 0, 0
var num int
for l1 != nil || l2 != nil || add != 0 {
if l1 == nil {
x = 0
} else {
x = l1.Val
l1 = l1.Next
}
if l2 == nil {
y = 0
} else {
y = l2.Val
l2 = l2.Next
}
num = x + y + add
add = num / 10
if head == nil {
cur = new(ListNode)
cur.Val = num % 10
head = cur
} else {
after := new(ListNode)
after.Val = num % 10
cur.Next = after
cur = after
}
}
return head
}
无重复字符的最长子串
解法1:用map做队列
func lengthOfLongestSubstring(s string) int {
pos := make(map[int]int, 30)
start := 0
ans := 0
for i, v := range s {
num := int(v - 'a')
if value, ok := pos[num]; ok {
for k := range pos {
if pos[k] <= value {
delete(pos, k)
}
}
start = value + 1
pos[num] = i
} else {
pos[num] = i
cur := i - start + 1
if cur > ans {
//fmt.Printf("%v %v\n", start, i)
ans = cur
}
}
}
return ans
}
解法2:用string做队列
func lengthOfLongestSubstring(s string) int {
str := ""
ans := 0
for _, v := range s {
c := string(v)
index := strings.Index(str, c)
if index != -1 {
str = str[index+1:] + c
} else {
str = str + c
}
l := len(str)
if l > ans {
ans = l
}
}
return ans
}
最长回文子串
解法1:动态规划
时间复杂度O(n2)
空间复杂度O(n2)
func longestPalindrome(s string) string {
n := len(s)
dp := make([][]bool, n)
for i := 0; i < n; i++ {
cur := make([]bool, n)
cur[i] = true
dp[i] = cur
}
ans := 0
str := string(s[0])
//babad
for i := 0; i < n; i++ {
for j := 0; j < i; j++ {
//fmt.Printf("i:%v j:%v vali:%v valj:%v dp:%v\n", i, j, s[i], s[j], dp[j+1][i-1])
if s[i] == s[j] && (j+1 >= i-1 || dp[j+1][i-1] == true) {
//fmt.Printf("i:%v j:%v vali:%v valj:%v\n", i, j, s[i], s[j])
dp[j][i] = true
if i-j+1 > ans {
ans = i - j + 1
str = s[j : i+1]
}
}
}
}
return str
}
解法2:中心扩展
时间复杂度:O(n2)
空间复杂度:O(1)
func longestPalindrome(s string) string {
ans := ""
maxl := 0
for i := 0; i < len(s); i++ {
s1 := center(s, i, i)
s2 := center(s, i, i+1)
s1_len, s2_len := len(s1), len(s2)
if s1_len > s2_len && s1_len > maxl {
maxl = s1_len
ans = s1
} else if s1_len < s2_len && s2_len > maxl {
maxl = s2_len
ans = s2
}
}
return ans
}
func center(s string, left int, right int) string {
for left >= 0 && right < len(s) && s[left] == s[right] {
left--
right++
}
return s[left+1 : right]
}
盛最多水的容器
func maxArea(height []int) int {
l, r := 0, len(height)-1
ans := 0
var cur int
for l < r {
if height[l] < height[r] {
cur = height[l] * (r - l)
l++
} else {
cur = height[r] * (r - l)
r--
}
if cur > ans {
ans = cur
}
}
return ans
}
时间复杂度O(n)
空间复杂度O(1)
三数之和
func threeSum(nums []int) [][]int {
n := len(nums)
if n < 3 {
return [][]int{}
}
ans := [][]int{}
sort.Ints(nums)
var sum int
for i := 0; i < n; i++ {
if i > 0 && nums[i] == nums[i-1] {
continue
}
l, r := i+1, n-1
for l < r {
if l > i+1 && nums[l] == nums[l-1] {
l++
continue
}
if r < n-1 && nums[r] == nums[r+1] {
r--
continue
}
sum = nums[l] + nums[r] + nums[i]
if sum == 0 {
cur := []int{nums[l], nums[i], nums[r]}
ans = append(ans, cur)
l++
r--
} else if sum > 0 {
r--
} else {
l++
}
}
}
return ans
}
时间复杂度O(n2)
空间复杂度O(1)
电话号码的字母组合
func letterCombinations(digits string) []string {
if digits == "" {
return nil
}
f := make(map[uint8][]string, 8)
f[2] = []string{"a", "b", "c"}
f[3] = []string{"d", "e", "f"}
f[4] = []string{"g", "h", "i"}
f[5] = []string{"j", "k", "l"}
f[6] = []string{"m", "n", "o"}
f[7] = []string{"p", "q", "r", "s"}
f[8] = []string{"t", "u", "v"}
f[9] = []string{"w", "x", "y", "z"}
ans := []string{}
n := len(digits)
var dfs func(index int, cur string)
dfs = func(index int, cur string) {
if index == n {
ans = append(ans, cur)
return
}
v := digits[index] - '0'
for _, val := range f[v] {
dfs(index+1, cur+val)
}
}
dfs(0, "")
return ans
}
时间复杂度O(3m+4n)
空间复杂度O(n)
括号生成
func generateParenthesis(n int) []string {
var dfs func(open, close int, cur string)
ans := []string{}
dfs = func(open, close int, cur string) {
if len(cur) == 2*n {
ans = append(ans, cur)
return
}
if open < n {
dfs(open+1, close, cur+"(")
}
if open > close {
dfs(open, close+1, cur+")")
}
}
dfs(0, 0, "")
return ans
}
时间复杂度O(n)
空间复杂度O(n)
下一个排序
如图所示,如果查找该数组的下一个全排序,我们需要从右往左找到第一个比右边小的点,然后跟右边仅大于该点的点进行交换。
交换后,右边这个点的右边需要成为一个递增的序列就是我们所要的解
func nextPermutation(nums []int) {
n := len(nums)
if n == 0 {
return
}
var index = n - 1
for i := n - 1; i >= 1; i-- {
if nums[i-1] < nums[i] {
index = i - 1
break
}
}
if index == n-1 {
sort.Ints(nums)
return
}
cur := nums[index]
var index2 int
for i := n - 1; i > index; i-- {
if cur < nums[i] {
index2 = i
break
}
}
nums[index], nums[index2] = nums[index2], nums[index]
sort.Ints(nums[index+1:])
return
}
时间复杂度O(nlog(n))
空间复杂度O(1)
旋转数组的最小数字
func minNumberInRotateArray(nums []int) int {
// write code here
l, r := 0, len(nums)-1
for l < r {
mid := (l + r) >> 1
if nums[mid] > nums[r] {
l = mid + 1
} else if nums[mid] < nums[l] {
r = mid
} else {
r--
}
}
return nums[l]
}
二分法,如果nums[mid]<nums[l],说明,现在是在旋转处的前面,需要往前找,如果nums[mid]>nums[r],说明是在旋转处的后面,需要往后找。
搜索旋转排序数组
二分法
一定有一半的数组是单调的
如果nums[mid]>nums[0]时:
0~mid一定是单调递增的,这时如果nums[mid]<target,一定是在[mid+1,r]区间内
如果nums[mid]>target并且最小的nums[l]>target,一定是在[mid+1,r]
如果nums[mid]<nums[0]时:
mid+1~r一定是单调递增的,这时如果nums[mid]>target,一定是在[l,mid-1]区间内
如果nums[mid]<target并且最大的nums[r]<target,一定是在[l,mid-1]
func search(nums []int, target int) int {
l, r := 0, len(nums)-1
for l < r {
mid := (l + r) >> 1
if nums[mid] == target {
return mid
}
//fmt.Println(l, r, nums[mid])
if nums[l] == target {
return l
}
if nums[mid] < target {
if nums[0] < nums[mid] || nums[r] >= target {
l = mid + 1
} else {
r = mid
}
} else {
if nums[0] > nums[mid] || nums[l] < target {
r = mid
} else {
l = mid + 1
}
}
}
if nums[r] == target {
return r
}
return -1
}
时间复杂度O(logn)
空间复杂度O(1)
在排序数组中查找元素的第一个和最后一个位置
func searchRange(nums []int, target int) []int {
n := len(nums)
if n == 0 {
return []int{-1, -1}
}
start, end := 0, n-1
var mid int
ans := make([]int, 2)
for start < end {
mid = (start + end) >> 1
if nums[mid] < target {
start = mid + 1
} else {
end = mid
}
}
if nums[start] == target {
ans[0] = start
} else {
return []int{-1, -1}
}
end = n - 1
for start < end-1 {
mid = (start + end) >> 1
if nums[mid] > target {
end = mid - 1
} else {
start = mid
}
}
if nums[end] == target {
ans[1] = end
} else {
ans[1] = start
}
return ans
}
时间复杂度O(log(n))
空间复杂度O(1)
组合总和
func combinationSum(candidates []int, target int) [][]int {
n := len(candidates)
if n == 0 {
return nil
}
ans := [][]int{}
cur := []int{}
var dfs func(sum int, index int)
dfs = func(sum int, index int) {
if sum > target || index >= n {
return
}
if sum == target {
tmp := make([]int, len(cur))
copy(tmp, cur)
ans = append(ans, tmp)
return
}
dfs(sum, index+1)
num := candidates[index]
cur = append(cur, num)
dfs(sum+num, index)
cur = cur[:len(cur)-1]
}
dfs(0, 0)
return ans
}
空间复杂度:O(target)
旋转图像
func rotate(matrix [][]int) {
n := len(matrix)
if n == 0 {
return
}
var j int
for i := 0; i < (n+1)/2; i++ {
tmp := make([]int, n-2*i)
copy(tmp, matrix[i][i:n-i])
for j = i + 1; j < n-i; j++ {
matrix[i][n-j-1] = matrix[j][i]
}
for j = i + 1; j < n-i; j++ {
matrix[j][i] = matrix[n-i-1][j]
}
for j = i + 1; j < n-i; j++ {
matrix[n-i-1][j] = matrix[n-j-1][n-i-1]
}
for j = 0; j < n-2*i; j++ {
matrix[i+j][n-i-1] = tmp[j]
}
}
}
时间复杂度O(n2)
空间复杂度O(n)
字母异位词分组
解1:hash表+排序
func groupAnagrams(strs []string) [][]string {
n := len(strs)
if n == 0 {
return [][]string{strs}
}
cur := map[string][]string{}
ans := [][]string{}
for _, v := range strs {
tmp := []byte(v)
sort.Slice(tmp, func(i, j int) bool {
return tmp[i] < tmp[j]
})
s := string(tmp)
cur[s] = append(cur[s], v)
}
for _, v := range cur {
ans = append(ans, v)
}
return ans
}
时间复杂度O(nklog(k))k是字符串的长度
空间复杂度O(nk)
解2:hash数组存字符串所有字母的个数集合
func groupAnagrams(strs []string) [][]string {
n := len(strs)
if n == 0 {
return [][]string{strs}
}
cur := map[[26]int][]string{}
ans := [][]string{}
for _, v := range strs {
cnt := [26]int{}
for _, b := range v {
cnt[b-'a']++
}
cur[cnt] = append(cur[cnt], v)
}
for _, v := range cur {
ans = append(ans, v)
}
return ans
}
时间复杂度O(nk)k是字符串的最大长度
空间复杂度O(nk)
跳跃游戏
解法1:标记数组
func canJump(nums []int) bool {
is_ok := make([]bool, len(nums))
is_ok[0] = true
n := len(nums)
if n == 0 {
return true
}
var cur int
for i, v := range nums {
if !is_ok[i] {
continue
}
for j := 1; j <= v; j++ {
cur = i + j
if cur > n-1 {
break
}
is_ok[cur] = true
}
}
return is_ok[n-1]
}
时间复杂度O(n*k) k是跳跃的步长
空间复杂度O(n)
解法2:扩展队列
func canJump(nums []int) bool {
n := len(nums)
if n <= 1 {
return true
}
pos := make([]int, n)
pos[0] = 0
vis := make([]bool, n)
l := 1
var next int
for l > 0 {
cur := pos[l-1]
//fmt.Println(cur)
l--
for i := 1; i <= nums[cur]; i++ {
next = cur + i
if next > n-1 || vis[next] {
continue
}
if next == n-1 {
return true
}
vis[next] = true
pos[l] = next
l++
}
}
return false
}
时间复杂度O(n*k)
空间复杂度O (n)
解法3:记录最大位置
其实每次我们只需要遍历时记录能跳跃到的最大位置就可以,每次遍历时首先判断当前位置是不是超过了记录的最大位置,如果超过的话直接返回false就行了,因为最大位置到不了这的话更不可能达到最后一个点。如果遍历到的点位置小于记录的最大位置,则根据遍历到的位置再尝试更新最大位置。直到最大位置大于等于最终的位置返回true
func canJump(nums []int) bool {
n := len(nums)
if n <= 1 {
return true
}
max_pos := 0
for i, v := range nums {
if i <= max_pos {
if i+v > max_pos {
max_pos = i + v
}
} else {
return false
}
if max_pos >= n-1 {
return true
}
}
return false
}
时间复杂度O(n)
空间复杂度O(1)
合并区间
func merge(intervals [][]int) [][]int {
n := len(intervals)
if n <= 1 {
return intervals
}
sort.Slice(intervals, func(i, j int) bool {
if intervals[i][0] == intervals[j][0] {
return intervals[i][1] < intervals[j][1]
}
return intervals[i][0] < intervals[j][0]
})
ans := [][]int{}
start, end := intervals[0][0], intervals[0][1]
for _, v := range intervals {
if v[0] <= end && v[1] > end {
end = v[1]
} else if v[0] > end {
ans = append(ans, []int{start, end})
start = v[0]
end = v[1]
}
}
ans = append(ans, []int{start, end})
return ans
}
时间复杂度:O(nlog(n))
空间复杂度:O(1)
不同路径
解1:dfs+剪枝
func uniquePaths(m int, n int) int {
cnt := make([][]int, m)
for i := 0; i < m; i++ {
cur := make([]int, n)
cnt[i] = cur
}
var dfs func(x, y int) int
dfs = func(x, y int) int {
if x >= m || y >= n {
return 0
}
if cnt[x][y] != 0 {
return cnt[x][y]
}
if x == m-1 && y == n-1 {
return 1
}
cnt[x][y] = dfs(x+1, y) + dfs(x, y+1)
return cnt[x][y]
}
return dfs(0, 0)
}
时间复杂度O(mn)
空间复杂度O(mn)
解法2:组合排序
根据m和n我们可以知道向右和向下的总步数,分别是n-1和m-1,然后总步数是m+n-2,我们只需要在m+n-2当中选出n-1个向右的,剩余的为向下的,能选出的组合数就是结果
func uniquePaths(m, n int) int {
return int(new(big.Int).Binomial(int64(m+n-2), int64(n-1)).Int64())
}
时间复杂度O(min(m,n))
空间复杂度O(1)
最小路径和
func min(x, y int) int {
if x > y {
return y
}
return x
}
func minPathSum(grid [][]int) int {
n := len(grid)
if n == 0 {
return 0
}
m := len(grid[0])
dp := make([][]int, n)
for i := 0; i < n; i++ {
cur := make([]int, m)
dp[i] = cur
if i != 0 {
dp[i][0] = dp[i-1][0] + grid[i][0]
} else {
dp[0][0] = grid[0][0]
}
}
for j := 1; j < m; j++ {
dp[0][j] = dp[0][j-1] + grid[0][j]
}
for i := 1; i < n; i++ {
for j := 1; j < m; j++ {
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
}
}
//fmt.Println(dp)
return dp[n-1][m-1]
}
时间复杂度O(mn)
空间复杂度O(mn)
滚动数组
func min(x, y int) int {
if x > y {
return y
}
return x
}
func minPathSum(grid [][]int) int {
n := len(grid)
if n == 0 {
return 0
}
m := len(grid[0])
dp := make([]int, m)
dp[0] = grid[0][0]
for i := 1; i < m; i++ {
dp[i] = dp[i-1] + grid[0][i]
}
tmp := make([]int, m)
for i := 1; i < n; i++ {
for j := 0; j < m; j++ {
if j == 0 {
tmp[j] = dp[j] + grid[i][j]
} else {
tmp[j] = min(dp[j], tmp[j-1]) + grid[i][j]
}
}
copy(dp, tmp)
}
return dp[m-1]
}
时间复杂度O(mn)
空间复杂度O(m)可以进一步优化为O(min(m,n))
颜色分类
解法1:计数
func sortColors(nums []int) {
n := len(nums)
if n <= 1 {
return
}
cnt0, cnt1, cnt2 := 0, 0, 0
var i int
for i = 0; i < n; i++ {
if nums[i] == 1 {
cnt1++
} else if nums[i] == 0 {
cnt0++
} else {
cnt2++
}
}
i = 0
for i < n {
if i < cnt0 {
nums[i] = 0
} else if i < cnt0+cnt1 {
nums[i] = 1
} else {
nums[i] = 2
}
i++
}
}
解法2:双指针
func sortColors(nums []int) {
n := len(nums)
if n <= 1 {
return
}
l, r := 0, 0
for i, v := range nums {
if v == 0 {
nums[l], nums[i] = nums[i], nums[l]
if nums[i] == 1 {
nums[i], nums[r] = nums[r], nums[i]
}
l++
r++
} else if v == 1 {
nums[r], nums[i] = nums[i], nums[r]
r++
}
}
}
子集
func subsets(nums []int) [][]int {
ans := [][]int{}
n := len(nums)
var dfs func(index int, cur []int)
dfs = func(index int, cur []int) {
if index == n {
tmp := make([]int, len(cur))
copy(tmp, cur)
ans = append(ans, tmp)
return
}
dfs(index+1, cur)
dfs(index+1, append(cur, nums[index]))
}
cur := []int{}
dfs(0, cur)
return ans
}
时间复杂度O(n*2n)
空间复杂度O(n)
单词搜索
解法1:回溯法
func exist(board [][]byte, word string) bool {
lw := len(word)
if lw == 0 {
return true
}
n := len(board)
if n == 0 {
return true
}
m := len(board[0])
vis := make([][]bool, n)
for i := 0; i < n; i++ {
cur := make([]bool, m)
vis[i] = cur
}
var dfs func(x, y int, index int) bool
dfs = func(x, y int, index int) bool {
if index == lw {
return true
}
if x >= n || y >= m || x < 0 || y < 0 || vis[x][y] {
return false
}
if board[x][y] != word[index] {
return false
}
vis[x][y] = true
//fmt.Println(x, y, index)
index++
if dfs(x+1, y, index) || dfs(x-1, y, index) || dfs(x, y+1, index) || dfs(x, y-1, index) {
return true
}
vis[x][y] = false
return false
}
for i := 0; i < n; i++ {
for j := 0; j < m; j++ {
if board[i][j] == word[0] {
for x := 0; x < n; x++ {
for y := 0; y < m; y++ {
vis[x][y] = false
}
}
if dfs(i, j, 0) {
return true
}
}
}
}
return false
}
不同的二叉搜索树
func numTrees(n int) int {
dp := make([]int, n+1)
dp[0] = 1
dp[1] = 1
for i := 2; i <= n; i++ {
for j := 1; j <= i; j++ {
dp[i] += dp[j-1] * dp[i-j]
}
}
return dp[n]
}
时间复杂度O(n2)
空间复杂度O(n)
验证二叉搜索树
func isValidBST(root *TreeNode) bool {
var dfs func(cur *TreeNode, min int, max int) bool
dfs = func(cur *TreeNode, min int, max int) bool {
if cur == nil {
return true
}
if cur.Val <= min || cur.Val >= max {
return false
}
return dfs(cur.Left, min, cur.Val) && dfs(cur.Right, cur.Val, max)
}
return dfs(root, math.MinInt64, math.MaxInt64)
}
时间复杂度O(n)
空间复杂度O(n)
二叉树的层次遍历
func levelOrder(root *TreeNode) [][]int {
if root == nil {
return nil
}
queue := []*TreeNode{root}
ans := [][]int{}
for i := 0; i < len(queue); i++ {
cur := make([]*TreeNode, len(queue))
copy(cur, queue)
queue = nil
tmp := []int{}
for _, v := range cur {
node := v
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
tmp = append(tmp, node.Val)
}
ans = append(ans, tmp)
}
return ans
}
时间复杂度O(n)
空间复杂度O(n)
从前序与中序遍历序列构造二叉树
解法1:递归加遍历
func buildTree(preorder []int, inorder []int) *TreeNode {
n := len(preorder)
if len(inorder) != n {
return nil
}
var dfs func(index, li, ri int) *TreeNode
dfs = func(index, li, ri int) *TreeNode {
if index >= n || li > ri {
return nil
}
cur := preorder[index]
next := li
for next <= ri {
if inorder[next] == cur {
break
}
next++
}
node := new(TreeNode)
node.Val = cur
node.Left = dfs(index+1, li, next-1)
node.Right = dfs(index+next-li+1, next+1, ri)
return node
}
return dfs(0, 0, n-1)
}
时间复杂度O(n2)
空间复杂度O(n)
解法2:哈希加遍历
func buildTree(preorder []int, inorder []int) *TreeNode {
n := len(preorder)
if len(inorder) != n {
return nil
}
pos := make(map[int]int, n)
for i, v := range inorder {
pos[v] = i
}
var dfs func(index, li, ri int) *TreeNode
dfs = func(index, li, ri int) *TreeNode {
if index >= n || li > ri {
return nil
}
cur := preorder[index]
next := pos[cur]
node := new(TreeNode)
node.Val = cur
node.Left = dfs(index+1, li, next-1)
node.Right = dfs(index+next-li+1, next+1, ri)
return node
}
return dfs(0, 0, n-1)
}
用哈希数组来代替遍历
时间复杂度O(n)
空间复杂度O(n)
二叉树展开为链表
解法1:递归
func flatten(root *TreeNode) {
var dfs func(cur *TreeNode) *TreeNode
if root == nil {
return
}
dfs = func(cur *TreeNode) *TreeNode {
if cur.Left == nil && cur.Right == nil {
return cur
}
var node *TreeNode
var node2 *TreeNode
if cur.Left == nil {
node2 = dfs(cur.Right)
return node2
} else if cur.Right == nil {
node = dfs(cur.Left)
cur.Right = cur.Left
cur.Left = nil
return node
} else {
node = dfs(cur.Left)
node2 = dfs(cur.Right)
node.Right = cur.Right
cur.Right = cur.Left
cur.Left = nil
return node2
}
}
dfs(root)
}
时间复杂度:O(n)
空间复杂度:O(n)
解法2:非递归遍历
func flatten(root *TreeNode) {
if root == nil {
return
}
stack := []*TreeNode{root}
var pre *TreeNode
for len(stack) > 0 {
cur := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if pre == nil {
pre = cur
} else {
pre.Right = cur
pre.Left = nil
pre = cur
}
left, right := cur.Left, cur.Right
if right != nil {
stack = append(stack, right)
}
if left != nil {
stack = append(stack, left)
}
}
}
时间复杂度:O(n)
空间复杂度:O(n)
解法3:寻找前驱节点
func flatten(root *TreeNode) {
if root == nil {
return
}
cur := root
for cur != nil {
if cur.Left != nil {
next := cur.Left
pre := next
for pre.Right != nil {
pre = pre.Right
}
pre.Right = cur.Right
cur.Right = cur.Left
cur.Left = nil
}
cur = cur.Right
}
}
时间复杂度:O(n)
空间复杂度:O(1)
最长连续序列
func longestConsecutive(nums []int) int {
n := len(nums)
if n == 0 {
return 0
}
vis := make(map[int]struct{}, n)
for _, v := range nums {
vis[v] = struct{}{}
}
max := 0
var cnt int
for index, _ := range vis {
cnt = 1
cur := index
delete(vis, cur)
for true {
if _, ok := vis[cur+1]; ok {
cur++
cnt++
delete(vis, cur)
} else {
break
}
}
cur = index
for true {
if _, ok := vis[cur-1]; ok {
cur--
cnt++
delete(vis, cur)
} else {
break
}
}
if cnt > max {
max = cnt
}
}
return max
}
时间复杂度:O(n)
空间复杂度:O(n)
单词拆分
动态规划:
动态规划:dp[i]表示s[0:i+1]的字符串是否可以用wordDict拼接
dp[i]能够由wordDict拼接有两种情况:
1.s[0:i+1]可以直接从wordDict当中找到
2.存在j<i,dp[j]为true,并且s[j+1:i+1]可以在wordDict当中找到
func wordBreak(s string, wordDict []string) bool {
n := len(s)
if n == 0 {
return true
}
dp := make([]bool, n)
vis := make(map[string]bool, len(wordDict))
for _, v := range wordDict {
vis[v] = true
}
for i := 0; i < n; i++ {
if vis[s[:i+1]] {
dp[i] = true
continue
}
for j := 0; j < i; j++ {
if dp[j] && vis[s[j+1:i+1]] {
dp[i] = true
}
}
}
return dp[n-1]
}
时间复杂度:O(m*n) m为字符串长度,n是数组长度
空间复杂度:O(max(m,n))
环形链表Ⅱ
快慢指针:设链表为a+b,a是环之前长度,b是环的长度。每次fast走两步,slow走一步。只要有环,两者一定会相遇。设相遇时slow走了s,则fast走了f,f=2s,f=s+nb。两者求得s=nb,f=2nb。然后再让s走a,就a+nb,就到了环的入口,怎么让s走a呢?只需要让fast再回到head,然后fast和slow每次走一步,两者相遇时就是环的入口的位置
func detectCycle(head *ListNode) *ListNode {
fast, slow := head, head
for true {
if fast == nil || fast.Next == nil {
return nil
}
fast = fast.Next.Next
slow = slow.Next
if fast == slow {
break
}
}
fast = head
for fast != slow {
fast = fast.Next
slow = slow.Next
}
return fast
}
时间复杂度:O(n)
空间复杂度:O(1)
LRU缓存
type DLinkedNode struct {
key, value int
pre, next *DLinkedNode
}
type LRUCache struct {
size int
capacity int
cache map[int]*DLinkedNode
head, tail *DLinkedNode
}
func newNode(key, value int) *DLinkedNode {
return &DLinkedNode{
key: key,
value: value,
}
}
func Constructor(capacity int) LRUCache {
l := LRUCache{
size: 0,
capacity: capacity,
cache: make(map[int]*DLinkedNode, capacity),
head: newNode(0, 0),
tail: newNode(0, 0),
}
l.head.next = l.tail
l.tail.pre = l.head
return l
}
func (this *LRUCache) moveToHead(cur *DLinkedNode) {
cur.pre.next = cur.next
cur.next.pre = cur.pre
cur.next = this.head.next
this.head.next.pre = cur
this.head.next = cur
cur.pre = this.head
}
func (this *LRUCache) Get(key int) int {
if _, ok := this.cache[key]; !ok {
return -1
}
node := this.cache[key]
if this.head.next == node {
return node.value
}
this.moveToHead(node)
return node.value
}
func (this *LRUCache) Put(key int, value int) {
if _, ok := this.cache[key]; ok {
node := this.cache[key]
node.value = value
this.moveToHead(node)
return
}
if this.size < this.capacity {
node := new(DLinkedNode)
node.key = key
node.value = value
node.next = this.head.next
this.head.next.pre = node
this.head.next = node
node.pre = this.head
this.cache[key] = node
this.size++
} else {
tail := this.tail.pre
delete(this.cache, tail.key)
tail.key = key
tail.value = value
this.cache[key] = tail
this.moveToHead(tail)
}
}
采用带首尾指针的双循环链表
时间复杂度O(1)
空间复杂度O(n)
排序链表
func sortList(head *ListNode) *ListNode {
n := 0
cur := head
for cur != nil {
n++
cur = cur.Next
}
nodeIndex := make(map[int]*ListNode, n)
cur = head
for i := 0; i < n; i++ {
nodeIndex[i] = cur
cur = cur.Next
}
var mergeSort func(l int, r int) *ListNode
mergeSort = func(l int, r int) *ListNode {
if l == r-1 {
nodeIndex[l].Next = nil
return nodeIndex[l]
}
mid := (l + r) >> 1
lList := mergeSort(l, mid)
rList := mergeSort(mid, r)
var tail *ListNode
var head *ListNode
for lList != nil || rList != nil {
if rList == nil || (lList != nil && lList.Val <= rList.Val) {
if tail == nil {
tail = lList
head = tail
} else {
tail.Next = lList
tail = lList
}
lList = lList.Next
} else if lList == nil || (rList != nil && rList.Val <= lList.Val) {
if tail == nil {
tail = rList
head = tail
} else {
tail.Next = rList
tail = rList
}
rList = rList.Next
}
tail.Next = nil
}
return head
}
return mergeSort(0, n)
}
时间复杂度:O(n*log(n))
空间复杂度:O(n)
乘积最大子数组
func maxProduct(nums []int) int {
n := len(nums)
if n == 0 {
return 0
}
maxF, minF, ans := nums[0], nums[0], nums[0]
for i := 1; i < n; i++ {
mx, mn := maxF, minF
maxF = max(mx*nums[i], max(nums[i], mn*nums[i]))
minF = min(mn*nums[i], min(nums[i], mx*nums[i]))
ans = max(ans, maxF)
}
return ans
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
时间复杂度:O(n)
空间复杂度:O(1)
打家劫舍
func rob(nums []int) int {
n := len(nums)
if n == 0 {
return 0
} else if n == 1 {
return nums[0]
}
dp := make([]int, n)
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i := 2; i < n; i++ {
dp[i] = max(dp[i-1], dp[i-2]+nums[i])
}
//for i := 0; i < n; i++ {
// fmt.Println(i, dp[i])
//}
return dp[n-1]
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
时间复杂度:O(n)
空间复杂度:O(n)
用变量代替数组
func rob(nums []int) int {
n := len(nums)
if n == 0 {
return 0
} else if n == 1 {
return nums[0]
}
min0 := nums[0]
min1 := max(nums[0], nums[1])
for i := 2; i < n; i++ {
min1, min0 = max(min1, min0+nums[i]), min1
}
//for i := 0; i < n; i++ {
// fmt.Println(i, dp[i])
//}
return min1
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
时间复杂度:O(n)
空间复杂度:O(1)
分割数组以得到最大和
func max(x, y int) int {
if x > y {
return x
}
return y
}
func maxSumAfterPartitioning(arr []int, k int) int {
n := len(arr)
if n == 0 || k == 0 {
return 0
}
dp := make([]int, n)
dp[0] = arr[0]
for i := 1; i < n; i++ {
m := arr[i]
for j := i; j >= max(0, i-k+1); j-- {
if arr[j] > m {
m = arr[j]
}
if j-1 >= 0 {
dp[i] = max(dp[i], dp[j-1]+m*(i-j+1))
} else {
dp[i] = m * (i - j + 1)
}
}
}
//for i := 0; i < n; i++ {
// fmt.Printf("index:%d value:%d\n", i, dp[i])
//}
return dp[n-1]
}
找出最长的半重复字符串
解法:滑动窗口
func longestSemiRepetitiveSubstring(s string) int {
before := 0
after := 0
max := 0
l := len(s)
cnt := 0
if l <= 1 {
return l
}
for after = 1; after < l; after++ {
if s[after-1] == s[after] {
cnt++
}
for cnt > 1 {
before++
if s[before] == s[before-1] {
cnt--
}
}
if after-before+1 > max {
max = after - before + 1
}
}
return max
}
移动机器人
const mod int = 1e9 + 7
func sumDistance(nums []int, s string, d int) int {
l := len(nums)
if l <= 1 || s == "" {
return 0
}
for i := 0; i < l; i++ {
if s[i] == 'R' {
nums[i] += d
} else {
nums[i] -= d
}
}
sum := 0
//cnt := 0
sort.Ints(nums)
if l == 2 {
return (nums[1] - nums[0]) % mod
}
//fmt.Println(nums)
n := l - 1
cnt := 0
for i := 0; i < l-1; i++ {
cnt += 1
sum = (sum + (((nums[i+1]-nums[i])%mod)*n*cnt)%mod) % mod
//fmt.Println("cnt:", cnt, nums[i+1], nums[i], sum, n)
n--
}
//fmt.Println(sum, cnt)
//sum = (sum + nums[l-1]*n) % mod
return sum
}
剪绳子
func max(x, y int) int {
if x > y {
return x
}
return y
}
func cuttingRope(n int) int {
dp := make([]int, n+1)
dp[1] = 1
if n <= 1 {
return n
}
for i := 2; i <= n; i++ {
for j := 1; j < i; j++ {
dp[i] = max(dp[i], max((i-j)*j, max((i-j)*dp[j], j*dp[i-j])))
}
}
return dp[n]
}
剪绳子Ⅱ
推论一:绳子剪成相等长度多段,得到的乘积最大
推论二:尽可能将绳子以长度3等分为多段时,乘积最大
const mod = 1e9 + 7
func cuttingRope(n int) int {
ans := 1
if n == 1 || n == 2 {
return 1
} else if n == 3 {
return 2
}
if n%3 == 0 {
k := n / 3
base := 3
for k > 0 {
if k&1 == 1 {
ans = ans * base % mod
}
base = base * base % mod
k >>= 1
}
} else if n%3 == 2 {
k := n / 3
base := 3
for k > 0 {
if k&1 == 1 {
ans = ans * base % mod
}
base = base * base % mod
k >>= 1
}
ans = 2 * ans % mod
} else if n%3 == 1 {
k := n/3 - 1
base := 3
for k > 0 {
if k&1 == 1 {
ans = ans * base % mod
}
base = base * base % mod
k >>= 1
}
ans = 4 * ans % mod
}
return ans
}
拆分成最多数目的正偶数之和
func maximumEvenSplit(finalSum int64) []int64 {
var tmp int64 = 2
if finalSum%2 != 0 {
return nil
}
vis := make(map[int64]struct{}, 10000)
ans := []int64{}
for finalSum > 0 {
if finalSum-tmp < 0 {
break
}
finalSum -= tmp
ans = append(ans, tmp)
if _, ok := vis[finalSum]; ok {
break
}
vis[tmp] = struct{}{}
tmp += 2
}
ans[len(ans)-1] += finalSum
return ans
}
最接近的三数之和
func threeSumClosest(nums []int, target int) int {
n := len(nums)
if n < 3 {
return -1
}
sort.Ints(nums)
ans := -1
min := -1
for i := 0; i < n; i++ {
a := nums[i]
left, right := i+1, n-1
for left < right {
cur := a + nums[left] + nums[right]
if cur > target {
if min == -1 || cur-target < min {
min = cur - target
ans = cur
}
right--
} else if cur < target {
if min == -1 || target-cur < min {
min = target - cur
ans = cur
}
left++
} else {
return target
}
}
}
return ans
}
数组的最大美丽值
func maximumBeauty(nums []int, k int) int {
sort.Ints(nums)
n := len(nums)
ans := 0
left, right := 0, 0
for right < n {
for left < n && nums[right]-nums[left] > 2*k {
left++
}
if right-left+1 > ans {
ans = right - left + 1
}
right++
}
return ans
}
排序后,维护一个子序列[l,r],使得nums[r]-nums[l]<=2*k,max(r-l+1)
合法分割的最小下标
func minimumIndex(nums []int) int {
n := len(nums)
if n == 0 {
return -1
}
num := nums[0]
cnt := 0
for i := 0; i < n; i++ {
if nums[i] == num {
cnt++
} else {
cnt--
}
if cnt == 0 {
num = nums[i]
cnt = 1
}
}
cnt = 0
for i := 0; i < n; i++ {
if nums[i] == num {
cnt++
}
}
cur_cnt := 0
for i := 0; i < n; i++ {
if nums[i] == num {
cur_cnt++
}
if cur_cnt*2 > i+1 && (cnt-cur_cnt)*2 > n-i-1 {
return i
}
}
return -1
}
构造最长非递减子数组
func max(x, y int) int {
if x > y {
return x
}
return y
}
func maxNonDecreasingLength(nums1 []int, nums2 []int) int {
n := len(nums1)
if n == 0 {
return 0
}
dp1, dp2 := make([]int, n), make([]int, n)
for i := 0; i < n; i++ {
dp1[i], dp2[i] = 1, 1
}
ans := 1
for i := 1; i < n; i++ {
if nums1[i] >= nums1[i-1] {
dp1[i] = dp1[i-1] + 1
}
if nums1[i] >= nums2[i-1] {
if dp1[i] == 1 {
dp1[i] = dp2[i-1] + 1
} else {
dp1[i] = max(dp1[i], dp2[i-1]+1)
}
}
if nums2[i] >= nums2[i-1] {
dp2[i] = dp2[i-1] + 1
}
if nums2[i] >= nums1[i-1] {
if dp2[i] == 1 {
dp2[i] = dp1[i-1] + 1
} else {
dp2[i] = max(dp2[i], dp1[i-1]+1)
}
}
ans = max(ans, max(dp1[i], dp2[i]))
}
return ans
}
使数组中的所有元素都等于零
差分数组
func checkArray(nums []int, k int) bool {
n := len(nums)
if n == 0 {
return true
}
tmp := make([]int, n+1)
sub := 0
for i := 0; i < n; i++ {
sub += tmp[i]
nums[i] -= sub
if nums[i] == 0 {
continue
}
if nums[i] < 0 || i+k > n {
return false
}
sub += nums[i]
tmp[i+k] -= nums[i]
//sub += tmp[i]
//fmt.Println(i, tmp, sub)
}
return true
}
访问数组中的位置使分数最大
维护两个变量odd和even,分别代表以奇数和偶数结尾的当前获取到的最大的结果。在刚开始时,需要根据num[0]的奇数和偶数性进行转化来获取odd和even的初始值。之后遍历数组,对于nums[i],如果nums[i]是奇数,更新odd:之前最大odd+当前nums[i],或者之前最大even减去开销x+当前nums[i],哪个大odd就是哪个。并根据条件来维护一个结果的最大值
func max(x, y int64) int64 {
if x > y {
return x
}
return y
}
func maxScore(nums []int, x int) int64 {
n := len(nums)
if n == 0 {
return 0
}
y := int64(x)
var even int64
var odd int64
if nums[0]%2 == 0 {
even = int64(nums[0])
odd = int64(nums[0] - x)
} else {
odd = int64(nums[0])
even = int64(nums[0] - x)
}
cur := int64(nums[0])
ans := cur
for i := 1; i < n; i++ {
v := int64(nums[i])
if nums[i]%2 == 0 {
cur = max(v+even, v+odd-y)
even = max(even+v, odd+v-y)
} else {
cur = max(v+odd, v+even-y)
odd = max(odd+v, even+v-y)
}
if cur > ans {
ans = cur
}
}
return ans
}
时间复杂度:O(n)
空间复杂度:O(1)
将一个数字表示成幂的和的方案数
const mod = 1e9 + 7
func pow(x, y int) int {
base := x
ans := 1
for y > 0 {
if y&1 == 1 {
ans *= base
}
base *= base
y >>= 1
}
return ans
}
func numberOfWays(n, x int) int {
dp := make([]int, n+1)
dp[0] = 1
for i := 1; pow(i, x) <= n; i++ {
v := pow(i, x)
for j := n; j >= v; j-- {
dp[j] += dp[j-v]
}
}
return dp[n] % mod
}
合并后数组的最大元素
func maxArrayValue(nums []int) int64 {
var ans int64
var cur int64
ans = 0
n := len(nums)
if n == 1 {
return int64(nums[0])
}
cur = int64(nums[n-1])
for i := n - 1; i >= 1; i-- {
if cur >= int64(nums[i-1]) {
cur += int64(nums[i-1])
} else {
cur = int64(nums[i-1])
}
if cur > ans {
ans = cur
}
}
return ans
}
统计完全子数组的数目
滑动窗口:先找到一个l和r,使得[l,r]内的nums恰好满足完全子数组,则[0,r]也一定满足完全子数组的条件,l+1则为右边包含[l,r]的完全子数组的数目。遍历r,把所有解都加上。
func countCompleteSubarrays(nums []int) int {
vis := make(map[int]struct{}, len(nums))
for _, v := range nums {
vis[v] = struct{}{}
}
num := len(vis)
ans := 0
l := 0
cnt := make(map[int]int, len(nums))
for r := 0; r < len(nums); r++ {
cnt[nums[r]]++
for len(cnt) == num {
x := nums[l]
cnt[x]--
if cnt[x] == 0 {
delete(cnt, nums[l])
}
l++
}
ans += l
}
return ans
}
时间复杂度:O(n)
空间复杂度:O(n)
使循环数组所有元素相等的最少秒数
建立存在的数字对应与其所有位置的哈希映射,map[int][]int,然后遍历计算同一个数字相邻两个位置的距离的一半向下取整,因为是循环的数组,所以需要把该数字第一次出现的位置加n
func minimumSeconds(nums []int) int {
n := len(nums)
if n == 0 || n == 1 {
return 0
}
pos := map[int][]int{}
for i, v := range nums {
pos[v] = append(pos[v], i)
}
ans := math.MaxInt
for _, v := range pos {
v = append(v, v[0]+n)
max := 0
for i := 1; i < len(v); i++ {
cur := (v[i] - v[i-1]) / 2
if cur > max {
max = cur
}
}
if ans > max {
ans = max
}
}
return ans
}
时间复杂度:O(n)
空间复杂度:O(n)
判断是否能拆分数组
解1:一个个删除元素
func canSplitArray(nums []int, m int) bool {
sum := 0
for _, v := range nums {
sum += v
}
l, r := 0, len(nums)-1
for l < r && l != r-1 {
if nums[l]+nums[l+1] < nums[r]+nums[r-1] {
sum -= nums[l]
if sum < m {
return false
}
l++
} else {
sum -= nums[r]
if sum < m {
return false
}
r--
}
}
return true
}
解2:
func canSplitArray(nums []int, m int) bool {
n := len(nums)
if n <= 2 {
return true
}
for i := 1; i < n; i++ {
if nums[i-1]+nums[i] >= m {
return true
}
}
return false
}
n<=2时,满足要求
n>=3时,无论怎么分割,一定会在某个时刻,分割出一个长为2连续的子数组,如果nums中所有连续的长度为2的子数组都小于m,那么无法满足要求,否则,如果有一个连续的长度为2的子数组大于等于m,我们可以把这个子数组留到最后分割。其它的元素可以一个个的单独分离出来,剩下的部分因为包含大于等于m的子数组,所以也是大于等于m的,肯定是满足要求的
树的子结构
func isSubStructure(A *TreeNode, B *TreeNode) bool {
if B == nil {
return false
}
var dfs func(cur *TreeNode, Subcur *TreeNode) bool
dfs = func(cur *TreeNode, Subcur *TreeNode) bool {
if Subcur == nil {
return true
}
if cur == nil {
return false
}
if cur.Val == Subcur.Val {
if dfs(cur.Left, Subcur.Left) && dfs(cur.Right, Subcur.Right) {
return true
}
}
if dfs(cur.Left, B) || dfs(cur.Right, B) {
return true
}
return false
}
return dfs(A, B)
}
数组中第K大的最大元素
func solve(nums []int, l, r int, index int) int {
//fmt.Println("before:", nums, l, r, index)
cur := nums[l]
ll, rr := l, r-1
for ll < rr {
for ll < rr && nums[rr] > cur {
rr--
}
if ll < rr {
nums[ll] = nums[rr]
}
for ll < rr && nums[ll] <= cur {
ll++
}
if ll < rr {
nums[rr] = nums[ll]
}
}
nums[ll] = cur
right := r - ll
//fmt.Println("after:", nums, l, r, ll, index)
if right == index {
return cur
}
if right > index {
return solve(nums, ll+1, r, index)
} else {
return solve(nums, l, ll, index-right)
}
}
func findKthLargest(nums []int, k int) int {
return solve(nums, 0, len(nums), k)
}
找出美丽数组的最小和
func minimumPossibleSum(n int, target int) int64 {
var cur int64 = 1
vis := make(map[int64]struct{}, n*5)
var ans int64
for i := 0; i < n; i++ {
_, ok := vis[cur]
for ok {
cur++
_, ok = vis[cur]
}
ans += cur
vis[int64(target)-cur] = struct{}{}
vis[cur] = struct{}{}
cur++
}
return ans
}
统计趣味子数组的数目
前缀和+哈希表
(sum[r+1]-sum[l])%module=k
sum[l]%module=(sum[r+1]-k)%module
func countInterestingSubarrays(nums []int, modulo int, k int) int64 {
cnt := make(map[int]int64, len(nums))
num := make([]int, len(nums))
var ans int64
cnt[0] = 1
for i, v := range nums {
if v%modulo == k {
if i == 0 {
num[i] = 1
} else {
num[i] = num[i-1] + 1
}
} else {
if i == 0 {
num[i] = 0
} else {
num[i] = num[i-1]
}
}
ans += cnt[(num[i]-k)%modulo]
cnt[num[i]%modulo]++
}
return ans
}
二叉搜索树的后序遍历序列
func verifyPostorder(postorder []int) bool {
n := len(postorder)
if n == 0 {
return true
}
var dfs func(l, r, min, max int) bool
dfs = func(l, r, min, max int) bool {
if l > r {
return true
} else if l == r {
return postorder[l] > min && postorder[l] < max
}
cur := postorder[r]
if cur > max || cur < min {
return false
}
i := l
for i < r && postorder[i] < cur {
i++
}
return dfs(l, i-1, min, cur) && dfs(i, r-1, cur, max)
}
return dfs(0, n-1, math.MinInt, math.MaxInt)
}
时间复杂度O(n^2)
空间复杂度O(n)
实现Trie(前缀树)
type Trie struct {
children [26]*Trie
isend bool
}
func Constructor() Trie {
return Trie{}
}
func (this *Trie) Insert(word string) {
cur := this
for _, v := range word {
if cur.children[v-'a'] == nil {
cur.children[v-'a'] = &Trie{}
}
cur = cur.children[v-'a']
}
cur.isend = true
}
func (this *Trie) Search(word string) bool {
cur := this
for _, v := range word {
if cur.children[v-'a'] == nil {
return false
}
cur = cur.children[v-'a']
}
return cur.isend
}
func (this *Trie) StartsWith(prefix string) bool {
cur := this
for _, v := range prefix {
if cur.children[v-'a'] == nil {
return false
}
cur = cur.children[v-'a']
}
return true
}
最大正方形
func maximalSquare(matrix [][]byte) int {
ans := 0
if len(matrix) == 0 || len(matrix[0]) == 0 {
return ans
}
n, m := len(matrix), len(matrix[0])
dp:=make([][]int,n)
for i:=0;i<n;i++{
dp[i]=make([]int,m)
for j:=0;j<m;j++{
if matrix[i][j]=='1'{
ans=1
dp[i][j]=1
}
}
}
for i:=1;i<n;i++{
for j:=1;j<m;j++{
if matrix[i][j]=='1'{
dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1
ans=max(ans,dp[i][j])
}
}
}
return ans*ans
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
时间复杂度:O(nm)
空间复杂度:O(nm)
解码方法
func toNum(ch1, ch2 byte) int {
return int((ch1-'0')*10 + ch2 - '0')
}
func numDecodings(s string) int {
n := len(s)
if n == 1 {
if s != "0" {
return 1
}
return 0
}
one, two := 1, 1
var cur int
for i := 0; i < n; i++ {
if s[i] == '0' {
if i == 0 || toNum(s[i-1], s[i]) > 26 || toNum(s[i-1], s[i]) == 0 {
return 0
}
cur = one
} else {
if i != 0 && s[i-1] == '0' {
cur = two
} else {
cur = two
if i != 0 && toNum(s[i-1], s[i]) <= 26 {
cur += one
}
}
}
one, two = two, cur
}
return cur
}
时间复杂度:O(n)
空间复杂度:O(1)
注意边界条件:
当前s[i]为’0’时,就要求i-1必须>=0,并且s[i-1]和s[i]组成的数字小于等于26
当前s[i]不为’0’,且存在s[i-1]为‘0’时,那么当前的s[i]只能单独解码,答案等于s[i-2]处的答案值
当前s[i]不为’0’且s[i-1]不为‘0’时,当前答案继承s[i-1]的答案值,除此之外,如果s[i-1]与s[i]组成的数字小于等于26,那么可以把s[i-1]和s[i]作为整体,继承s[i-2]处的值
打家劫舍Ⅱ
func max(x, y int) int {
if x > y {
return x
}
return y
}
func solve(nums []int) int {
n := len(nums)
if n == 0 {
return 0
} else if n == 1 {
return nums[0]
} else if n == 2 {
return max(nums[0], nums[1])
}
one, two := nums[0], max(nums[0], nums[1])
for i := 2; i < n; i++ {
one, two = two, max(one+nums[i], two)
}
return max(one, two)
}
func rob(nums []int) int {
n := len(nums)
if n == 0 {
return 0
} else if n == 1 {
return nums[0]
} else if n == 2 {
return max(nums[0], nums[1])
}
return max(solve(nums[1:]), solve(nums[:n-1]))
}
打家劫舍Ⅲ
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
func rob(root *TreeNode) int {
var dfs func(cur *TreeNode) (int, int)
dfs = func(cur *TreeNode) (int, int) {
if cur == nil {
return 0, 0
}
l, ll := dfs(cur.Left)
r, rr := dfs(cur.Right)
include, exclude := max(ll+rr+cur.Val, l+r), l+r
return include, exclude
}
include, exclude := dfs(root)
return max(include, exclude)
}
打家劫舍Ⅳ
func is_ok(nums []int, key int, num int) bool {
last := -1
cnt := 0
for i, v := range nums {
if v <= key {
if last == -1 || i != last+1 {
last = i
cnt++
}
}
}
return cnt >= num
}
func minCapability(nums []int, k int) int {
l, r := math.MaxInt, math.MinInt
for _, v := range nums {
if v < l {
l = v
}
if v > r {
r = v
}
}
var mid int
for l < r {
mid = (l + r) >> 1
if is_ok(nums, mid, k) {
r = mid
} else {
l = mid + 1
}
}
return l
}
二分查找
搜索二维矩阵Ⅱ
func searchMatrix(matrix [][]int, target int) bool {
n := len(matrix)
if n == 0 {
return false
}
m := len(matrix[0])
row, col := 0, m-1
for row < n {
for col >= 0 && matrix[row][col] > target {
col--
}
if col < 0 {
return false
}
if matrix[row][col] == target {
return true
}
row++
}
return false
}
从第一行最右边开始遍历,找到第一个小于等于target的值的小标index,等于则直接返回true,index~m对应的列之后可以不用看了,因为第一行已经大于target,越往下的行越大,不可能 小于等于target,所以下一行时只需要看0~index,往后的行重复第一行的操作,不断排除列,对比行数据
时间复杂度:O(n+m)
完全平方数
func min(x,y int)int{
if x<y{
return x
}
return y
}
func numSquares(n int) int {
is_ok := make(map[int]bool, n)
for i := 1; i <= 100; i++ {
is_ok[i*i] = true
}
dp := make([]int, n+1)
dp[1] = 1
for i := 2; i <= n; i++ {
if is_ok[i] {
dp[i] = 1
continue
}
dp[i] = math.MaxInt
for j := 1; j*j < i; j++ {
dp[i] = min(dp[i], dp[i-j*j]+1)
}
}
return dp[n]
}
动态规划:(nsqrt(n))
空间复杂度O(n)
寻找重复数
func findDuplicate(nums []int) int {
n := len(nums)
l, r := 1, n-1
var mid int
var is_ok func(cur int) bool
is_ok = func(cur int) bool {
cnt := 0
for i := 0; i < n; i++ {
if nums[i] <= cur {
cnt++
}
}
return cur < cnt
}
for l < r {
mid = (l + r) >> 1
if is_ok(mid) {
r = mid
} else {
l = mid + 1
}
}
return l
}
二分法,时间复杂度O(nlogn),空间复杂度O(1)
[l,r]中二分一个数mid,判断[1,mid]区间的数字个数cnt,如果cnt>mid,说明答案的值在[1,mid]区间中,尝试在[l,mid]区间寻找解。如果cnt<=mid,说明要求的值大于mid,则在[mid+1,r]中寻找解。
将钱分给最多的儿童
func distMoney(money int, children int) int {
money -= children
if money < 0 {
return -1
}
ans := money / 7
left := money % 7
if ans > children {
return children - 1
}
if (left == 3 && ans == children-1) || (left != 0 && ans == children) {
return ans - 1
}
return ans
}
树上的操作
type LockingTree struct {
parents []int
children [][]int
locked map[int]int
}
func Constructor(parent []int) LockingTree {
lt := LockingTree{
parents: make([]int, len(parent)),
children: make([][]int, len(parent)),
locked: make(map[int]int, len(parent)),
}
for i := 0; i < len(parent); i++ {
lt.children[i] = []int{}
}
for i, v := range parent {
lt.parents[i] = v
if v != -1 {
lt.children[v] = append(lt.children[v], i)
}
lt.locked[i] = -1
}
return lt
}
func (this *LockingTree) Lock(num int, user int) bool {
if this.locked[num] != -1 {
return false
}
this.locked[num] = user
return true
}
func (this *LockingTree) Unlock(num int, user int) bool {
if this.locked[num] == -1 || this.locked[num] != user {
return false
}
this.locked[num] = -1
return true
}
func (this *LockingTree) Upgrade(num int, user int) bool {
if this.locked[num] != -1 {
return false
}
queue := []int{}
queue = append(queue, this.children[num]...)
for len(queue) != 0 {
cur := queue[0]
if this.locked[cur] != -1 {
break
}
queue = queue[1:]
for _, v := range this.children[cur] {
queue = append(queue, v)
}
}
if len(queue) == 0 {
return false
}
p := this.parents[num]
for p != -1 {
if this.locked[p] != -1 {
return false
}
p = this.parents[p]
}
this.locked[num] = user
queue = []int{}
queue = append(queue, this.children[num]...)
for len(queue) != 0 {
cur := queue[0]
this.locked[cur] = -1
queue = queue[1:]
for _, v := range this.children[cur] {
queue = append(queue, v)
}
}
return true
}
餐厅过滤器
func filterRestaurants(restaurants [][]int, veganFriendly int, maxPrice int, maxDistance int) []int {
var index int
var n int
n = len(restaurants)
if veganFriendly == 1 {
for i := 0; i < n; i++ {
if restaurants[i][2] == 1 && restaurants[i][3] <= maxPrice && restaurants[i][4] <= maxDistance {
restaurants[index] = restaurants[i]
index++
}
}
} else {
for i := 0; i < n; i++ {
if restaurants[i][3] <= maxPrice && restaurants[i][4] <= maxDistance {
restaurants[index] = restaurants[i]
index++
}
}
}
restaurants = restaurants[:index]
sort.Slice(restaurants, func(i, j int) bool {
if restaurants[i][1] == restaurants[j][1] {
return restaurants[i][0] > restaurants[j][0]
}
return restaurants[i][1] > restaurants[j][1]
})
ans := make([]int, index)
for i := 0; i < index; i++ {
ans[i] = restaurants[i][0]
}
return ans
}
时间复杂度:O(nlogn)
空间复杂度:O(1)
买卖股票的最佳时机含冷冻期
buy,sell,freeze分别代表当前含有一张股票的累计最高收益,sell代表当前不含有股票且不在冷冻期的累计最高收益,freeze代表当前不含有股票且在冷冻期的累计最高收益
func max(x, y int) int {
if x < y {
return y
}
return x
}
func maxProfit(prices []int) int {
n := len(prices)
if n <= 1 {
return 0
}
buy, sell, freeze := -prices[0], 0, 0
for i := 1; i < n; i++ {
buy, sell, freeze = max(buy, sell-prices[i]), max(freeze, sell), prices[i]+buy
}
return max(sell, freeze)
}
时间复杂度:O(n)
空间复杂度:O(1)
买卖股票的最佳时机含手续费
func max(x, y int) int {
if x < y {
return y
}
return x
}
func maxProfit(prices []int, fee int) int {
n := len(prices)
if n <= 1 {
return 0
}
buy, sell := -prices[0], 0
for i := 1; i < n; i++ {
buy, sell = max(sell-prices[i], buy), max(sell, buy+prices[i]-fee)
}
return sell
}
时间复杂度:O(n)
空间复杂度:O(1)
股票价格跨度
单调栈:
type Pair struct {
key int
sum int
}
type StockSpanner struct {
queue []Pair
}
func Constructor() StockSpanner {
return StockSpanner{
[]Pair{},
}
}
func (this *StockSpanner) Next(price int) int {
n := len(this.queue) - 1
cur := n
sum := 1
for cur >= 0 && this.queue[cur].key <= price {
sum += this.queue[cur].sum
cur--
}
this.queue = this.queue[:cur+1]
this.queue = append(this.queue, Pair{sum: sum, key: price})
return sum
}
时间复杂度:O(n)
空间复杂度:O(n)
只出现一次的数字Ⅱ
func singleNumber(nums []int) int {
ans := int32(0)
for i := 0; i < 32; i++ {
total := int32(0)
for _, v := range nums {
total += (int32(v) >> i) & 1
}
if total%3 != 0 {
ans |= 1 << i
}
}
return int(ans)
}
执行K次操作后的最大分数
type PriorityQueue []int
func (p PriorityQueue) Len() int {
//TODO implement me
return len(p)
}
func (p PriorityQueue) Less(i, j int) bool {
//TODO implement me
return p[i] > p[j]
}
func (p PriorityQueue) Swap(i, j int) {
//TODO implement me
p[i], p[j] = p[j], p[i]
}
func (p *PriorityQueue) Push(x any) {
//TODO implement me
*p = append(*p, x.(int))
}
func (p *PriorityQueue) Pop() any {
//TODO implement me
n := len(*p)
x := (*p)[n-1]
*p = (*p)[:n-1]
return x
}
func maxKelements(nums []int, k int) int64 {
q := PriorityQueue(nums)
heap.Init(&q)
var ans int64
for i := 0; i < k; i++ {
x := heap.Pop(&q).(int)
ans += int64(x)
heap.Push(&q, (x+2)/3)
}
return ans
}
统计无向图中无法互相到达点对数
var f []int
func fin(x int) int {
if x != f[x] {
f[x] = fin(f[x])
}
return f[x]
}
func bind(x, y int) {
fx, fy := fin(x), fin(y)
if fx < fy {
f[fy] = fx
} else {
f[fx] = fy
}
}
func countPairs(n int, edges [][]int) int64 {
var ans int64
f = make([]int, n)
for i := 0; i < n; i++ {
f[i] = i
}
for i := 0; i < len(edges); i++ {
bind(edges[i][0], edges[i][1])
}
cnt := make(map[int]int, n)
for i := 0; i < n; i++ {
cnt[fin(i)]++
}
for _, v := range cnt {
ans = ans + int64(v*(n-v))
n -= v
}
return ans
}
掷骰子等于目标和的方法数
const mod = 1e9 + 7
func max(x, y int) int {
if x > y {
return x
}
return y
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
func numRollsToTarget(n int, k int, target int) int {
cur := make([]int, target+1)
before := make([]int, target+1)
if n == 1 {
if k < target {
return 0
} else {
return 1
}
}
for i := 1; i <= min(k, target); i++ {
before[i] = 1
}
for i := 1; i < n; i++ {
for j := 1; j <= target; j++ {
cur[j] = 0
for l := max(1, j-k); l < j; l++ {
cur[j] = (cur[j] + before[l]) % mod
}
}
copy(before, cur)
}
return before[target]
}
空间复杂度:O(target)
时间复杂度:O(ntargetk)
求一个整数的惩罚数
func is_ok(square int, cur int) bool {
if square == 0 {
return cur == 0
}
if cur < 0 {
return false
}
if square == cur {
return true
}
for i := 10; i <= square; i *= 10 {
if is_ok(square/i, cur-square%i) {
return true
}
}
return false
}
func punishmentNumber(n int) int {
ans := 0
for i := 1; i <= n; i++ {
if is_ok(i*i, i) {
ans += i * i
}
}
return ans
}
时间复杂度:O(n*log(n))
空间复杂度:O(log(n))
数组中两个数的最大异或值
func findMaximumXOR(nums []int) int {
ans := 0
for i := 30; i >= 0; i-- {
seen := map[int]bool{}
for _, num := range nums {
seen[num>>i] = true
}
found := false
ans = ans*2 + 1
for _, num := range nums {
if seen[num>>i^ans] {
found = true
break
}
}
if !found {
ans -= 1
}
}
return ans
}
a^b=ans
b=a^ans
维护一个ans,从最高位开始,每次标记一下nums中的数从最高位到当前位的一个哈希表,每次尝试让ans中的该位为1,去从哈希表中找b,找到则该位可以是1,否则则必须是0。直到遍历到最低位。
时间复杂度:O(NlogC) c是元素范围
空间复杂度:O(N)
重复的DNA序列
func findRepeatedDnaSequences(s string) []string {
n := len(s)
if n <= 10 {
return nil
}
x := 0
sTon := map[byte]int{'A': 0, 'C': 1, 'G': 2, 'T': 3}
for i := 0; i < 10; i++ {
x = x<<2 | sTon[s[i]]
}
ans := []string{}
cnt := map[int]int{}
for i := 10; i < n; i++ {
cnt[x]++
x = (x<<2 | sTon[s[i]]) & (1<<20 - 1)
if cnt[x] == 1 {
ans = append(ans, s[i-9:i+1])
}
}
return ans
}
可以把每一个字母编码为00,01,10,11
然后用20位的数来存储长度为10的字符串的状态,每次通过位运算来获取下一个字符串的值
时间复杂度:O(N)
空间复杂度:O(N)
最大单词长度乘积
func max(x, y int) int {
if x > y {
return x
}
return y
}
func maxProduct(words []string) int {
nums := make([]int, len(words))
for index, v := range words {
for _, v2 := range v {
nums[index] |= 1 << (v2 - 'a')
}
}
ans := 0
for i := 0; i < len(words); i++ {
for j := 0; j < i; j++ {
if nums[i]&nums[j] == 0 {
ans = max(ans, len(words[i])*len(words[j]))
}
}
}
return ans
}
时间复杂度O(N*max(N,L))
空间复杂度:O(N)
区域和检索
type NumArray struct {
nums []int
sum []int
size int
}
func Constructor(nums []int) NumArray {
size:=int(math.Sqrt(float64(len(nums))))+1
sum:=make([]int,size)
for i,v:=range nums{
sum[i/size]+=v
}
return NumArray{
nums: nums,
sum: sum,
size: size,
}
}
func (this *NumArray) Update(index int, val int) {
sum_index:=index/this.size
if sum_index>this.size {
return
}
this.sum[sum_index]-=(this.nums[index]-val)
this.nums[index]=val
}
func (this *NumArray) SumRange(left int, right int) int {
sum:=0
lsum_index,rsum_index:=left/this.size,right/this.size
if lsum_index==rsum_index {
for i:=left;i<=right;i++ {
sum+=this.nums[i]
}
}else {
for i := left; i < (lsum_index+1)*this.size && i < len(this.nums); i++ {
sum += this.nums[i]
}
for i := lsum_index + 1; i < rsum_index&& i < len(this.sum); i++ {
sum += this.sum[i]
}
for i := rsum_index * this.size; i <= right && i < len(this.nums); i++ {
sum += this.nums[i]
}
}
return sum
}
分块查找
时间复杂度:O(sqrt(n))
空间复杂度:O(n)
避免洪水泛滥
func avoidFlood(rains []int) []int {
n := len(rains)
queue := []int{}
ans := make([]int, n)
vis := make(map[int]int, n)
for i := 0; i < n; i++ {
if rains[i] > 0 {
v, ok := vis[rains[i]]
if ok {
it := sort.SearchInts(queue, v)
if it == len(queue) {
return nil
}
ans[queue[it]] = rains[i]
queue = append(queue[:it], queue[it+1:]...)
}
vis[rains[i]] = i
ans[i] = -1
} else {
queue = append(queue, i)
}
}
for i := 0; i < len(queue); i++ {
ans[queue[i]] = 1
}
return ans
}
时间复杂度:O(nlogn)
空间复杂度:O(n)
思路:贪心+二分
维护一个map来映射i号湖泊满水时的时间点
维护一个int切片存储没有下雨的时间点
遍历时间点,对于每一次下雨的湖泊,如果这个湖泊没有下过雨,则直接记录当前下雨时间点。如果之前下过雨,则需要从没有下雨的时间点中挑选一个时间点来清空水,这个时间点不能早于上一次满水时的时间点。这个时间点的选择很重要,优先应该选择尽量小的时间点,采用二分的思路。因为时间点是往后遍历的,越大的时间点在之后遇到同样满水时满足条件的概率越大。
子数组的最小值之和
const mod = 1e9 + 7
func sumSubarrayMins(arr []int) int {
queue := []int{}
left := make([]int, len(arr))
right := make([]int, len(arr))
for i := 0; i < len(arr); i++ {
for len(queue) > 0 && arr[queue[len(queue)-1]] >= arr[i] {
queue = queue[:len(queue)-1]
}
if len(queue) == 0 {
left[i] = i + 1
} else {
left[i] = i - queue[len(queue)-1]
}
queue = append(queue, i)
}
queue = []int{}
for i := len(arr) - 1; i >= 0; i-- {
for len(queue) > 0 && arr[queue[len(queue)-1]] > arr[i] {
queue = queue[:len(queue)-1]
}
if len(queue) == 0 {
right[i] = len(arr) - i
} else {
right[i] = queue[len(queue)-1] - i
}
queue = append(queue, i)
}
ans := 0
for i, v := range arr {
ans = (ans + right[i]*left[i]*v) % mod
}
return ans
}
时间复杂度:O(n)
空间复杂度:O(n)
维护一个单调递增栈然后遍历记录当前index为结尾时最小的长度以及为首时最小的长度。
可获得的最大点数
func min(x, y int) int {
if x < y {
return x
}
return y
}
func maxScore(cardPoints []int, k int) int {
left := len(cardPoints) - k
sum := 0
cur := 0
minSum := math.MaxInt
for i := 0; i <= len(cardPoints); i++ {
if i >= left {
if cur < minSum {
minSum = cur
}
if i == len(cardPoints) {
break
}
cur -= cardPoints[i-left]
}
if i == len(cardPoints) {
break
}
cur += cardPoints[i]
sum += cardPoints[i]
}
return sum - minSum
}
求k个数的最大值,也就是求数组剩余的n-k的最小值。因为n-k是连续的,所以可以通过滑动窗口的思路来解决
时间复杂度:O(n)
空间复杂度:O(1)
到达首都的最小油耗
func minimumFuelCost(roads [][]int, seats int) int64 {
roadSlice := make([][]int, len(roads)+1)
for _, v := range roads {
roadSlice[v[0]] = append(roadSlice[v[0]], v[1])
roadSlice[v[1]] = append(roadSlice[v[1]], v[0])
}
var ans int64
var dfs func(cur, pre int) int64
dfs = func(cur, pre int) int64 {
var sum int64
sum = 1
for _, to := range roadSlice[cur] {
if to == pre {
continue
}
tmp := dfs(to, cur)
ans += (tmp + int64(seats) - 1) / int64(seats)
sum += tmp
}
return sum
}
dfs(0, -1)
return ans
}
思路:深度优先遍历,从深度最深的地方,依次往上层聚集,并在答案中加上下一层聚集到上一层的油耗floor(总人数/最大载的人数),聚集后,把聚集后的人的总数返回给上层,然后上层的总数就会加上上一层聚集的总数。
重新规划路线
type toPair struct {
to int
val int
}
func minReorder(n int, connections [][]int) int {
pairMap := make([][]*toPair, n)
for _, v := range connections {
pairMap[v[0]] = append(pairMap[v[0]], &toPair{v[1], 1})
pairMap[v[1]] = append(pairMap[v[1]], &toPair{v[0], 0})
}
var dfs func(x, pre int) int
dfs = func(x, pre int) int {
ret := 0
for _, v := range pairMap[x] {
if v.to == pre {
continue
}
ret += v.val + dfs(v.to, x)
}
return ret
}
return dfs(0, -1)
}
思路也是一样,深度搜索,一层一层的往上聚集。如果不用修改箭头就能走到则距离为0,如果需要修改箭头才能走到则距离为1
出租车的最大利润
解法1:动态规划+二分查找
func max(i, j int64) int64 {
if i < j {
return j
}
return i
}
func maxTaxiEarnings(n int, rides [][]int) int64 {
m := len(rides)
sort.Slice(rides, func(i, j int) bool {
return rides[i][1] < rides[j][1]
})
dp := make([]int64, m)
for i := 0; i < m; i++ {
index := sort.Search(i, func(k int) bool {
return rides[k][1] > rides[i][0]
})
if i == 0 {
dp[i] = int64(rides[i][1] - rides[i][0] + rides[i][2])
} else {
if index == 0 {
dp[i] = max(dp[i-1], int64(rides[i][1]-rides[i][0]+rides[i][2]))
} else {
dp[i] = max(dp[i-1], dp[index-1]+int64(rides[i][1]-rides[i][0]+rides[i][2]))
}
}
}
return dp[m-1]
}
dp[i]代表接的范围是乘客i的范围的最大利润,然后每次都可以二分查找满足接了当前乘客,又可以在这之前乘车的用户index,然后更新最大值
时间复杂度:O(mlogm)
空间复杂度:O(m)
解法2:动态规划+哈希表
func max(i, j int64) int64 {
if i < j {
return j
}
return i
}
func maxTaxiEarnings(n int, rides [][]int) int64 {
priceMap:=make(map[int][][]int,n)
for i:=0;i<len(rides);i++{
priceMap[rides[i][1]]=append(priceMap[rides[i][1]],rides[i])
}
dp:=make([]int64,n+1)
for i:=0;i<=n;i++{
for _,v:=range priceMap[i]{
dp[i]=max(dp[i],dp[v[0]]+int64(v[1]-v[0]+v[2]))
}
if i>0{
dp[i]=max(dp[i],dp[i-1])
}
}
return dp[n]
}
dp[i]代表运送到位置i的最大利润,用哈希表建立终点和ride值的映射,对于每一个位置i,只需要从哈希表查找终点为i的所有ride,然后根据每一个ride起点的最大利润加上当前ride的最大利润。
时间复杂度:O(n+m)
空间复杂度:O(n+m)
环形链表Ⅱ
func detectCycle(head *ListNode) *ListNode {
fast,slow:=head,head
for fast!=nil{
fast=fast.Next
if fast==nil{
return nil
}
fast=fast.Next
slow=slow.Next
if fast==slow{
break
}
}
if fast==nil{
return nil
}
tmp:=head
for tmp!=fast{
tmp=tmp.Next
fast=fast.Next
}
return tmp
}
马拉车算法
删除链表的倒数第n个节点
func removeNthFromEnd(head *ListNode, n int) *ListNode {
fast,slow:=head,head
for i:=0;i<n;i++{
if fast==nil{
return head
}
fast=fast.Next
}
if fast==nil{
head=head.Next
return head
}
fast=fast.Next
for fast!=nil{
fast=fast.Next
slow=slow.Next
}
slow.Next=slow.Next.Next
return head
}
寻找峰值
func findPeakElement(nums []int) int {
l,r:=0,len(nums)
getNum:=func(i int)int{
if i==-1||i==len(nums){
return math.MinInt
}
return nums[i]
}
for l<=r{
mid:=(l+r)>>1
if getNum(mid)>getNum(mid+1)&&getNum(mid)>getNum(mid-1){
return mid
}else if getNum(mid)<getNum(mid+1){
l=mid+1
}else{
r=mid-1
}
}
return -1
}
思路:对于下标i,如果nums[i]<nums[i+1],则[i+1,n]之间一定有一个符合条件的值,如果nums[i]<nums[i-1],则[0,i-1]中一定有一个满足条件的值。一直往更大的方向走一定能找到值,可以用二分的思路寻找答案。
美丽塔Ⅱ
func maximumSumOfHeights(maxHeights []int) int64 {
if len(maxHeights) == 0 {
return 0
}
stack1 := []int{}
stack2 := []int{}
preffixSum := make([]int64, len(maxHeights))
suffixSum := make([]int64, len(maxHeights))
for i := 0; i < len(maxHeights); i++ {
for len(stack1) > 0 && maxHeights[stack1[len(stack1)-1]] > maxHeights[i] {
stack1 = stack1[:len(stack1)-1]
}
if len(stack1) == 0 {
preffixSum[i] = int64(maxHeights[i] * (i + 1))
} else {
preffixSum[i] = preffixSum[stack1[len(stack1)-1]] + int64((i-stack1[len(stack1)-1])*maxHeights[i])
}
stack1 = append(stack1, i)
}
for i := len(maxHeights) - 1; i >= 0; i-- {
for len(stack2) > 0 && maxHeights[stack2[len(stack2)-1]] > maxHeights[i] {
stack2 = stack2[:len(stack2)-1]
}
if len(stack2) == 0 {
suffixSum[i] = int64(maxHeights[i] * (len(maxHeights) - i))
} else {
suffixSum[i] = suffixSum[stack2[len(stack2)-1]] + int64((stack2[len(stack2)-1]-i)*maxHeights[i])
}
stack2 = append(stack2, i)
}
var ans int64
for i := 0; i < len(maxHeights); i++ {
cur := preffixSum[i] + suffixSum[i] - int64(maxHeights[i])
if cur > ans {
ans = cur
}
}
return ans
}
思路:单调栈+前后缀和
时间复杂度:O(n)
空间复杂度:O(n)
数组的逆序对
const mod=1000000007
func InversePairs( nums []int ) int {
// write code here
n:=len(nums)
if n<=0{
return 0
}
ans:=0
var mergeSort func(l,r int)
mergeSort=func(l, r int) {
if l>=r{
return
}
mid:=(l+r)>>1
mergeSort(l,mid)
mergeSort(mid+1,r)
sortedNums:=make([]int, r-l+1)
k:=0
cur1,cur2:=l,mid+1
for cur1<=mid&&cur2<=r{
if nums[cur1]<=nums[cur2]{
sortedNums[k]= nums[cur1]
cur1++
}else{
sortedNums[k]=nums[cur2]
ans=(ans+mid-cur1+1)%mod
cur2++
}
k++
}
for cur1<=mid{
sortedNums[k]=nums[cur1]
k++
cur1++
}
for cur2<=r{
sortedNums[k]=nums[cur2]
k++
cur2++
}
for i:=l;i<=r;i++{
nums[i]=sortedNums[i-l]
}
return
}
mergeSort(0,n-1)
return ans
}
归并排序思想。当第二块小于第一块中的某个位置时,第一块的该位置一直到末尾都是大于该位置的。答案加上第一块该位置到末尾的长度
统计结果概率
func statisticsProbability(num int) []float64 {
beforeDp:=make([]int64,6*num+1)
dp:=make([]int64,6*num+1)
beforeDp[0]=1
for i:=0;i<num;i++{
for k:=1;k<=6*(i+1);k++{
dp[k]=0
}
for j:=6*(i+1);j>0;j--{
for k:=1;k<=6;k++{
if j-k>=0{
dp[j]+=beforeDp[j-k]
}
}
}
copy(beforeDp,dp)
}
ans:=make([]float64,5*num+1)
var sum int64
for i:=1;i<6*num+1;i++{
sum+=dp[i]
}
for i:=num;i<6*num+1;i++{
ans[i-num]=float64(dp[i])/float64(sum)
}
return ans
}
时间复杂度O(n^n)
空间复杂度O(n)
收集巧克力
func min(x,y int)int{
if x>y{
return y
}
return x
}
func minCost(nums []int, x int) int64 {
dp:=make([]int,len(nums))
var ans int64
for i:=0;i<len(nums);i++{
dp[i]=nums[i]
ans+=int64(nums[i])
}
for i:=1;i<len(nums);i++{
var sum int64
for j:=0;j<len(nums);j++{
dp[j]=min(dp[j],nums[(j-i+len(nums))%len(nums)])
sum+=int64(dp[j])
}
if sum+int64(i*x)<ans{
ans=sum+int64(i*x)
}
}
return ans
}
时间复杂度:O(n^n)
空间复杂度:O(n)
思路:枚举操作次数,计算和,更新答案
插入区间
func insert(intervals [][]int, newInterval []int) [][]int {
left, right := newInterval[0], newInterval[1]
ans := [][]int{}
cur := 0
for cur < len(intervals) && intervals[cur][1] < left {
ans = append(ans, intervals[cur])
cur++
}
for cur < len(intervals) && intervals[cur][0] <= right {
if intervals[cur][1] > right {
right = intervals[cur][1]
}
if intervals[cur][0] < left {
left = intervals[cur][0]
}
cur++
}
ans = append(ans, []int{left, right})
for cur < len(intervals) && intervals[cur][0] > right {
ans = append(ans, intervals[cur])
cur++
}
return ans
}
从链表中移除节点
方法1:递归
func removeNodes(head *ListNode) *ListNode {
var dfs func(cur *ListNode)(*ListNode,int)
dfs=func(cur *ListNode)(*ListNode,int){
if cur==nil{
return nil,-1
}
right,v:=dfs(cur.Next)
if v>cur.Val{
return right,v
}
cur.Next=right
return cur,cur.Val
}
head,_=dfs(head)
return head
}
时间复杂度:O(n)
空间复杂度:O(n)
解法2:反转链表
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNodes(head *ListNode) *ListNode {
var reverse func(cur *ListNode)*ListNode
reverse=func(cur *ListNode)*ListNode{
if cur==nil{
return cur
}
cur2:=cur.Next
cur.Next=nil
for cur2!=nil{
tmp:=cur2.Next
cur2.Next=cur
cur=cur2
cur2=tmp
}
return cur
}
cur:=reverse(head)
mx:=-1
cur2:=cur
for cur2!=nil&&cur2.Next!=nil{
if cur2.Val>mx{
mx=cur2.Val
}
if cur2.Next.Val<mx{
cur2.Next=cur2.Next.Next
}else{
cur2=cur2.Next
}
}
head=reverse(cur)
return head
}
先反转链表,再从头到尾维护一个最大的值来判断是否需要将其删除
被列覆盖的最多行数
func maximumRows(matrix [][]int, numSelect int) int {
m:=len(matrix)
if m==0{
return 0
}
n:=len(matrix[0])
rows:=make([]int,m)
for i:=0;i<m;i++{
for j:=0;j<n;j++{
rows[i]|=(matrix[i][j]<<j)
}
}
ans:=0
for cur:=0;cur<(1<<n);cur++{
if bits.OnesCount(uint(cur)) != numSelect{
continue
}
cnt:=0
for i:=0;i<m;i++{
if (rows[i]&cur)==rows[i]{
cnt++
}
}
if cnt>ans{
ans=cnt
}
}
return ans
}
切片存储每一行组成的二进制数,遍历可以选择的所有列的二进制表示,然后根据存储的每一行的二进制数进行&运算进行判断
蛇梯棋
func max(x, y int) int {
if x > y {
return x
}
return y
}
type Pos struct{
val int
step int
}
func snakesAndLadders(board [][]int) int {
n := len(board)
if n == 0 {
return 0
}
var idToPos func(cur int) (int, int)
idToPos = func(cur int) (int, int) {
if cur > n*n || cur <= 0 {
return -1, -1
}
x := n - (cur-1)/n - 1
y := (cur - 1) % n
if (n-x)%2 == 0 {
y = n - 1 - y
}
return x, y
}
queue := []Pos{Pos{val:1}}
vis := make([]bool, n*n+1)
vis[1] = true
for len(queue) > 0 {
m:=len(queue)
for i:=0;i<m;i++{
for j:=1;j<=6;j++{
nxt:=queue[i].val+j
if nxt>n*n{
break
}
x,y:=idToPos(nxt)
if board[x][y]>0{
nxt=board[x][y]
}
if nxt==n*n{
return queue[i].step+1
}
if !vis[nxt]{
vis[nxt]=true
queue=append(queue,Pos{val:nxt,step:queue[i].step+1})
}
}
}
queue=queue[m:]
}
return -1
}
深度优先遍历,到一个点后,尝试在+6的范围内走,如果遇到蛇或者梯子,则直接传送
时间复杂度:O(n)
空间复杂度:O(n)
字符串中的额外字符
func min(x,y int)int{
if x<y{
return x
}
return y
}
func minExtraChar(s string, dictionary []string) int {
dp:=make([]int,len(s)+1)
for i:=1;i<len(s)+1;i++{
dp[i]=i
}
dictMap:=make(map[string]bool,len(dictionary))
for _,v:=range dictionary{
dictMap[v]=true
}
for i:=1;i<=len(s);i++{
for j:=i-1;j>=0;j--{
if dictMap[s[j:i]]{
dp[i]=min(dp[i],dp[j])
}else{
dp[i]=min(dp[i],dp[j]+i-j)
}
}
}
return dp[len(s)]
}
动态规划,dp[i]代表前i个字符能够分割剩余的最小值,对于j<i,如果s[j:i+1]包含在列表中,则s[j:i+1]可以直接移除,dp[i]可以等于dp[j],如果不包含的话,dp[i]可以等于dp[j]+i+1-j,即不排除后面不包含的字符
时间复杂度:O(n)
空间复杂度:O(n)
构造有效字符串的最少插入数
func addMinimum(word string) int {
if len(word)==0{
return 0
}
ans:=0
cur:=0
ch:='c'
for cur<len(word){
if ch=='a'{
if word[cur]!='b'{
ans++
}else{
cur++
}
ch='b'
}else if ch=='b'{
if word[cur]!='c'{
ans++
}else{
cur++
}
ch='c'
}else{
if word[cur]=='c'{
ans+=2
}else if word[cur]=='b'{
ans++
ch='b'
}else{
ch='a'
}
cur++
}
}
if ch=='a'{
ans+=2
}else if ch=='b'{
ans++
}
return ans
}
时间复杂度:O(n)
空间复杂度:O(1)
用一个字符ch记录上一个字符,当上一个字符是a的时候,如果当前字符不是b,则需要添加一个b,如果是的话,则跳过当前字符。b同理。如果上一个字符是c的话,如果当前字符是c,则需要在前面添加ab,如果是a,则不需要添加,如果是b,则需要添加a。每次上一个字符是c,就相当于在当前字符前面填充,填充完跳到当前字符下面的位置,如果上一个字符不是c,则代表从上一字符后面填充
数字范围按位与
func rangeBitwiseAnd(left int, right int) int {
for left!=right{
if left<right{
right=right&(right-1)
}else{
left=left&(left-1)
}
}
return left
}
求[left,right]范围内的数字相与,实际就是求left,right最长公共前缀,因为公共前缀后面的一定会出现0,与后也是0
拿出最少数目的魔法豆
func min(x,y int64)int64{
if x>y{
return y
}
return x
}
func minimumRemoval(beans []int) int64 {
sort.Ints(beans)
var sum int64
for _,v:=range beans{
sum+=int64(v)
}
ans:=sum
for i,v:=range beans{
ans=min(ans,sum-int64(v*(len(beans)-i)))
}
return ans
}
先对所有魔法豆数进行排序,然后记录魔法豆和,对于魔法豆i,i之前的都是小于等于beans[i],这部分是直接移除的,i后面的移除为beans[i],即如果所有魔法豆都成为beans[i],需要移除的数目为sum-(n-i)*beans[i],表示在移除所有魔法豆的基础上减去了不需要移除的数量
时间复杂度:O(n)
空间复杂度:O(1)
交错字符串
func isInterleave(s1 string, s2 string, s3 string) bool {
n,m:=len(s1),len(s2)
if n+m!=len(s3){
return false
}
dp:=make([][]bool,n+1)
for i:=0;i<=n;i++{
dp[i]=make([]bool,m+1)
}
dp[0][0]=true
for i:=0;i<=n;i++{
for j:=0;j<=m;j++{
if i>0{
dp[i][j]=dp[i][j]||dp[i-1][j]&&s1[i-1]==s3[i+j-1]
}
if j>0{
dp[i][j]=dp[i][j]||dp[i][j-1]&&s2[j-1]==s3[i+j-1]
}
}
}
return dp[n][m]
}
dp[i][j]代表s1取前i个,s2取前j个是否能够匹配上。
编辑距离
func min(x,y int)int{
if x<y{
return x
}
return y
}
func minDistance(word1 string, word2 string) int {
n,m:=len(word1),len(word2)
dp:=make([][]int,n+1)
for i:=0;i<=n;i++{
dp[i]=make([]int,m+1)
}
for i:=1;i<=len(word2);i++{
dp[0][i]=i
}
dp[0][0]=0
for i:=1;i<=len(word1);i++{
dp[i][0]=i
for j:=1;j<=len(word2);j++{
if word1[i-1]==word2[j-1]{
dp[i][j]=dp[i-1][j-1]
}else{
dp[i][j]=min(dp[i-1][j-1],min(dp[i][j-1],dp[i-1][j]))+1
}
}
}
return dp[n][m]
}
时间复杂度:O(nm)
空间复杂度:O(nm)
dp[i][j]表示将word1前i个字符转变为word2的前j个字符的最小操作次数,如果word1[i-1]等于word2[j-1],那么dp[i][j]就是dp[i-1][j-1],否则,dp[i][j]等于dp[i-1][j]+1表示删除字符,dp[i-1][j-1]+1表示修改字符,dp[i][j-1]表示增加字符