leetcode-cn 2021年1月 2021年2月 每日一题

// 力扣
// 1. 最大值最小 → 二分 + 判定
// 至于连通性判断:搜索 、并查集均可;搜索效率更高。

func maxInt(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func absInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func findSet(x int, parent []int) int {
	if parent[x] < 0 {
		return x
	}
	parent[x] = findSet(parent[x], parent)
	return parent[x]
}

func unionSet(x, y int, parent []int) int {
	r1, r2 := findSet(x, parent), findSet(y, parent)
	if r1 == r2 {
		return 0
	}
	if parent[r1] < parent[r2] {
		parent[r1] += parent[r2]
		parent[r2] = r1
	} else {
		parent[r2] += parent[r1]
		parent[r1] = r2
	}
	return 1
}

func minimumEffortPath(heights [][]int) int {
    n, m := len(heights), len(heights[0])
    parent := make([]int, n*m)
    maxH, dx, dy := 0, []int{-1, 1, 0, 0}, []int{0, 0, -1, 1}
    for i := 0; i < n; i++ {
        for j := 0; j < m; j++ {
            for k := 0; k < 4; k++ {
                ii, jj := i + dx[k], j + dy[k]
                if ii >= 0 && ii < n && jj >= 0 && jj < m {
                    maxH = maxInt(maxH, absInt(heights[i][j]-heights[ii][jj]))
                }
            }
        }
    }
    var check = func(h int) bool {
        for i := 0; i < n*m; i++ {
            parent[i] = -1
        }
        for i := 0; i < n; i++ {
            for j := 0; j < m; j++ {
                for k := 0; k < 4; k++ {
                    ii, jj := i + dx[k], j + dy[k]
                    if ii >= 0 && ii < n && jj >= 0 && jj < m && absInt(heights[i][j]-heights[ii][jj]) <= h {
                        unionSet(i*m+j, ii*m+jj, parent)
                    }
                }
            }
        }
        return findSet(0, parent) == findSet(n*m-1, parent)
    }
    l, r, ans := 0, maxH, -1
    for l <= r {
        mid := l + (r-l)/2
        if check(mid) {
            ans = mid
            r = mid - 1
        } else {
            l = mid + 1
        }
    }
    return ans
}

// 2. 最短路径 → dijkstra + heap 、spfa

func maxInt(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func absInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func minimumEffortPath(heights [][]int) int {
	n, m := len(heights), len(heights[0])
	dis := make([]int, n*m)
	for i := 1; i < n*m; i++ {
		dis[i] = 0x3f3f3f3f
	}
	dx, dy := []int{-1, 1, 0, 0}, []int{0, 0, -1, 1}
	que := make([]int, 0, 1000000)
	inq := make([]bool, n*m)
	que = append(que, 0)
	inq[0] = true
	for len(que) > 0 {
		u := que[0]
		que = que[1:]
		inq[u] = false
		x, y := u/m, u%m
		for i := 0; i < 4; i++ {
			xx, yy := x+dx[i], y+dy[i]
			if xx >= 0 && xx < n && yy >= 0 && yy < m {
				v := xx*m + yy
				w := maxInt(dis[u], absInt(heights[x][y]-heights[xx][yy]))
				if dis[v] > w {
					dis[v] = w
					if !inq[v] {
						inq[v] = true
						que = append(que, v)
					}
				}
			}
		}
	}
	return dis[n*m-1]
}

778. 水位上升的泳池中游泳

并查集,给每个结点按从小到大排序,然后按序取出,并将该节点四周高度小于该节点的点并入并查集。

type UnionSet struct {
    parent []int
}

func NewUnionSet(size int) *UnionSet {
    parent := make([]int, size)
    for i:=0; i<size; i++ {
        parent[i] = i
    }
    return &UnionSet{parent}
}

func (us *UnionSet) Find(x int) int {
    if us.parent[x] != x {
        us.parent[x] = us.Find(us.parent[x])
    }
    return us.parent[x]
}

func (us *UnionSet) Union(x, y int) {  
    us.parent[us.Find(y)] = us.Find(x)
}

func (us *UnionSet) InSameSet(x, y int) bool {  
    return us.Find(x) == us.Find(y)
}

type point struct {
    x, y, height int
}

func swimInWater(grid [][]int) int {
    n := len(grid)
    total := n*n
    
    points := []point{}
    for i:=0; i<n; i++ {
        for j:=0; j<n; j++ {
            points = append(points, point{i, j, grid[i][j]})
        }
    }

    sort.Slice(points, func(i, j int) bool {
        return points[i].height < points[j].height
    })

    us := NewUnionSet(total)
    for i:=0; i<total; i++ {
        idx := points[i].x * n +points[i].y
        if points[i].x > 0 &&  points[i].height >= grid[points[i].x-1][points[i].y]{
            us.Union(idx, idx-n)
        }
        if points[i].y > 0 &&  points[i].height >= grid[points[i].x][points[i].y-1]{
            us.Union(idx, idx-1)
        }
        if points[i].x < n-1 &&  points[i].height >= grid[points[i].x+1][points[i].y]{
            us.Union(idx, idx+n)
        }
        if points[i].y < n-1 &&  points[i].height >= grid[points[i].x][points[i].y+1]{
            us.Union(idx, idx+1)
        }
        if us.InSameSet(0, total-1) {
            return points[i].height
        }
    }

    return grid[n-1][n-1]
    
}

888. 公平的糖果棒交换

难度简单116

爱丽丝和鲍勃有不同大小的糖果棒:A[i] 是爱丽丝拥有的第 i 根糖果棒的大小,B[j] 是鲍勃拥有的第 j 根糖果棒的大小。

因为他们是朋友,所以他们想交换一根糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。)

返回一个整数数组 ans,其中 ans[0] 是爱丽丝必须交换的糖果棒的大小,ans[1] 是 Bob 必须交换的糖果棒的大小。

如果有多个答案,你可以返回其中任何一个。保证答案存在。


/*
最后等价转换为求两个有序数组中的相同数字
*/
func fairCandySwap(A []int, B []int) []int {
	sumA := 0
	sumB := 0
	for _, v := range A {
		sumA += v
	}

	for _, v := range B {
		sumB += v
	}

	sort.Ints(A)
	sort.Ints(B)

	//双指针遍历 A,B,判断是否存在 a=b+target
	target := (sumA - sumB) / 2
	i := 0
	j := 0
	for i < len(A) && j < len(B) {

		if A[i] == B[j]+target {
			return []int{A[i], B[j]}
		}

		if A[i] < B[j]+target {
			i++
		} else {
			j++
		}

	}

	// }

	return []int{0, 0}
}

424. 替换后的最长重复字符

难度中等258

给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。

**注意:**字符串长度 和 k 不会超过 104。


/*
424. 替换后的最长重复字符
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。

注意:字符串长度 和 k 不会超过 104。
1. 二分法,每次定义最长的重复字符长度 winLen
2. 通过滑窗判断是否可以满足 ; 计算滑窗内各个字符的个数,然后得到频率最大的字符个数 cnt, 判断 cnt+k>= winLen ,就返回true
*/

/*
424. 替换后的最长重复字符
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。

注意:字符串长度 和 k 不会超过 104。
1. 二分法,每次定义最长的重复字符长度 winLen
2. 通过滑窗判断是否可以满足 ; 计算滑窗内各个字符的个数,然后得到频率最大的字符个数 cnt, 判断 cnt+k>= winLen ,就返回true
*/
func characterReplacement(s string, k int) int {
	if len(s) == 0 || k >= len(s)-1 {
		return len(s)
	}

	sb := []byte(s)
	start := k
	best := -1
	end := len(s)
	for start <= end {
		mid := (end-start)/2 + start
		if judge(sb, mid, k) {
			best, start = mid, mid+1
		} else {
			end = mid - 1
		}
	}

	return best

}

func judge(s []byte, winEnd, k int) bool {

	charCnt := make([]int, 26)
	for i := 0; i < winEnd; i++ {
		charCnt[s[i]-'A']++
	}

	if windowCanfill(charCnt, winEnd, k) {
		return true
	}

	for i := winEnd; i < len(s); i++ {
		charCnt[s[i]-'A']++
		charCnt[s[i-winEnd]-'A']--
		if windowCanfill(charCnt, winEnd, k) {
			return true
		}

	}

	return false

}

/*
1. 求窗口内频率最高的字符 c
2. 把窗口内c之外的字符个数如果小于等于k,那么就可以把其他的字符都换为c
*/
func windowCanfill(charCnt []int, winEnd, k int) bool {

	maxCnt := 0
	for _, v := range charCnt {
		if v > maxCnt {
			maxCnt = v
		}
	}
	if maxCnt+k >= winEnd {
		return true
	}
	return false
}

相似题目

1004. 最大连续1的个数 III

难度中等142

给定一个由若干 01 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。

返回仅包含 1 的最长(连续)子数组的长度。


/*
 相似题目 424,1493,1004.
 替换后的最长重复字符
 利用二分法加滑窗
*/
func longestOnesBinary(A []int, K int) int {
	if K >= len(A) {
		return len(A)
	}

	start := K
	end := len(A)
	best := -1
	for start <= end {

		mid := (end-start)/2 + start
		if judege(A, mid, K) {
			best, start = mid, mid+1
		} else {
			end = mid - 1
		}

	}
	return best
}

func judege(A []int, winLen, K int) bool {
	numberCnt := [2]int{} //记录0,1的个数
	for i := 0; i < winLen; i++ {
		numberCnt[A[i]]++
	}

	if numberCnt[1]+K >= winLen {
		return true
	}

	for i := winLen; i < len(A); i++ {
		numberCnt[A[i-winLen]]--
		numberCnt[A[i]]++
		if numberCnt[1]+K >= winLen {
			return true
		}
	}

	return false

}

//双指针方法
func longestOnes(A []int, K int) int {
	if K >= len(A) {
		return len(A)
	}
	if K == 0 {
		return findMaxConsecutiveOnes(A)
	}

	numCnt := [2]int{}
	start, end := 0, 0 //左闭,右开区间,包含start,不包含end
	maxLen := 0
	for start <= end && end < len(A) {
		//要加的end是1,可以继续加 或 要加入的是0, 而且目前窗口中0 的数量还小于 K,可以继续加 0
		if A[end] == 1 || numCnt[0] < K {
			numCnt[A[end]]++
			end++
			if end-start > maxLen {
				maxLen = end - start
			}
			continue
		}

		//当前窗口中0的数量等于k,就不能再往里面加0了,就需要把窗口左边缩小
		numCnt[A[start]]--
		start++

	}

	return maxLen

}

func findMaxConsecutiveOnes(nums []int) int {
	maxLen := 0
	curLen := 0
	for _, v := range nums {

		if v == 1 {
			curLen++
		} else {
			if curLen > maxLen {
				maxLen = curLen
			}
			curLen = 0
		}
	}

	if curLen > maxLen {
		maxLen = curLen
	}
	return maxLen
}

1493. 删掉一个元素以后全为 1 的最长子数组

难度中等20

给你一个二进制数组 nums ,你需要从中删掉一个元素。

请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。

如果不存在这样的子数组,请返回 0 。


/*
 相似题目 424,1493,1004.
二分法
https://github.com/kingeasternsun/leetcode-cn
*/
func longestSubarrayBinary(nums []int) int {

	start := 0
	end := len(nums)
	best := -1
	for start <= end {

		mid := (end-start)/2 + start
		if judege(nums, mid) {
			best, start = mid, mid+1
		} else {
			end = mid - 1
		}

	}
	if best > 0 {
		return best - 1
	}
	return best

}

func judege(nums []int, winLen int) bool {
	numCnt := [2]int{}
	for i := 0; i < winLen; i++ {
		numCnt[nums[i]]++
	}

	if numCnt[0] <= 1 {
		return true
	}

	for i := winLen; i < len(nums); i++ {
		numCnt[nums[i-winLen]]--
		numCnt[nums[i]]++
		if numCnt[0] <= 1 {
			return true
		}

	}

	return false
}

func longestSubarray(nums []int) int {

	maxLen := 0
	numCnt := [2]int{}
	beg, end := 0, 0 //左闭右开区间
	for beg <= end && end < len(nums) {
		//如果当前end上是1 或 [beg,end)里面没有0 ,就可以把 当前end加入窗口
		if nums[end] == 1 || numCnt[0] == 0 {
			numCnt[nums[end]]++
			end++
			if end-beg-1 > maxLen {
				maxLen = end - beg - 1
			}
			continue
		}
		//end上是0 ,而且 [beg,end)里面已经有一个0了,就需要去掉左边的beg
		numCnt[nums[beg]]--
		beg++

	}

	return maxLen
}

480. 滑动窗口中位数

难度困难152

中位数是有序序列最中间的那个数。如果序列的大小是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。

例如:

  • [2,3,4],中位数是 3
  • [2,3],中位数是 (2 + 3) / 2 = 2.5

给你一个数组 nums,有一个大小为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。

/*
 * @Author: kingeasternsun
 * @Date: 2021-02-03 09:02:46
 * @LastEditTime: 2021-02-03 09:40:16
 * @LastEditors: kingeasternsun
 * @Description: In User Settings Edit
 * @FilePath: \490medianSlidingWindow\490.go
 */
package leetcode

import "sort"

func medianSlidingWindow(nums []int, k int) []float64 {
	if k > len(nums) {
		return nil
	}

	if k == 0 {
		return nil
	}
	res := []float64{}

	if k == 1 {
		for _, v := range nums {
			res = append(res, float64(v))
		}
		return res
	}

	winNums := make([]int, k) //维护一个有序的滑动窗口就可以了
	copy(winNums, nums[:k])
	sort.Ints(winNums)

	res = append(res, getMedian(winNums, k))
	for i := k; i < len(nums); i++ {
		id := sort.SearchInts(winNums, nums[i-k]) //移除i-k这个数字
		winNums[id] = nums[i]                     //加入 i上的数字

		//调整winNum[id]的数字位置,让winNums保持有序
		for id < k-1 && winNums[id] > winNums[id+1] {
			winNums[id], winNums[id+1] = winNums[id+1], winNums[id]
			id++
		}

		for id > 0 && winNums[id] < winNums[id-1] {
			winNums[id], winNums[id-1] = winNums[id-1], winNums[id]
			id--
		}

		res = append(res, getMedian(winNums, k))
	}
	return res
}

func getMedian(winNums []int, k int) float64 {
	return (float64(winNums[k/2]) + float64(winNums[(k-1)/2])) / 2
}

// 力扣
// 1. 双堆&&延迟删除
// 2. 线段树/树状数组 + 离散化
// 3. 朴素算法
// 4. skip list?

func lowbit(x int) int {
	return x & (-x)
}

func add(p, c, n int, stat []int) {
	for i := p; i <= n; i += lowbit(i) {
		stat[i] += c
	}
}

func sum(p int, stat []int) int {
	ret := 0
	for i := p; i > 0; i -= lowbit(i) {
		ret += stat[i]
	}
	return ret
}

func findK(k, n int, stat []int) int {
	l, r, ret := 1, n, -1
	for l <= r {
		mid := l + (r-l)/2
		if sum(mid, stat) >= k {
			ret, r = mid, mid-1
		} else {
			l = mid + 1
		}
	}
	return ret
}

func medianSlidingWindow(nums []int, k int) []float64 {
	n := len(nums)
	cnums := make([]int, n)
	copy(cnums, nums)
	sort.Ints(cnums)

	imap := make(map[int]int)
	for i := 0; i < n; i++ {
		imap[cnums[i]] = i + 1
	}

	stat := make([]int, n+1)
	result := make([]float64, 0, n)

	var findMedian = func(k int) float64 {
		if k%2 == 0 {
			idx1, idx2 := findK(k/2, n, stat)-1, findK(k/2+1, n, stat)-1
			return float64(cnums[idx1]+cnums[idx2]) / 2.0
		}
		idx := findK((k+1)/2, n, stat) - 1
		return float64(cnums[idx])
	}

	for i := 0; i < k; i++ {
		add(imap[nums[i]], 1, n, stat)
	}
	result = append(result, findMedian(k))

	for i := k; i < n; i++ {
		add(imap[nums[i]], 1, n, stat)
		add(imap[nums[i-k]], -1, n, stat)
		result = append(result, findMedian(k))
	}
	return result
}
func pushUp(idx int, tree []int) {
	tree[idx] = tree[idx<<1] + tree[(idx<<1)|1]
}

func query(k, s, t, idx int, tree []int) int {
	if s == t {
		return s
	}
	mid := (s + t) >> 1
	if tree[idx<<1] >= k {
		return query(k, s, mid, idx<<1, tree)
	}
	return query(k-tree[idx<<1], mid+1, t, idx<<1|1, tree)
}

func update(p, c, s, t, idx int, tree []int) {
	if s == t {
		tree[idx] += c
		return
	}
	mid := (s + t) >> 1
	if p <= mid {
		update(p, c, s, mid, idx<<1, tree)
	} else {
		update(p, c, mid+1, t, (idx<<1)|1, tree)
	}
	pushUp(idx, tree)
}

func medianSlidingWindow(nums []int, k int) []float64 {
	n := len(nums)
	cnums := make([]int, n)
	copy(cnums, nums)
	sort.Ints(cnums)
	imap := make(map[int]int)
	for i := 0; i < n; i++ {
		imap[cnums[i]] = i + 1
	}

	tree := make([]int, (n+10)<<2)
	result := make([]float64, 0, n)

	var findMedian = func(k int) float64 {
		if k%2 == 0 {
			idx1, idx2 := query(k/2, 1, n, 1, tree)-1, query(k/2+1, 1, n, 1, tree)-1
			return float64(cnums[idx1]+cnums[idx2]) / 2.0
		}
		idx := query((k+1)/2, 1, n, 1, tree) - 1
		return float64(cnums[idx])
	}

	for i := 0; i < k; i++ {
		update(imap[nums[i]], 1, 1, n, 1, tree)
	}
	result = append(result, findMedian(k))

	for i := k; i < n; i++ {
		update(imap[nums[i]], 1, 1, n, 1, tree)
		update(imap[nums[i-k]], -1, 1, n, 1, tree)
		result = append(result, findMedian(k))
	}
	return result
}
type Item struct {
	val int
	idx int
}

type MaxPQ []*Item

func (pq MaxPQ) Len() int { return len(pq) }

func (pq MaxPQ) Less(i, j int) bool {
	if pq[i].val == pq[j].val {
		return pq[i].idx > pq[j].idx
	}
	return pq[i].val > pq[j].val
}

func (pq MaxPQ) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

func (pq *MaxPQ) Push(x interface{}) {
	item := x.(*Item)
	*pq = append(*pq, item)
}

func (pq *MaxPQ) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	*pq = old[0 : n-1]
	return item
}

