目录
1--二分查找法
2--二分查找法进阶
2-1--寻找第一个等于目标值的位置
2-2--寻找最后一个等于目标值的位置
3--双指针算法
3-1--快慢指针移除元素
3-2--有序数组的平方
4--滑动窗口算法
5--循环不变量
二分查找法用于有序数组的元素查找,一般可以分为左闭右闭写法、左闭右开写法、左开右闭写法,其中左闭右闭写法最常用;
#include
#include
class Solution {
public:
// 左闭右闭写法
int search1(std::vector& arr, int target){
int left = 0, right = arr.size() - 1;
while(left <= right){ // 因为是左闭右闭写法,当left == right时[left, right]也是合法的,因此left <= right
int mid = left + (right - left) / 2;
if(arr[mid] > target){
right = mid - 1;
}
else if(arr[mid] < target){
left = mid + 1;
}
else return arr[mid];
}
return -1; // 表示未找到
}
// 左闭右开写法
int search2(std::vector& arr, int target){
int left = 0, right = arr.size();
while(left < right){ // 因为是左闭右开写法,当left == right时[left, right)是非法的,因此left < right
int mid = left + (right - left) / 2;
if(arr[mid] > target){
right = mid; // 右开
}
else if(arr[mid] < target){
left = mid + 1; // 左闭
}
else return arr[mid];
}
return -1; // 表示未找到
}
// 左开右闭写法
int search3(std::vector& arr, int target){
int left = -1, right = arr.size() - 1;
while(left < right){ // 因为是左闭右开写法,当left == right时(left, right]是非法的,因此left < right
int mid = left + (right - left) / 2;
if(arr[mid] > target){
right = mid - 1; // 右闭
}
else if(arr[mid] < target){
left = mid; // 左开
}
else return arr[mid];
}
return -1; // 表示未找到
}
};
int main(int argc, char *argv[]){
std::vector test = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int target = 2;
Solution S1;
int res1 = S1.search1(test, target);
int res2 = S1.search2(test, target);
int res3 = S1.search3(test, target);
std::cout << res1 << ", " << res2 << ", " << res3 << std::endl;
return 0;
}
等于号 = 放在哪个条件判断的依据:当 arr[mid] == target 时,应该往哪一边继续二分寻找;
#include
#include
class Solution {
public:
// 左闭右闭写法
int search1(std::vector& arr, int target){
int left = 0, right = arr.size() - 1;
int res = -1;
while(left <= right){
int mid = left + (right - left) / 2;
if(arr[mid] >= target){
res = mid; // 记录可能的位置
right = mid - 1;
}
else if(arr[mid] < target){
left = mid + 1;
}
}
if(res == -1 || arr[res] != target) return -1; //未找到target
return res;
}
};
int main(int argc, char *argv[]){
std::vector test = {1, 2, 2, 2, 3, 3, 4};
int target = 2;
Solution S1;
int res1 = S1.search1(test, target);
std::cout << res1 << std::endl;
return 0;
}
等于号 = 放在哪个条件判断的依据:当 arr[mid] == target 时,应该往哪一边继续二分寻找;
#include
#include
class Solution {
public:
// 左闭右闭写法
int search1(std::vector& arr, int target){
int left = 0, right = arr.size() - 1;
int res = -1;
while(left <= right){
int mid = left + (right - left) / 2;
if(arr[mid] > target){
right = mid - 1;
}
else if(arr[mid] <= target){
res = mid; // 记录可能的位置
left = mid + 1;
}
}
if(res == -1 || arr[res] != target) return -1; //未找到target
return res;
}
};
int main(int argc, char *argv[]){
std::vector test = {1, 2, 2, 2, 3, 3, 4};
int target = 2;
Solution S1;
int res1 = S1.search1(test, target);
std::cout << res1 << std::endl;
return 0;
}
#include
#include
class Solution {
public:
int removeElement(std::vector& nums, int val) {
int slow = 0, fast = 0;
while(fast < nums.size()){
if(nums[fast] != val){
nums[slow] = nums[fast];
slow++;
fast++;
}
else{
fast++;
}
}
return slow;
}
};
int main(int argc, char argv[]){
// nums = [3,2,2,3], val = 3
std::vector test = {3, 2, 2, 3};
int val = 3;
Solution S1;
int res = S1.removeElement(test, val);
std::cout << res << std::endl;
}
主要思路:
由于数组包含负数,则平方后的数组符合:大→小→大的排列规律;可以利用双指针算法从两端向中间遍历,取最大值放在结果数组(提前开辟);
#include
#include
class Solution {
public:
std::vector sortedSquares(std::vector& nums) {
std::vector res(nums.size(), 0);
int i = 0, j = nums.size() - 1, k = nums.size() - 1;
while(i <= j){
if(nums[i]*nums[i] > nums[j]*nums[j]){
res[k] = nums[i]*nums[i];
k--;
i++;
}
else{
res[k] = nums[j]*nums[j];
k--;
j--;
}
}
return res;
}
};
int main(int argc, char argv[]){
// nums = [-4,-1,0,3,10]
std::vector test = {-4, -1, 0, 3, 10};
Solution S1;
std::vector res = S1.sortedSquares(test);
for(auto v : res) std::cout << v << " ";
return 0;
}
主要思路:
维护一个滑动窗口,当满足滑动窗口内元素和 》= target 时,判断是否更新最小长度;
#include
#include
class Solution {
public:
int minSubArrayLen(int target, std::vector& nums) {
int i = 0, j = 0;
int sum = 0, min_len = nums.size() + 1;
while(j < nums.size()){
sum += nums[j];
while(sum >= target){
min_len = std::min(min_len, j - i + 1);
//缩小滑动窗口范围
sum = sum - nums[i];
i++;
}
j++; //寻找下一个满足条件的滑动窗口
}
return min_len == nums.size() + 1 ? 0 : min_len;
}
};
int main(int argc, char argv[]){
// target = 7, nums = [2,3,1,2,4,3]
std::vector test = {2, 3, 1, 2, 4, 3};
int target = 7;
Solution S1;
int res = S1.minSubArrayLen(target, test);
std::cout << res << std::endl;
return 0;
}
主要思路:
定义循环不变量:遵循左闭右开的循环遍历规则;
#include
#include
class Solution {
public:
std::vector> generateMatrix(int n) {
std::vector> res(n, std::vector(n, 0));
int offset = 1, count = 1;
int start_x = 0, start_y = 0, i, j;
int loop = n / 2;
while(loop--){
i = start_x;
j = start_y;
// 上边界左闭右开
for(; j < n - offset; j++){
res[i][j] = count;
count++;
}
// 右边界上闭下开
for(; i < n - offset; i++){
res[i][j] = count;
count++;
}
// 下边界右闭左开
for(; j > start_y; j--){
res[i][j] = count;
count++;
}
// 左边界下闭上开
for(; i > start_x; i--){
res[i][j] = count;
count++;
}
offset++;
start_x++;
start_y++;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2 == 1) {
res[n/2][n/2] = count;
}
return res;
}
};
int main(int argc, char argv[]){
// n = 4
int test = 4;
Solution S1;
std::vector> res = S1.generateMatrix(test);
for(auto v : res){
for(auto item : v) std::cout << item << " ";
std::cout << std::endl;
}
return 0;
}