两数之和 三数之和(相向双指针)
两数之和
暴力:枚举第一个数,再枚举第二个,时间复杂度n^2
优化:排序,(使用指针)选最两边的数,如果加起来大于target,就删去最大的数,然后一直排除,时间复杂度n
三数之和
暴力:排序,枚举一个数,加上两数之和的优化
优化:最小的三个数字相加,如果大于0,那直接break;如果x和剩余最大的两个数相加,小于0,也直接break,时间复杂度n^2,空间复杂度1
#
盛最多水的容器
如果要找到容量比蓝色区域更大的线,先指向最左和最右,如果还没有相遇就可以构成面积,算出来面积,去掉矮的,更新最大值,时间复杂度n,空间复杂度1
接雨水
方法一:单个容器能存最多的水:左边最高的和右边最高的之间容纳的水,简历两个数组,分别算前缀最大值和后缀最大值,同时遍历,算出每个水桶能接的水的最大值(前后缀最大值的插值),时间复杂度n,空间复杂度n
方法二:?还没看
滑动窗口
本质:设定一个框,根据需求移动框的左右端点位置,因为我们需要得到的结果肯定是连续的,而且原先数组每个元素都是正的,所以可以利用这个方法。
长度最小的子数组
暴力做法:n^2,枚举两次,相加和
滑动窗口:利用数组元素都是正整数的性质,设置子数组的左右端点,设置滑动窗口,如果加起来的数大于target,就向右移动左端点,时间复杂度n,空间复杂度1。
乘积小于k的子数组
同上
无重复字符的最大子串
哈希表记录字符出现次数,使用移动窗口,时间复杂度n,空间复杂度1
找到字符串中所有字母异位词
滑动窗口遍历s
-
不需要再额外存储一个哈希表。可以使用两个指针(左指针
left
和右指针right
)来构建一个滑动窗口在s
上滑动。 -
开始时,
left = 0
,right = 0
。 -
每次移动右指针
right
,将新进入窗口的字符(s[right]
)从哈希表对应的计数中减1(如果该字符在哈希表中的计数变为0,可以考虑从哈希表中删除这个字符,这样可以节省后续比较的时间)。 -
当窗口大小等于
p
的长度时(即
right - left+1 = len(p)
):
-
检查此时哈希表中的所有计数是否都为0,如果是,说明当前窗口内的子串是
p
的异位词,记录下left
的值(起始索引)。 -
然后移动左指针
left
,将离开窗口的字符(s[left]
)在哈希表中的计数加1。
-
二分查找 红蓝染色法
单纯二分查找第一个出现的位置
暴力:从左到右遍历,时间复杂度n
优化:两个指针,分别初始化为0和n-1,二分查找,取最中间的元素,
闭区间:判断m,取【l,m】【m+1,r】(m为红色的话);如果m为蓝色,也就是大于等于target,【l,m-1】是红色,然后继续询问这里的,关键是循环不变量,l-1始终是红色,r+1始终是蓝色,所以r+1是要找的答案,因为循环结束r+1=l,所以答案也可以用l表示
ps:溢出问题,改成l+(r+1)/2
在排序数组中查找元素的第一个和最后一个位置
分为求大于等于和小于等于
做题经验
最好理解的方式就是按照红蓝颜色,如果要找左端点,就找大于等于mid(left+(right-left)/2),然后left和right各自+-1,右端点就找小于等于mid的;还有赋值start和end的时候需要在循环体外面,第一次找start还需要判断是否确保left的值在数组中,只需要判断一次即可,有left就肯定有right了。
二维矩阵搜索
也可以直接left和right二分,注意mid对应的矩阵的坐标是nums【mid/n】【mid%n】!
寻找峰值
依然是染色法,二分找封顶,要不大于左/右,要不小于(题目保证不相等),时间复杂度logn
寻找旋转排序数组的最小值
讨论了蓝色(nums【mid】 在 target 及其右侧)的三种情况:
-
nums【mid】 在左侧递增段,如果 target 也在左侧且 nums【mid】>=target,就是蓝色。
-
nums【mid】 在右侧递增段,如果 target 在左侧递增段,那就直接就是蓝色。
-
nums【mid】 在右侧递增段(或者 nums 只有一个递增段),如果第 2 种情况不成立,说明 target 也在右侧递增段,此时如果 nums【mid】>=target,就是蓝色。
-
其余情况为红色。
查找旋转点k不能小于等于!需要小于!left<right
然后我的方法是先找出k然后找target
target<nums[0] or target >= nums[k] && target <= nums[n - 1]
为啥不一样呢?
链表
可以想到哈希表、快慢指针
注意判断nullptr,设置新的哨兵结点dummy,
相交链表
关键在于链表长度
1.最简单的,复杂度最高的,取最后公共的长度,让两个数组的长度相同,然后遍历,得到相同的指针的时候直接return
2.设两个链表长度分别为(x+z)和(y+z),然后总的遍历到达x+y+z的时候,说明肯定会相等
反转链表
用next记录原来的next,pre初始化成null
反转中间一段链表
反转这一段的上一个节点的next和下一个结点的pre改变;注意如果最前面的head是所谓中间的链表,需要在最前面加上一个p0,
快慢指针 环形链表 重排链表
求链表的中间值/偶数的话就是中间偏后的那个值:
slow=slow->next;fast=fast->next->next;
环形链表
1.使用哈希表存储见到过的结点,在哈希表中找到了说明出现了环。
2.使用快慢指针,快指针每次走两步,慢指针每次走一步,如果相遇,就说明有环,可以追上;如果fast是null或者fast->next是null那么返回false。注意一开始需要条件判断,如果head或者head->next是空,直接返回false。
3.给数组赋值,见到过的赋null(很局限)。
如果需要返回环形起点的指针,哈希表太慢了,就只能用快慢指针——相遇之处和链表头距离环入口距离相等
前后指针 链表删除 链表去重
二叉树
递归 数学归纳法 栈
相同 对称 平衡 右视图
前序 中序 后序
最近公共祖先
层序遍历 BFS 队列
回溯
子集型 分割回文串
组合型 剪枝
排列型 N皇后
动态规划
背包all
线性DP 最长公共子序列 编辑距离
线性DP 最长递增子序列
状态机DP 买卖股票系列
区间DP 最长回文子序列 最优三角剖分
树形DP
单调栈
单调队列
一些常用但不会用的技巧
排序
给一个vector<vector<int>>按照其中的一个元素排序
sort(intervals.begin(),intervals.end(),[](const vector<int>&a,const vector<int>&b)