func (pq *MaxPQ) Top() interface{} {
	if pq.Len() == 0 {
		return nil
	}
	return (*pq)[0]
}

type MinPQ []*Item

func (pq MinPQ) Len() int { return len(pq) }

func (pq MinPQ) Less(i, j int) bool {
	if pq[i].val == pq[j].val {
		return pq[i].idx < pq[j].idx
	}
	return pq[i].val < pq[j].val
}

func (pq MinPQ) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

func (pq *MinPQ) Push(x interface{}) {
	item := x.(*Item)
	*pq = append(*pq, item)
}

func (pq *MinPQ) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	*pq = old[0 : n-1]
	return item
}

func (pq *MinPQ) Top() interface{} {
	if pq.Len() == 0 {
		return nil
	}
	return (*pq)[0]
}

func medianSlidingWindow(nums []int, k int) []float64 {
	var (
		n       = len(nums)
		minHeap = make(MinPQ, 0) // 我擦他大爷,最大堆/最小堆写反了
		maxHeap = make(MaxPQ, 0)
		sz1     = 0
		sz2     = 0
		result  = make([]float64, 0, n)
	)

	var add = func(val, idx int) {
		if sz2 == 0 {
			sz1++
			heap.Push(&maxHeap, &Item{val, idx})
		} else if sz1 == 0 {
			sz2++
			heap.Push(&minHeap, &Item{val, idx})
		} else {
			x := maxHeap.Top().(*Item)
			if val < x.val {
				sz1++
				heap.Push(&maxHeap, &Item{val, idx})
			} else {
				sz2++
				heap.Push(&minHeap, &Item{val, idx})
			}
		}
	}

	var adjust = func(idx int) {
		if idx-k >= 0 {
			if item, ok := maxHeap.Top().(*Item); ok && (item.val > nums[idx-k] || (item.val == nums[idx-k] && item.idx >= idx-k)) {
				sz1--
			} else {
				sz2--
			}
		}
		for sz1 > sz2+1 {
			item := heap.Pop(&maxHeap).(*Item)
			if item.idx <= idx-k {
				continue
			}
			sz1--
			sz2++
			heap.Push(&minHeap, item)
		}
		for sz1+1 < sz2 {
			item := heap.Pop(&minHeap).(*Item)
			if item.idx <= idx-k {
				continue
			}
			sz2--
			sz1++
			heap.Push(&maxHeap, item)
		}
		for maxHeap.Len() > 0 {
			if item := maxHeap.Top().(*Item); item.idx <= idx-k {
				heap.Pop(&maxHeap)
			} else {
				break
			}
		}
		for minHeap.Len() > 0 {
			if item := minHeap.Top().(*Item); item.idx <= idx-k {
				heap.Pop(&minHeap)
			} else {
				break
			}
		}
	}

	var calc = func() float64 {
		if sz1 == sz2 {
			return float64(maxHeap.Top().(*Item).val+minHeap.Top().(*Item).val) / 2.0
		} else if sz1 > sz2 {
			return float64(maxHeap.Top().(*Item).val)
		}
		return float64(minHeap.Top().(*Item).val)
	}

	for i := 0; i < n; i++ {
		add(nums[i], i)
		adjust(i)
		if i >= k-1 {
			result = append(result, calc())
		}
	}
	return result
}

