算法复习-数组
数组双指针——左右指针
双指针操作时候left=0,right=s.size()-1,注意指针越界问题,判断时候有短路问题,先验边界!
两端向中间
n数之和
两数之和:先排序,再双指针,扫的时候左右指针跳过重复元素,代码写法固定,分三种情况,用while跳过重复元素
三数之和:避免重复的关键点在于,不能让第一个数重复,至于后面的两个数,我们复用的 twoSumTarget 函数会保证它们不重复。所以代码中必须用一个 while 循环来保证 3Sum 中第一个元素不重复。
有序数组的平方
-4 0 12 15 按平方大小重排
思路:两个指针分别指向数组的两端,比较两端元素的平方大小,将较大的平方放到结果数组的末尾,并移动对应的指针,直到两个指针相遇。
回文串相关
中间向两端(最长回文子串)
遍历每一个“中心”向外扩散(从一个中心扩散或者两个中心扩散)
1 | for 0 <= i < len(s): |
二分查找
数组有序就考虑
找一个元素
左闭右开区间[left, right)
while(left < right) 因为当left == right时,区间已经没有元素了,可以跳出
left = mid + 1; left所指的元素还要再找,所以指向跳过mid这个元素的位置
right = mid; right所指向的mid刚好不用再找
while(left < right){
int mid = left + (right - left) / 2;
if(nums[mid] == target) return mid;
else if(nums[mid] < target) left = mid + 1;
else right = mid;
}
return -1; // not found
}
1 |
|
数组双指针——快慢指针
去重/删除/移动元素
一个慢指针用来写,一个快指针用来在前面扫
1 | int slow = 0, fast = 0; |
滑动窗口(重点)
注意:可以滑动固定大小的窗口,维护数据和答案!
用int,unordered_map,set等维护窗口内数据
同样用左闭右开区间。slow fast指针不回退,所以只用O(n)扫一遍数组,O(n2)全遍历的部分情况不用扫
外循环更新right,内循环更新left,[left,right)左闭右开区间
核心还是思考 什么时候收缩窗口?什么时候更新答案?(注意伸缩窗口可能要维护一些数据)
求最长:不断加入right,while(窗口不合法)时才收缩,收缩到符合条件出while循环,更新答案,因为此时窗口刚刚恢复合法。
1 | for (int right = 0; right < n; ) { |
求最短:不断加入right,while(窗口合法)时收缩left,尝试找到更短的答案,收缩到不合法出while循环,在while内更新答案,因为while内部窗口是合法的。
1 | for (int right = 0; right < n; ) { |
计数:在最长修正出一个满足条件的以right为右的最长子串,计数+=right-left(都以right为右边界)
通用外层框架
1 | // 滑动窗口算法框架伪码 |
习题
Leetcode 76. 最小覆盖子串
维护need,window两个unordered_map哈希表,字符移入移出时更新window,当某个字符是need所需且符合/不符合数量时更新valid,valid == need.size()时说明窗口已经覆盖了need,进入内循环更新结果并缩小窗口
1 | unordered_map<char, int> need, window; |
Leetcode 713. 乘积小于K的子数组
计数:以right为右边界开区间的满足条件的子数组个数是right-left(包括单元素子数组)
Leetcode 424. 替换后的最长重复字符
Leetcode 219. 存在重复元素 II
定长窗口滑动 维护窗口内是否有重复元素
Leetcode 424. 替换后的最长重复字符
maxCount维护历史最大数量,因为出内层while时都是(满足可替换)且right-left=maxCount+k,res每轮=max(res,right-left),所以找到历史最大maxCount就可以找到最长长度
小巧思
n*n二维方阵原地旋转
先主对角线翻转,再水平翻转
螺旋矩阵
待补充
合并两个有序数组
从后往前扫填补就可以
将矩阵按对角线排序(未完成)
在同一个对角线上的元素,其横纵坐标之差是相同的。有了这个规律辅助,这道题就很容易做了,我直接用一个哈希表把每个对角线的元素存起来,想办法排序,最后放回二维矩阵上即可。
二维网格迁移
二维数组按一维数组往后走k步,后k个到前面。先把二维数组映射为一维的下标,然后处理一维(三次reverse)
带权随机选择算法
考虑把权重累加为一条长线段,随机数落在某个区间就选那个元素。用前缀和数组维护区间,二分查找随机数落在哪个区间。
1 | // 不带权重 |
169. 多数元素
求众数:摩尔投票算法,维护一个候选元素和一个计数器,遍历数组,如果计数器为0,更新候选元素为当前元素;如果当前元素等于候选元素,计数器加1,否则减1。最后返回候选元素。
思想:两个不同的元素可以直接消掉,剩下的一定是众数
区间问题
排序区间起点,讨论end落在哪,落在外面则生成新区间
单调队列
239. 维护定长滑动窗口中的最值
是双端队列,维护一个严格单调递增或递减的队列,队头永远是当前窗口的最值
- 入队:在队尾入队,且要对队尾进行修剪,因为新入队的元素“更年轻”,更有可能成为未来窗口的最值,所以更老且更没用的队尾要被修剪掉
注意:不能弹出同等大小的老元素,因为后面pop时用值比较,若弹出同等大小,后面再pop时会把这个同等大小也pop出去
语法
1 | auto cmp = [](pair<int,int> a,pair<int,int> b){return a.second<b.second;}; // 自定义比较器 的优先级 |

