// 二分查找主要适合有规律的数组,通过不断 移动指向数组中段的指针(不断缩小目标所在区间)
实现查找目标的目的。
在没有规律的数组中,一般用 双指针 去遍历寻找目标。
// 题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,
写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
class Solution:
public:
int search(vector<int>& nums , int target){
int left = 0;
int right = nums.size() - 1;
while(left <= right){ //这里的left<=right 主要还是与下面的middle计算方式有关
int middle = left + ((right - left)/2);
if(nums[middle] > target){
right = middle - 1;
}
else if(nums[middle] < target){
left = middle + 1;
}
else{
return middle;
}
}
return -1;
}
};
//题目: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
//如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
输入: nums = [1,3,5,6], target = 5
输出: 2
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n - 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;
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;
// 目标值插入数组中的位置 [left, right],return right + 1
// 目标值在数组所有元素之后的情况 [left, right], return right + 1
return right + 1;
}
};
题目:在排序数组中查找目标出现的第一个位置和最后位置。
示例:
输入:nums = [5,7,7,8,8,10],target = 8
输出:[3,4]
思路:
考虑三种情况:
1、target 不在数组的范围内 返回{-1,-1}
2、target 在数组范围内,但是数组中没有target,返回{-1,-1}
3、target 在数组中 返回其在数组中的位置
class Solution{
public:
vector<int> searchRange(vector<int>& nums,int target){
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// 情况一
if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
// 情况三
if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
// 情况二
return {-1, -1};
}
private:
int getRightBorder(vector<int>& nums,int target){
int left = 0;
int right = nums.size() - 1;
int rightBorder = -2; //记录rightBorder没有被赋值的情况
while(left <= right){
int middle = left + (right-left)/2;
if(nums[middle] > target){
right = middle - 1;
}
else{
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
int getLeftBorder(vector<int>& nums,int target){
int left = 0;
int right = nums.size() - 1;
int leftBorder = -2;
while(left <= right){
int middle = left + (right - left)/2;
if(nums[middle] >= target){
right = middle - 1;
leftBorder = right;
}
else{
left = middle + 1;
}
}
return leftBorder;
}
};
数组学习总结:
1、数组的元素只能覆盖,不能删除。例如先创建一个n*n的元素全为0 的矩阵,开辟一个空间,然后再对其特定位置进行赋值来覆盖其原本的值。
2、二维数组在内存空间并不是一个连续的地址空间。
// 滑动窗口法:使用双指针
/*
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足 其和 ≥ s 的长度最小的 连续子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
*/
//使用滑动窗口法,不断调节子序列的起始位置和终止位置,从而得出我们想要的结果
class Solution{
public:
int minSubArrayLen(int s,vector<int>& nums){
int result = INT32_MAX;
int sum = 0; //滑动窗口内的数值之和
int i = 0; //滑动窗口的起始位置
int subLength = 0; //滑动窗口的长度
for(int j = 0;j < nums.size(); j++){
sum += nums[j];
// key point
while(sum >= s){
subLength = j - i + 1;
result = result < subLength ? result : subLength;
sum -= nums[i++];
}
}
return result == INT32_MAX ? 0 : result;
}
};
螺旋矩阵
//使用vector创建数组方法:适用于每一行的列数相等的二维数组
/*
vector<vector<int>> matrix(m, vector<int>(n, -1));
//以下是拆分理解://创建一维数组matirx,这个数组里有m个元素,元素是int型vector。
vector<vector<int>> matrix(m);//除了定义数组类型及数组大小外,同时给数组中的元素赋值:将元素赋值为大小为n的
int型vector。
vector<vector<int>> matrix(m, vector<int>(n));//除了定义数组类型、数组大小、列的大小,同时给数组列中的元素(或者说,数组中的所
有元素)赋值为-1。
vector<vector<int>> matrix(m, vector<int>(n, -1));
*/
每一条边都只栈n-1,剩余角点的位置留给下一条边,如图:
/*
题目:给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出:
矩阵:
1 2 3
8 9 4
7 6 5
*/
class Solution{
public:
vector<vector<int>> generateMatrix(int n){
vector<vector<int>> res(n, vector<int>(n,0));
//vector定义二维数组
int startx = 0, starty = 0; //定义每循环一个圈的起始位置
int loop = n/2; //每个圈循环几次,例如n=3,则为3*3的矩阵,只需要循环最外边的一圈
int mid = n/2; //矩阵中间的位置,不需要循环直接赋值
int count = 1; //用来给矩阵每一个位置赋值
int offset = 1; //每一次循环都要控制每一条边遍历的长度
int i,j;
while(loop --){
i = startx;
j = starty;
//下面使用for循环模拟转一圈
//模拟填充最上行从左到右(左闭右开)
for (j = starty; j < starty + n - offset; j++){
res[startx[j]] = count++;
}
//模拟填充最右列从上到下(左闭右开)
for(i = startx; i < startx + n - offset; i++){
res[i][j] = count++;
}
//模拟填充最下行从右到左(左闭右开)
for(; j < starty; j--){
res[i][j] = count++;
}
//模拟填充左列从下到上(左闭右开)
for(; i > startx; i--){
res[i][j] = count++;
}
//第二圈开始的时候,起始位置变化。例如:(0,0) -> (1,1)
startx++;
starty++;
//offset 控制每一圈里面的每一条边的遍历长度
offset += 2;
}
//如果n为技术,需要单独给最中间的位置赋值
if(n % 2){
res[mid][mid] = count;
}
return res;
}
};
以上所有都引自“代码随想录”