// TODO: 周末搞个 skiplist 模板,复习一下。

643. 子数组最大平均数 I

难度简单140

给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数

go

func findMaxAverage(nums []int, k int) float64 {

	sum := 0
	for _, v := range nums[:k] {
		sum += v
	}

	res := float64(sum) / float64(k)
	for i := k; i < len(nums); i++ {
		sum += (nums[i] - nums[i-k])
		tmp := float64(sum) / float64(k)
		if tmp > res {
			res = tmp
		}
	}

	return res

}

Rust


 pub struct Solution;
 impl Solution {
    pub fn find_max_average(nums: Vec<i32>, k: i32) -> f64 {
        let k = k as usize;
        if k==1{
            let r = nums.iter().max().unwrap();
            return *r as f64;
        }

        let mut sum : i32= nums.iter().take(k as usize).sum();
        let mut res = sum as f64/k as f64;

        for i in k..nums.len(){
            sum+=&nums[i]-&nums[i-k];
            let tmp = sum as f64/k as f64;
            if tmp > res {
                res = tmp;
            }

        }

        return res;
        
    }
}

#[cfg(test)]
mod tests {
    use crate::Solution;

    #[test]
    fn it_works() {
        assert_eq!(Solution::find_max_average(vec![1,12,-5,-6,50,3], 4),12.75);
        assert_eq!(Solution::find_max_average(vec![1,12,-5,-6,50,1], 1),50.00);
    }
}

