系列综述:
目的:本系列是个人整理为了秋招面试
的,整理期间苛求每个知识点,平衡理解简易度与深入程度。
来源:材料主要源于LeetCodeHot100进行的,每个知识点的修正和深入主要参考各平台大佬的文章,其中也可能含有少量的个人实验自证。
结语:如果有帮到你的地方,就点个赞和关注一下呗,谢谢!!!
【C++】秋招&实习面经汇总篇
点此到文末惊喜↩︎
unordered_map
可通过O(1)
时间进行查找[left,right]区间的和为k
等价于 前right项的和 - 前left-1项的和 = k
前left-1项的和(历史值) = 前right项的和(先锋值) - k
。先锋值需要一直记录求解,而历史值可以将先锋值记录在map中,方便以O(1)时间查找int subarraySum(vector<int>& nums, int k) {
int sum = 0, res = 0;
// 第一次前缀和恰好等于k时,则sum为0,值为1
unordered_map<int, int> umap{{0, 1}};
for (int& c : nums) {
sum += c; // 记录和
if (umap.count(sum - k) > 0)
res += umap[sum - k];
++umap[sum]; // 如果有负数,不同位置可能有相同的前缀和
}
return res;
}
if (umap.count(target_key) > 0)
,判断目标元素是否存在vector<int> maxSlidingWindow(vector<int>& nums, int k) {
// 单调队列
deque<int> que;
// 弹出元素:若为left元素则弹出
auto pop = [&que](int value){
if (!que.empty() && value == que.front())
que.pop_front();
};
// 压入元素:从尾部弹出所有比压入值小的,保证队首元素是最大的
auto push =[&que](int value){
while (!que.empty() && value > que.back())
que.pop_back();
que.push_back(value);
};
vector<int> result;
for (int i = 0; i < k; i++) { // 先将前k的元素放进队列
push(nums[i]);
}
result.push_back(que.front()); // result 记录前k的元素的最大值
for (int i = k; i < nums.size(); i++) {
pop(nums[i - k]); // 滑动窗口移除最前面元素
push(nums[i]); // 滑动窗口前加入最后面的元素
result.push_back(que.front()); // 记录对应的最大值
}
return result;
}
// 返回字符串 s 中包含字符串 t 的全部字符的最小窗口
string SlideWindow(string s, string t) {
// need记录子串情况,window记录合适窗口
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
// 记录最小覆盖子串的起始索引及长度
int start = 0, len = INT_MAX;
int valid = 0;
while (right < s.size()) {
// 记录操作值,然后再进行复杂条件判断
char c = s[right]; // c 是将移入窗口的字符
right++; // 右移窗口
// 进行窗口内数据的一系列更新
if (need.count(c)) { // 判断need中是否存在c
window[c]++;
if (window[c] == need[c])
valid++;
}
while (valid == need.size()) { // TODO:收缩条件
// TODO:更新结果记录
if (right - left < len) {
start = left;// 更新起始值
len = right - left;// 最小长度
}
// 收缩窗口
char d = s[left];
left++;
// TODO:收缩处理
if (need.count(d)) {
if (window[d] == need[d])
valid--;
window[d]--;
}
}
}
// 返回最小覆盖子串
return len == INT_MAX ?
"" : s.substr(start, len);
}
过去状态
是否选择的一个标准// 贪心
int maxSubArray(vector<int>& nums) {
int result = INT32_MIN;
int count = 0;
for (int i = 0; i < nums.size(); i++) {
count += nums[i];
if (count > result) { // 取区间累计的最大值(相当于不断确定最大子序终止位置)
result = count;
}
if (count <= 0) count = 0; // 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
}
return result;
}
// 动态规划
int maxSubArray(vector<int>& nums) {
int len = nums.size();
// dp[i] 表示:以 nums[i] 结尾的连续子数组的最大和
vector<int> dp(len);
dp[0] = nums[0];
for (int i = 1; i < len; i++) {
// 状态转移:是否选择过去状态dp[i-1] + 当前nums[i]
if (dp[i - 1] > 0) {
dp[i] = dp[i - 1] + nums[i];
} else {
dp[i] = nums[i];
}
}
// 也可以在上面遍历的同时求出 res 的最大值,这里我们为了语义清晰分开写,大家可以自行选择
int res = dp[0];
for (int i = 1; i < len; i++) {
res = max(res, dp[i]);
}
return res;
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (!intervals.size()) return {};
sort(intervals.begin(), intervals.end(), less<vector<int>>());
int slow = 0;
int fast = 1;
while (fast < intervals.size()) {
if (intervals[slow][1] >= intervals[fast][0]) {
intervals[slow][1] = max(intervals[fast][1], intervals[slow][1]);
} else {
slow++;
intervals[slow] = intervals[fast];
}
fast++;
}
intervals.resize(slow+1);
return intervals;
}
void reverse(vector<int>& nums, int start, int end) {
while (start < end) {
swap(nums[start], nums[end]);
start += 1;
end -= 1;
}
}
void rotate(vector<int>& nums, int k) {
k %= nums.size();
reverse(nums, 0, nums.size() - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.size() - 1);
}
res[i] = MUL(0, i-1) * MUL(i+1, end)
(写公式,分析公式)vector<int> productExceptSelf(vector<int>& nums) {
const int n = nums.size();
vector<int> res(n, 0);
// res中每个位置左边的乘积
int k = 1;
for (int i = 0; i < n; i ++) {
res[i] = k;
k *= nums[i];
}
// res中每个位置乘以右边的乘积
k = 1;
for (int i = n - 1; i >= 0; i --) {
res[i] *= k;
k *= nums[i];
}
return res;
}
// 原地交换
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
for(int i = 0; i < n; i++){
// while继续确定交换回来数的位置
while(nums[i] >= 1 && nums[i] <= n // 规定遍历范围
&& nums[i] != nums[nums[i] - 1]){ // 避免原地交换,因为已经成功了
swap(nums[i], nums[nums[i] - 1]); // 将值nums[i]交换到目标位置num[nums[i] - 1]
}
}
for(int i = 0; i < n; i++){
if(nums[i] != i + 1)
return i + 1;
}
return n + 1;
}
// 哈希法(空间O(n))
int firstMissingPositive(vector<int>& nums) {
int n=nums.size();
unordered_map<int,bool> hashmap;
for(int& num:nums){
hashmap[num]=true;
}
for(int i=1;i<=n;i++){
if(hashmap[i]==false) return i;
}
return n+1;
}
解决的问题:
给定一个线性表(字符串、数组等),一次遍历求满足指定条件的连续子部分
typedef struct {
int x;
int y;
} Cord;
void setZeroes(vector<vector<int>>& matrix) {
// 使用两个unordered_set分别存储需要置为0的行和列
unordered_set<int> row_record;
unordered_set<int> col_record;
for (int i = 0; i < matrix.size(); ++i) {
for (int j = 0; j < matrix[0].size(); ++j) {
if (matrix[i][j] == 0) {
row_record.emplace(i);
col_record.emplace(j);
}
}
}
for (auto i : row_record) {
for (int p = 0; p < matrix[0].size(); ++p) {
matrix[i][p] = 0;
}
}
for (auto j : col_record) {
for (int p = 0; p < matrix.size(); ++p) {
matrix[p][j] = 0;
}
}
}
vector<int> spiralOrder(vector<vector<int>>& matrix) {
// 健壮性检查
if (matrix.empty()) return vector<int>();
vector<int> res;
int up = 0, down = matrix.size()-1;
int left = 0, right = matrix[0].size()-1;
while (up <= down && left <= right) {
// 从左到右
for (int i = left; i <= right; ++i)
res.push_back(matrix[up][i]);
++up; // 缩小上边界
if (up > down) break;
// 从上到下
for (int i = up; i <= down; ++i)
res.push_back(matrix[i][right]);
--right;
if (left > right) break;
// 从左到右
for (int i = right; i >= left; i--)
res.push_back(matrix[down][i]);
--down;
// 从左到右
for (int i = down; i >= up; i--)
res.push_back(matrix[i][left]);
++left;
}
return res;
}
原索引位置matrix[i][j] -> 旋转后索引位置matrix[i][n-1-i]
// 直接映射
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
// 深拷贝 matrix -> tmp
vector<vector<int>> tmp = matrix;
// 根据元素旋转公式,遍历修改原矩阵 matrix 的各元素
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
matrix[j][n - 1 - i] = tmp[i][j];
}
}
}
// 空间优化
void rotate(vector<vector<int>>& matrix) {
// 设矩阵行列数为 n
int n = matrix.size();
// 起始点范围为 0 <= i < n / 2 , 0 <= j < (n + 1) / 2
// 其中 '/' 为整数除法
for (int i = 0; i < n / 2; i++) { // 一共转的圈数
for (int j = i; j < n-i-1; j++) { // 每一圈要处理一行对应旋转一次
// 暂存 A 至 tmp
int tmp = matrix[i][j];
// 元素旋转操作 A <- D <- C <- B <- tmp
matrix[i][j] = matrix[n - 1 - j][i];
matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j];
matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i];
matrix[j][n - 1 - i] = tmp;
}
}
}
while(条件) { ··· ++p;}
m*n
还是n*n
原索引位置matrix[i][j] -> 旋转后索引位置matrix[i][n-1-i]
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(!matrix.size() && !matrix[0].size()) return false;
int i = 0, j = matrix[0].size() - 1; //矩阵右上角
while(i < matrix.size() && j >= 0) {
if(matrix[i][j] == target) return true;
else if( matrix[i][j] < target) i++; //排除一行
else if( matrix[i][j] > target) j--; //排除一列
}
return false;
}
点此跳转到首行↩︎