学习来源:
代码随香炉:https://www.programmercarl.com/
labuladong算法:https://labuladong.github.io/algo/
数组
前缀和数组
区域和检索 - 数组不可变 (一维)
size设置大于n,为了统一起来,主要有一个元素的情况 比如 sum[1]-sums[0], sums[0] = 0
实现一个类,实现o1的复杂度,计算区域和
二维区域和检索 - 矩阵不可变 (二维矩阵)
和为K的子数组个数
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。
暴力解法 超出时间限制
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int count = 0;
int len = nums.size();
for (int left = 0; left < len; left++) {
int sum = 0;
// 区间里可能会有一些互相抵销的元素
for (int right = left; right < len; right++) {
sum += nums[right];
if (sum == k) {
count++;
}
}
}
return count;
}
};
前缀和 + 哈希表优化
差分数组
前缀和主要适用的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和。
差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。
区间加法
航班预定统计
class Solution {
public:
vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
vector<int> nums(n);
for (auto& booking : bookings) {
nums[booking[0] - 1] += booking[2];
if (booking[1] < n) {
nums[booking[1]] -= booking[2];
}
}
for (int i = 1; i < n; i++) {
nums[i] += nums[i - 1];
}
return nums;
}
};
拼车
trips = [[2,1,5],[3,3,7]], capacity = 4
class Solution {
public:
bool carPooling(vector<vector<int>>& trips, int capacity) {
vector<int> d(1001);
for(auto &e: trips) {
d[e[1]] += e[0];
d[e[2]] -= e[0];
}
for(int i = 0; i <= 1000; i ++ ) {
if(i) d[i] += d[i - 1];
if(d[i] > capacity) return false;
}
return true;
}
};
二分查找
二分查找
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
return middle; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
};
搜索插入位置(返回target index 或者插入的index)
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;
// 目标值插入数组中的位置 [left, right],return right + 1
// 目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1
搜索插入位置(上下界)
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if(nums.size()==0){
return {-1,-1};
}
int l =lb(nums,target);
int r =rb(nums,target);
return {l,r};
}
int lb(vector<int>& nums,int target){
int l=0,r=nums.size()-1;
while(l<=r){
int mid = l+(r-l)/2;
if(nums[mid]<target){
l=mid+1;
}else if(nums[mid]>target){
r = mid-1;
}else if(nums[mid]==target){
r = r-1;
}
}
if(l>=nums.size()||nums[l]!=target){
return -1;
}
return l;
}
int rb(vector<int>& nums,int target){
int l=0,r=nums.size()-1;
while(l<=r){
int mid = l+(r-l)/2;
if(nums[mid]<target){
l=mid+1;
}else if(nums[mid]>target){
r = mid-1;
}else if(nums[mid]==target){
l = l+1;
}
}
if(r<0||nums[r]!=target){
return -1;
}
return r;
}
};
x的平方根
class Solution {
public:
int mySqrt(int x) {
int l = 0, r = x, ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if ((long long)mid * mid <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
};
有效的完全平方数
给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
输入:num = 16
输出:true
双指针法
双指针技巧总纲
1. 快慢指针 删除有序数组 和 有序链表的重复项
2. 左右指针 二分查找+有序数组的两数之和
3. 反转数组
4. 回文串判断
boolean isPalindrome(String s) {
// 一左一右两个指针相向而行
int left = 0, right = s.length() - 1;
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
5. 最长回文子串
找回文串的难点在于,回文串的的长度可能是奇数也可能是偶数,解决该问题的核心是从中心向两端扩散的双指针技巧。
如果回文串的长度为奇数,则它有一个中心字符;如果回文串的长度为偶数,则可以认为它有两个中心字符。所以我们可以先实现这样一个函数:
// 在 s 中寻找以 s[l] 和 s[r] 为中心的最长回文串
String palindrome(String s, int l, int r) {
// 防止索引越界
while (l >= 0 && r < s.length()
&& s.charAt(l) == s.charAt(r)) {
// 双指针,向两边展开
l--; r++;
}
// 返回以 s[l] 和 s[r] 为中心的最长回文串
return s.substring(l + 1, r);
}
这样,如果输入相同的 l 和 r,就相当于寻找长度为奇数的回文串,如果输入相邻的 l 和 r,则相当于寻找长度为偶数的回文串。
移除元素
双指针方法
删除有序数组中的重复项
移动零(将0移动到末尾,保持相对顺序)
比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
输入:s = “ab#c”, t = “ad#c”
输出:true
解释:s 和 t 都会变成 “ac”。
法一:使用栈,重构字符串比价
法二:双指针法
M+N 1
有序数组的平方(有正负数)
方法一:直接排序
方法二:找分界线,归并排序
方法三:双指针
滑动窗口
长度最小的子数组 连续
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
2*n 1
水果成篮
哈希map 套滑动窗口的模板
最小覆盖子串
本问题要求我们返回字符串 s 中包含字符串 t的全部字符的最小窗口。我们称包含 tt 的全部字母的窗口为「可行」窗口。
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
找所有字母的异位词(同找排列问题)
相当于,输入一个串 S,一个串 T,找到 S 中所有 T 的排列,返回它们的起始索引
字符串排列( t 的某个排列 在 s中出现)
最长无重复子串
剑指offerII的题目
模拟行为
翻转图像
请你将图像顺时针旋转 90 度。
主对角线镜像对称,然后每一行翻转。
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
// 先沿对角线反转二维矩阵
for(int i=0;i<=m-1;i++){
for(int j =i;j<=n-1;j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
// 然后反转二维矩阵的每一行
for(int i = 0;i<m;i++){
int left = 0,right = matrix[0].size()-1;
// 反转一维数组
while(left<right){
int temp = matrix[i][left];
matrix[i][left] = matrix[i][right];
matrix[i][right] = temp;
left++;
right--;
}
}
}
};
螺旋矩阵(顺时针遍历->vector中)
螺旋矩阵II(顺时针生成矩阵)
你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
顺时针打印矩阵
同螺旋矩阵(顺时针遍历->vector中)