// 题目见楼上

func findMaxAverage(nums []int, k int) float64 {
	n, sumK, pK := len(nums), 0, 0
	for i := 0; i < k; i++ {
		sumK += nums[i]
		pK += nums[i]
	}
	for i := k; i < n; i++ {
		pK += nums[i]
		pK -= nums[i-k]
		if pK > sumK {
			sumK = pK
		}
	}
	return float64(sumK) / float64(k)
}

1208. 尽可能使字符串相等

双百解法,双指针维护一个动态窗口,每次移动一位后判断当前窗口内的和是否超过 maxCost,如果超过就移动preIndex直至窗口内的值小于等于 maxCost,然后更新最大窗口大小。
时间O(n) 空间O(1)

func equalSubstring(s string, t string, maxCost int) int {
    maxLen,  preIndex, preCostSum := 0, 0, 0
    for i, n:=0, len(s); i<n; i++ {
        preCostSum += abs(int(t[i]) - int(s[i]))
        for preCostSum > maxCost && preIndex <= i{
            preCostSum -= abs(int(t[preIndex]) - int(s[preIndex]))
            preIndex++
        }
        maxLen = max(maxLen, i+1-preIndex)
    }
    return maxLen
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
} 

func abs(x int) int {
    if x < 0 {
        return -x
    }
    return x
}

