1. 704. 二分查找 - 力扣(LeetCode)
哎哟 我去 我还以为你都搞懂了 呵呵
当时问题出现在右边界初始化
左闭右开 右边界是取不到的
int left = 0, right = nums.size() ;
while(left < right) {
int mid = left + (right - left) / 2;
if( target > nums[mid]){
left = mid + 1;
}
if( target < nums[mid]){
right = mid;
}
else
return mid;
}
左闭右闭 右边界可以取到
int left = 0, right = nums.size() - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if( target > nums[mid]){
left = mid + 1;
}
if( target < nums[mid]){
right = mid - 1;
}
else
return mid;
}
2. 27. 移除元素 - 力扣(LeetCode)
因为不能开辟新的空间,所以考虑用双指针,左指针的值等于val就跟右指针交换放到右边去,然后再把左指针往前移一位,右指针向前移动一位,继续寻找指向不等于val的元素。左指针的值不等于val就直接将左指针后移,右指针不需要移动。
本质:把val放在右边,不等于val的放在左边
int left = 0, right = nums.size() - 1;
for( int left = 0; left <= right; left++){
if( nums[left] == val){
swap(nums[left], nums[right]);
left--, right--;
}
}
return ++right;
因为最后右指针往前移所以返回的时候要加回来,要不然就少了不等于val的元素
左右元素交换后,右指针前移没问题。但是为什么左右元素交换后左指针还要向前移动一次呢?因为左指针等于val时可能右指针也等于val,此时左右元素交换后,左指针指向的值还是val,如果交换完直接将左指针后移那就出错辣,留下了val在左边;但是如果此时将左指针前移动,左指针指向的值不是val,右指针指向的值不是val,此时循环又将左指针往后移了,但是右指针的值不是val;这样就不会有遗漏的val在左边。总而言之,左右交换后,左指针再次前移是为了规避右指针所指向元素也为val时带来的影响。
3. 977. 有序数组的平方 - 力扣(LeetCode)
这道题可以直接用暴力,先都平方后然后再排序,但是会超时,不知道是不是因为我用的冒泡排序,私密马赛
巧妙的点在原数组是非递减、有正有负序列,也就是说数组平方后的最大元素不是在第一个就是在最后一个,你想想嘛!那么就可以用双指针,一个指针看左边一个指针看右边。如果左指针平方后的元素小于右指针平方后的元素,就先把左指针平方后的元素放入结果集,然后左指针向右移动;如果右指针平方后的元素小于左指针,就先把右指针平方后的元素放入结果集,然后右指针向左移动;知道左右指针相遇后可以结束循环。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int l = nums.size() - 1;
int i = 0;
vector<int> res(nums.size(), 0);
for(int i = 0, j = nums.size() - 1; i <= j;){
if(nums[i] * nums[i] > nums[j] * nums[j]){
res[l--] = nums[i] * nums[i];
i++;
}
else{
res[l--] = nums[j] * nums[j];
j--;
}
}
return res;
}
};
4. 209. 长度最小的子数组 - 力扣(LeetCode)
这个题其实还挺有意思的
暴力做法:
两个for循环嵌套,其实也可以看成左右双指针咯,左指针(外循环)遍历起点,右指针(内循环)遍历终点(找到子数组总和大于等于target的)。 获取子数组长度,求数组元素总和,当子数字元素总和大于目标值的时候,我们就可以去寻找新的起点,看看有没有更短长度的子数组
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int res = INT32_MAX;
int sum = 0, sub = 0;
for(int i = 0; i < nums.size(); i++){
sum = 0; // 每次得到一个新得子数组 总和初始位0
for(int j = i; j < nums.size(); j++){
sum += nums[j];
if( sum >= target) {
sub = j - i + 1;
res = res < sub ? res : sub; // 取较小值
break; // 已经得知这个子数组大于等于target了不用继续加元素了
}
}
}
return res = res == INT32_MAX ? 0 : res;
}
};
力扣上暴力是过不了的,要用滑动窗口
滑动窗口:
暴力方法用两个for循环,滑动窗口用一个for循环里面嵌套while
关键的点是for循环遍历的是子数组的起点还是终点的,当然是终点辣~~ 要是是起点不又回到的暴力做法,嘻嘻。在for循环内遍历终点,求子数组元素总和,当总和大于等于目标值的时候,我们就求长度,然后后移起点,不断地去找新的起点(减掉第一个值)
我觉得有点像,for循环把所有的子数组都求出来,然后while循环操作子数组:挑选出满足条件的子数组然后在这些子数组中后移子数组的起点,看能不能找到长度更小的
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int res = INT32_MAX;
int i = 0, sum = 0, sub = 0;
for(int j = 0; j < nums.size(); j++){
sum += nums[j];
while(sum >= target){
sub = j - i + 1;
res = res < sub ? res : sub;
sum -= nums[i++]; //这里!!
}
}
return res == INT32_MAX ? 0 : res;
}
};
sum -= nums[i++];
更改起点:
-:去掉此时第一个元素参与 ++:下一个子数组起点
5. 59. 螺旋矩阵 II - 力扣(LeetCode)
没有什么算法含量,主要是模拟,考验对代码掌握的熟练程度
一开始懵懵懂懂晕头转向,现在看觉得挺巧妙挺有意思的
是这样一圈圈来的 是这样左闭右开的 最后的mid是这样的
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int loop = n / 2; //要转多少圈
int mid = n / 2; // 如果是奇数 最后的中间点单独操作
int offset = 1; // 每次转完一圈 边界往里面缩一圈
int count = 1; // 数值 元素
int startx = 0, starty = 0; // 每一圈的起点
int i, j; // 位置
vector<vector<int>> res(n, vector<int>(n, 0));
while(loop--){
i = startx, j = starty;
for(j; j < n - offset; j++){
res[i][j] = count++;
}
for(i; i < n - offset; i++){
res[i][j] = count++;
}
for(; j > startx; j--){
res[i][j] = count++;
}
for(; i > starty; i--){
res[i][j] = count++;
}
startx++, starty++, offset++;
}
// 如果方阵是奇数行奇数列 剩下最中间那个单独赋值
if(n % 2){
res[mid][mid] = count;
}
return res;
}
};
左闭右开左闭右开 坚持循环不变量(其实这句话我还不是很明白)