今日内容:
● 239. 滑动窗口最大值
● 347.前 K 个高频元素
● 总结
1. 滑动窗口最大值 (一刷至少需要理解思路)
关联 leetcode 239. 滑动窗口最大值
- 思路
- 使用单调队列来实现
- 单调队列:
- 整个队列元素 单调递减/递增
- 保证队列里的元素 从大到小 排列
- 不需要对窗口内的数进行排序
- 保证进入窗口内,后面的数小于前面的数即可
- 单调队列:
- 使用单调队列来实现
- 题解
-
自己实现一个单调队列
- pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
- front(): 返回需要的最大值
-
代码实现
// MyQueue 自定义的单调队列 // Back 进 Front 出 type MyQueue struct { queue []int } func NewMyQueue() *MyQueue { return &MyQueue{queue: make([]int, 0)} } func (q *MyQueue) Empty() bool { return len(q.queue) == 0 } func (q *MyQueue) Pop(x int) { // 只 pop() 队首元素的原因: // 其他元素在push()的时候已经被清洗掉了 if !q.Empty() && x == q.Front() { //这里可以不用做判空 //if len(q.queue) > 1 { // q.queue = q.queue[1:] //} else { // q.queue = make([]int, 0) //} q.queue = q.queue[1:] } } // Push // 入队 // 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。 // 这样就保持了队列里的数值是单调从大到小的了。 func (q *MyQueue) Push(x int) { for !q.Empty() && x > q.Back() { // 将要入队的元素比队尾元素还大 q.queue = q.queue[:len(q.queue)-1] // 移除队尾元素 } q.queue = append(q.queue, x) } func (q *MyQueue) Front() int { return q.queue[0] } func (q *MyQueue) Back() int { return q.queue[len(q.queue)-1] } func maxSlidingWindow(nums []int, k int) []int { res := make([]int, 0) myQueue := NewMyQueue() for i := 0; i < k; i++ { myQueue.Push(nums[i])//将前 k个 元素放入队列中 } res = append(res, myQueue.Front()) for i := k; i < len(nums); i++ { myQueue.Pop(nums[i-k])//真正开始移除元素,移除的是 nums 里面的元素,按值移除元素,从 nums[0]开始移除 myQueue.Push(nums[i])//加入新元素 res = append(res, myQueue.Front()) } return res }
-
2. 前 K 个高频元素 (一刷至少需要理解思路)
关联 347.前 K 个高频元素
- 现有哈希表【Map】统计元素出现次数
- 使用小顶堆【优先队列】来实现排序
- 小顶堆,每次弹出来是最小的那个元素
- 思路
- 解法1: 自己构建小顶堆, 时间复杂度 O(n*logk)
- 解法2: 对统计好的Map做排序, 时间复杂度 O(n*logn)
3. 总结
- 面试题:栈里面的元素在内存中是连续分布的么?
- 依赖实现栈的底层容器
- 栈实现队列:
- 需要两个栈;出栈+入栈
- 队列实现栈
- 最少需要一个队列:循环队列即可
- 编译器在词法分析中处理括号匹配逻辑,就使用了栈的数据结构
- 递归的实现也是依赖栈
- 函数栈,函数独享的内存空间,实现了函数彼此调用
9. 题外话
- 优先级队列
- 披着队列外衣的栈
- 对外接口
- 从对头取元素
- 从队尾添加元素
- 内部依照元素的权重排列
- 快速排序
- 堆
- 一棵完全二叉树,树中每个节点的值都不小于(大于)其左右孩子的值
- 如果对满二叉树的结点进行编号, 约定编号从根结点起, 自上而下, 自左而右。则深度为k的, 有n个结点的二叉树, 当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时, 称之为完全二叉树