1208. 尽可能使字符串相等

难度中等58

给你两个长度相同的字符串,st

s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。

用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。

如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。

如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0

二分方法


func abs(a int) int {
	if a > 0 {
		return a
	}
	return -a
}

//二分搜索加滑窗
func equalSubstringBinary(s string, t string, maxCost int) int {

	left, right, best := 0, len(s), 0

	for left <= right {

		mid := (left-right)/2 + right
		if judge(s, t, maxCost, mid) {
			best, left = mid, mid+1
		} else {
			right = mid - 1
		}
	}

	return best

}

func judge(s string, t string, maxCost int, winLen int) bool {

	winSum := 0
	for i := 0; i < winLen; i++ {
		winSum += abs(int(s[i]) - int(t[i]))
	}

	if winSum <= maxCost {
		return true
	}

	for i := winLen; i < len(s); i++ {
		winSum -= abs(int(s[i-winLen]) - int(t[i-winLen]))
		winSum += abs(int(s[i]) - int(t[i]))
		if winSum <= maxCost {
			return true
		}

	}
	return false
}

直接滑窗

//纯滑窗
func equalSubstring(s string, t string, maxCost int) int {

	tmpCost, maxLen := 0, 0
	beg, end := 0, 0 //左闭,右开区间
	//注意这里 条件是 beg<= end,beg也可以等于end,意味着从新开始新的区间. 例如s和t中相应字符的差值 都大于 maxCost 的情况
	for beg <= end && end < len(s) {

		if tmpCost <= maxCost { //当前窗口可以满足预算
			if end-beg > maxLen {
				maxLen = end - beg
			}
			//然后追加 end
			delTa := abs(int(s[end]) - int(t[end]))
			tmpCost += delTa
			end++
			continue
		}

		//去掉beg
		delTa := abs(int(s[beg]) - int(t[beg]))
		tmpCost -= delTa
		beg++

	}

	//这里不能漏掉,要再次判断下
	if tmpCost <= maxCost && (end-beg) > maxLen {
		maxLen = end - beg
	}
	return maxLen

}

