leetcode(中等)

文章目录

两数相加

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(n
k)

跳跃游戏

解法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(m
n)

解法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(n
m)

解码方法

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(n
m)
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]表示增加字符

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值