【题目经验】
【数组】
【滑动窗口】 eg.长度最小的子数组
-- for中嵌套while:窗口末端for实现递进;窗口起始while判断、灵活框选。
【二分查找】
-- 注意开闭区间对应不同边界写法,保证全文的统一性。单值也要进入循环判断。
-- 如果目的不是判断有无,而是有其他需求,应该加入代码!
【二叉树】
--二叉搜索树的中序遍历是升序数组!
--需要临近值比较等操作,加入pre树指针
【哈希表】
使用场景:查找元素问题;避免重复问题;映射问题;
Set:只能存储key,查找key。但是胜在不需要key连续、不需要知道key范围。
Map:能存key和对应val,可以map[]索引。比Int[]优在不需要知道范围,不浪费,以及函数库。
Int[]:时间空间简单,但只适合表示范围已知、较为连续的表
【回溯问题】
无序组合:for循环中start起始,i+1作为start传入下一次
有序-全排列:始终0始;用过元素记录表
子集:start;不return;降重(排序等)
【动态规划】
【背包问题】
一、起始
//书包重量和物体取用两个考虑维度
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
//dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
//推导中dp[i][j]意在判别某物取与不取哪个更合适,dp[i - 1][j]在于同样容量不取本物 取其他物下值;
//取的前提要判别容量够用
二、简化:滚动数组
//由于每层i只和上一层i-1有关,可以将i维度压缩,相当于即刻使用i-1并覆盖。
//01背包:由于使用后覆盖了,后续往前查找会找不到前面的i-1原值,因而要从后往前遍历包容量j。
双for循环必须外围物品原因:本来大号要在小号物品基础上运算,若外围容量(还要从大到小遍历),每次没有其他物品基础,而是以最大物品覆盖该层容量值,也即只装了一个最贵物品。
//完全背包:因为可以无限使用,要从小到大遍历容量j。
//滚动数组形式:最大价值不用初始化:没有i-1超限问题,第一行物品没有就是0; 但是求组合需要初始化dp[0] = 1,因为需要1起始累计,
//解题思路:将问题转化为是否存入背包问题
-- 状态转移
//最大值问题:max;
//排列组合问题:+=;dp[0]为1;
//非排列组合问题遍历顺序无所谓
组合问题一定外层遍历物品(相当于保证顺序),外层遍历容量的话组合会出现{1,5}{5,1}重复。
最小组合dp初始化MAX,递推加判断跳过MAX
排列问题外层for遍历背包,内层for循环遍历物品,因为需要重复。
【股票】
----dp构成:列维度表示操作持有与否,操作次数;行i表示天数递增。
[0]表示购入或持有;[1]表示卖出或无;加维表示多次或者冷静期等多状态
--单多次购买差别:仅单次购买,购入之前未持有利润是0。仅用一维数组就够用;
能够多次购买,购买之前利润是上一次未持有(抛出)的(最大)利润。dp[i][1]卖出之前肯定有本次(及之前)买入。
--限次购买:加列数组,第一次持有公式要写成单次购买的,其他次在上一次基础上操作;最多k次,少的话后维坍缩了而已。
【子序列】
----书写思路:dp[i]表示nums[i]结尾的最长递增子序列的长度
两层for:外层推进度;内层表示子序列上一位(注意j<i)
dp[i] = max(dp[i], dp[j] + 1); 不是两种情况,而是为了取ij组合的最大值
--注意:不再return最后位!不一定带最后一位最大!
【单调栈】
使用情况:一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。单调栈的本质是空间换时间,时间复杂度为O(n)。
//循环题可以访问两遍数组,结合%取余操作
//标引i可以通过数组访问数字,但是反过来不行。所以存储i相当于存了两个数据
【图论】
----深度优先搜索:类似回溯一条路搜到头再回溯
----广度优先搜索:最近一圈存入队列,类似二叉树层序遍历
----岛屿问题:
--如果和边界有关:可以四边搜索,而非双for全部遍历
【排序】
【快速排序】
---- 思路:取pivot位,小于pivot放左边,大于放右边
void quickselect(vector<int> &nums, int left, int right) {
//只要书写正确,与开闭区间无关。此处写的左闭右闭
if(left >= right) return; //不合理或者只一个数结束
int pivot = nums[right];
int i=left, j=right;
while(i < j){
while(i<j && nums[i]<=pivot) i++; //小于等于合理 大于等待处理
nums[j] = nums[i]; //right无用了需要被先覆盖。注意书写顺序!以及ij!
while(i<j && nums[j]>=pivot) j--; //由于判断条件加了i<j,最终i=j停止
//等于在哪边不重要
nums[i] = nums[j];
}
nums[j] = pivot;
quickselect(nums, left, i-1); //闭区间书写
quickselect(nums, i+1, right);
}
【上机测试经验】
---- 记得调成C++
---- 万能头文件#include<bits/stdc++.h>
---- 由于答案可能过大,请对10^9+7取模。
int mod = 1e9+7; ... res = res%mod; return res;
---- cin接收字符串遇到空格 本次输入就结束了!要想能接受空格:
-- getline可用于string
getline(cin, s); s是存储对象,不用初始化空间
-- cin.getline只能用于char组!
cin.getline(ch,10); //(字符数组名,截取字符个数)
实际ch只接受九个!因为有结束标志’\0’占一位!
cin.getline(ch,10,'*'); //(字符数组名,截取字符个数+1,结束标志)
并且第二个参数要比接收的字符串长度要+1!
-- cin接收容器char组,”或者’也算单个字符!因而需要直接输入目标字符不加边框。
---- 保留几位小数
#include<iomanip> cout<<fixed<<setprecision(5)<<x<<endl;
//setprecision(5)保留五位数字(整数、小数都算),fixed固定整数位不参与setprecision。
//合在一起意为保留五位小数。
---- 不同组的测试用例用\n隔开
\n就是换行符,new line 的意思。也即endl,cout<<x<<endl就完事了。
【c++易忘点】
----自定义排序
--sort:
bool cmp(const int &u, const int &v){
return u > v; }
sort(x.begin(), s.end(), cmp);
--set等自定义排序
set<int, cmp> s; //相当于把排序函数变成类型
class MyCompare {
public: bool operator()(int v1, int v2) {
return v1 > v2;
} };
---- 数字和字符串转换
to_string //int、long等数组转字符串
stoi、stoll //字符串转数字
----INT_MIN、INT_MAX //类型最小最大值
----位运算符
&(双1为1);|(有1为1);^异或(同0异1);~非
<< >> 左右移位(补零或者符号位)
---- 截取子字符串
string str = "Hello, World!";
str.substr(7, 5); // 从索引位置7开始截取长度为5的子字符串