func abs(a int) int {
	if a > 0 {
		return a
	}
	return -a
}

// 力扣
// 前缀和 + 滑动窗口

func absInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func equalSubstring(s string, t string, maxCost int) int {
	n := len(s)
	cost := make([]int, n+1)
	for i := 0; i < n; i++ {
		cost[i+1] = absInt(int(s[i]) - int(t[i]))  // 注意: int(s[i]-t[i])
		cost[i+1] += cost[i]
	}
	maxL, l := 0, 0
	for i := 1; i <= n; i++ {
		for cost[i]-cost[l] > maxCost && l < i {
			l++
		}
		if maxL < i-l {
			maxL = i - l
		}
	}
	return maxL
}

1423. 可获得的最大点数

难度中等69

几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。

每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。

你的点数就是你拿到手中的所有卡牌的点数之和。

给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数

/*
 * @Description:
 * @Version: 2.0
 * @Author: kingeasternsun
 * @Date: 2021-02-06 10:06:07
 * @LastEditors: kingeasternsun
 * @LastEditTime: 2021-02-06 10:20:17
 * @FilePath: \1423maxScore\1423.go
 */
package leetcode

func maxScore(cardPoints []int, k int) int {
	//将给cardPoints首位相接, 可以认为给 cardPoints 添加一个虚拟的k的长度的数据。
	if k == 1 {
		if cardPoints[0] > cardPoints[len(cardPoints)-1] {
			return cardPoints[0]
		}
		return cardPoints[len(cardPoints)-1]
	}

	tmpRes := 0
	for i := len(cardPoints) - k; i < len(cardPoints); i++ {
		tmpRes += cardPoints[i]
	}

	if k == len(cardPoints) {
		return tmpRes
	}

	res := tmpRes
	for i := len(cardPoints); i < len(cardPoints)+k; i++ {
		tmpRes += cardPoints[i%len(cardPoints)]
		tmpRes -= cardPoints[i-k]
		if tmpRes > res {
			res = tmpRes
		}
	}

	return res
}

另外一种好看的解法

func maxScore(cardPoints []int, k int) int {

	//将cardPoints首位相接, 可以认为给 cardPoints 添加一个虚拟的k的长度的数据。
	tmpRes, res := 0, 0
	for i := len(cardPoints) - k; i < len(cardPoints)+k; i++ {
		if i < len(cardPoints) {
			tmpRes += cardPoints[i]
			res = tmpRes
		} else {
			tmpRes += cardPoints[i%len(cardPoints)]
			tmpRes -= cardPoints[i-k]
			if tmpRes > res {
				res = tmpRes
			}
		}

	}

	return res
}

665. 非递减数列

难度简单434

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]

解法

转为求非连续的递增子序列问题

方法一 动态规划

/*
1300ms 6.5MB
*/
func checkPossibility1(nums []int) bool {

	if len(nums) <= 2 {
		return true
	}

	//计算最长的递增子序列长度
	dp := make([]int, len(nums)) //以当前元素为结尾的自增序列长度
	dp[0] = 1
	for i := 1; i < len(nums); i++ {
		tmp := 1
		for j := 0; j < i; j++ {
			if nums[i] >= nums[j] && dp[j]+1 > tmp {
				tmp = dp[j] + 1
			}
		}
		dp[i] = tmp
	}

	cnt := 1
	for i := range dp {
		if dp[i] > cnt {
			cnt = dp[i]
		}
	}
	return cnt >= (len(nums) - 1)
}

方法二 二分加贪心

/*
 二分 贪心法 转为求最长非连续递增子序列问题 44ms 6.7MB
[1,2,4]  new 3 ,fount 3, get  [1,2,3]  //单调递增
[1,2,4]  new 3 ,found 4, get  [1,2,3]  //递增

[1,2,4]  new 2, found 2, get  [1,2,4]  //单调自增
[1,2,4]  new 2, found 3, get  [1,2,2]  //递增
*/
func checkPossibility(nums []int) bool {

	if len(nums) <= 2 {
		return true
	}

	//计算最长的递增子序列长度
	increNums := []int{nums[0]} //维护当前可以构建的递增序列
	for i := 1; i < len(nums); i++ {

		//因为是可以有重复,所以这里查询的时候要+1
		pos := sort.SearchInts(increNums, nums[i]+1)
		if pos == len(increNums) {
			increNums = append(increNums, nums[i])
		} else {
			increNums[pos] = nums[i]
		}

	}

	return len(increNums) >= (len(nums) - 1)

}

Rust

/*
 * @Description: 665
 * @Version: 2.0
 * @Author: kingeasternsun
 * @Date: 2021-02-07 10:08:19
 * @LastEditors: kingeasternsun
 * @LastEditTime: 2021-02-07 11:19:14
 * @FilePath: \665checkPossibility\check_possibility\src\lib.rs
 */
pub struct Solution;

impl Solution {
    pub fn check_possibility(nums: Vec<i32>) -> bool {
        let mut incre_nums = Vec::new();
        let nums_len = nums.len();
        for n in nums {
            // println!("{:?}",incre_nums);
            let idx = incre_nums.binary_search(&(n+1)).unwrap_or_else(|x| x);
            if idx == incre_nums.len(){
                incre_nums.push(n);
            }else{
                //如果找到了 要在处理下 找到最左边界
                let mut j = idx;
                loop{
                    if j ==0 {
                        break;
                    }

                    if incre_nums[j-1]!=incre_nums[j]{
                        break;
                    }
                    j-=1;
                }
                
                incre_nums[j]=n;
            }

            // println!("after {:?}",incre_nums);


        }

        return incre_nums.len() >=nums_len-1
    }
}
//4 ,5, 1,2,3

#[cfg(test)]
mod tests {
    use crate::Solution;

    #[test]
    fn it_works() {
        assert_eq!(Solution::check_possibility(vec![4,2,3]),true);
        assert_eq!(Solution::check_possibility(vec![4,2,1]),false);
        assert_eq!(Solution::check_possibility(vec![2,3,3,2,2]),false);
    }
}

// 昨晚玩王者荣耀过点了。。。今天早晨到公司补的这周题目,总体 bc45 难度偏简单

func sumOfUnique(nums []int) int {
    n, sum := len(nums), 0
    mark := make([]int, 101)
    for i := 0; i < n; i++ {
        mark[nums[i]]++
    }
    for i := 1; i <= 100; i++ {
        if mark[i] == 1 {
            sum += i
        }
    }
    return sum
}
func maxInt(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func minInt(a, b int) int {
    if a < b {
        return a 
    }
    return b
}

func maxAbsoluteSum(nums []int) int {
    n, s1, s2 := len(nums), 0, 0
    ps1, ps2 := 0, 0
    for i := 0; i < n; i++ {
        ps1 += nums[i]
        ps2 += nums[i]
        s1 = maxInt(s1, ps1)
        s2 = minInt(s2, ps2)
        if ps1 < 0 {
            ps1 = 0
        }
        if ps2 > 0 {
            ps2 = 0
        }
    }
    return maxInt(s1, -s2)
}
func minimumLength(s string) int {
    l, r := 0, len(s)-1
    for l <= r {
        if s[l] == s[r] {
            if l == r {
                return 1
            }
            ch := s[l]
            for l < r && s[l] == ch {
                l++
            }
            for r > l && s[r] == ch {
                r--
            }
            if l == r && s[l] == ch {
                return 0
            }
        } else {
            break
        }
    }
    return r-l+1
}
type Node struct {
    s, e, v int
}

func minInt(a, b int) int {
    if a < b {
        return a
    }
    return b 
}

func maxInt(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func maxValue(events [][]int, k int) int {
    n := len(events)
    nds := make([]Node, 0, n)
    for i := 0; i < n; i++ {
        nds = append(nds, Node{events[i][0], events[i][1], events[i][2]})
    }
    sort.Slice(nds, func(i, j int) bool {
        if nds[i].e == nds[j].e {
            return nds[i].s < nds[j].s
        }
        return nds[i].e < nds[j].e
    })

    m := k+1
    dp := make([]int, n*m+10)
    dp[1] = nds[0].v
    for i := 1; i < n; i++ {
        dp[i*m+1] = maxInt(dp[(i-1)*m+1], nds[i].v) 
        for j := 2; j <= minInt(i+1, k); j++ {
            l, r, idx := 0, i-1, -1
            for l <= r {
                mid := l + (r-l)/2
                if nds[mid].e < nds[i].s {
                    l, idx = mid+1, mid
                } else {
                    r = mid - 1
                }
            }
            if idx != -1 {
                dp[i*m+j] = maxInt(dp[i*m+j], nds[i].v + dp[idx*m+j-1])
            }
            dp[i*m+j] = maxInt(dp[i*m+j], dp[(i-1)*m+j])
        }
    }
    ans := 0
    for i := 1; i <= k; i++ {
        ans = maxInt(ans, dp[(n-1)*m+i])
    }
    return ans
}

// 227 contest

func maximumScore(a int, b int, c int) int {
	arr, sum := []int{a, b, c}, 0
	sort.Ints(arr)
	for arr[1] > 0 && arr[2] > 0 {
		arr[1]--
		arr[2]--
		sum++
		sort.Ints(arr)
	}
	return sum
}
func largestMerge(word1 string, word2 string) string {
    n1, n2 := len(word1), len(word2)
    ans := make([]byte, 0, n1+n2)
    i1, i2 := 0, 0
    for i1 < n1 || i2 < n2 {
        if i1 < n1 && i2 < n2 {
            if word1[i1] > word2[i2] {
                ans = append(ans, word1[i1])
                i1++
            } else if int(word2[i2]) > int(word1[i1]) {
                ans = append(ans, word2[i2])
                i2++
            } else {
                if word1[i1:] >= word2[i2:] {
                    ans = append(ans, word1[i1])
                    i1++
                } else {
                    ans = append(ans, word2[i2])
                    i2++
                }
            } // ab, ac
        } else if i1 >= n1 {
            ans = append(ans, word2[i2])
            i2++
        } else {
            ans = append(ans, word1[i1])
            i1++
        }
    }
    return string(ans)
}
func absInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func minAbsDifference(nums []int, goal int) int {
	n := len(nums)
	n1, n2 := n/2, n-n/2
	d1 := make([]int, 1<<uint(n1))
	d2 := make([]int, 1<<uint(n2))
	ans := absInt(nums[0] - goal)
	for i := 0; i < n1; i++ {
		d1[1<<uint(i)] = nums[i]
	}
	for i := 0; i < n2; i++ {
		d2[1<<uint(i)] = nums[n/2+i]
	}
	for i := 1; i < 1<<uint(n1); i++ {
		d1[i] = d1[i&(i-1)] + d1[i&(-i)]
		ans = minInt(ans, absInt(d1[i]-goal))
	}
	for i := 2; i < 1<<uint(n2); i++ {
		d2[i] = d2[i&(i-1)] + d2[i&(-i)]
		ans = minInt(ans, absInt(d2[i]-goal))
	}
	sort.Ints(d1)
	sort.Ints(d2)
	// two pointer
	l, r, xx := 0, (1<<uint(n2))-1, (1<<uint(n1))-1
	for l <= xx && r >= 0 {
		ans = minInt(ans, absInt(d1[l]+d2[r]-goal))
		if d1[l]+d2[r] > goal {
			r--
		} else {
			l++
		}
	}
	return ans
}

今日一题
978. 最长湍流子数组


/*
 贪心思想
考虑情况1
 ans[i] 代表以第i个元素为结尾最长的湍流子数组长度
 ans[0]=1
 ans [i] 当 i 是偶数 ans[i]=A[i]>A[i-1]?ans[i-1]+1:1
 当 i 是奇数数 ans[i]=A[i]<A[i-1]?ans[i-1]+1:1
由于ans[i]只和ans[i-1]有关,空间复杂度可以优化到O(1)
情况2 同理
*/

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func maxTurbulenceSize(arr []int) int {
	best := 1
	ans, ans2 := 1, 1
	for i := 1; i < len(arr); i++ {
		if arr[i] == arr[i-1] {
			ans, ans2 = 1,1
		} else if i&1 == 0 {
			if arr[i] < arr[i-1] {
				ans++
				ans2=1
			} else {
				ans = 1
				ans2++
			}
		} else {
			if arr[i] > arr[i-1] {
				ans++
				ans2 = 1
			} else {
				ans = 1
				ans2++
			}
		}
		best = max(best, ans)
		best = max(best, ans2)
	}

	return best
}

978. 最长湍流子数组

思路: 两个双指针,共用一个右指针,一次遍历

func maxTurbulenceSize(arr []int) int {
    mx := 0
    i1, i2, j := 0, 0, 1
    for ;j<len(arr); j++ {
        if (j%2==0 && arr[j]<=arr[j-1]) || (j%2==1 && arr[j]>=arr[j-1]) {
            mx = max(mx, j-i1)
            i1 = j
        }
        if (j%2==0 && arr[j]>=arr[j-1]) || (j%2==1 && arr[j]<=arr[j-1]) {
            mx = max(mx, j-i2)
            i2 = j
        }
    }
    mx = max(mx, j-i1)
    mx = max(mx, j-i2)
    return mx
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

//dp状态转移方程

//down[i]=up[i-1]+1 下

//up[i]=down[i-1]+1 上

//理论上可以再简化
func maxTurbulenceSize(arr []int) int {

n := len(arr)

up := make([]int, n)

down := make([]int, n)

up[0] = 1

down[0] = 1

ans := 1

for i := 1; i < len(arr); i++ {

    if arr[i-1] < arr[i] {

        up[i] = down[i-1] + 1

        down[i] = 1

    } else if arr[i-1] > arr[i] {

        up[i] = 1

        down[i] = up[i-1] + 1

    } else {

        up[i] = 1

        down[i] = 1

    }

    ans = maxInt(ans, maxInt(up[i], down[i]))

}

return ans

}