刷题的第一天,从头开始学习。只要学习都是收获。
题目704
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1
题目链接:https://leetcode.cn/problems/binary-search/
文章讲解:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html
视频讲解:https://www.bilibili.com/video/BV1fA4y1o715
思路
本题是使用二分法来解题。因为没学过数据结构,所以根本没听说过二分法。
首先想到的就是使用循环去遍历数组中的每一个元素,判断是否与目标值相等。相等的话就输出下表,否则返回-1。
二分查找法
先去学习了一下什么是二分法。
使用二分法的前提:
1、数组为有序数组
2、数组中无重复元素
二分查找法的过程可以简单描述为如下过程:
1、使用了while循环和for循环。将目标元素和查找范围的中间值做比较(如果目标元素=中间值,查找结束),将目标元素分到较大/或者较小的一组。
2、通过分组,可以将查找范围缩小一半。
3、重复第三步,直到目标元素=新的范围的中间值,查找结束。
相关内容的学习参考了如下的链接: https://blog.csdn.net/iLoyo_/article/details/131491911
解答
根据区间的不同,结果可以分为两种情况
第一种是区间左闭右闭
class Solution {
public:
int search(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1; // 这个地方要减去1
while (left <= right) // 当left==right,区间[left, right]依然有效,所以用 <=
{
int middle = (right - left) / 2;//注意这一句,一定要写在while循环里面,因为每一次都要更新middle的值
if (nums[middle] > target)
{
right = middle - 1; // target 在左区间,所以[left, middle - 1]
}
else if (nums[middle] < target)
{
left = middle + 1; // target 在右区间,所以[middle + 1, right]
}
else
return middle; // 数组中找到目标值,直接返回下标
}
return -1;//不存在返回-1
}
};
第二种是左闭右开
class Solution {
public:
int search(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size(); //right不能在区间内,不用减去1
while (left < right) // left == right的时候,[left, right)是无效的空间,所以使用 <
{
int middle = (right - left) / 2;
if (nums[middle] > target)
{
right = middle; // target 在左区间,在[left, middle)中
}
else if (nums[middle] < target)
{
left = middle + 1; // target 在右区间,在[middle + 1, right)中
}
else
{
return middle; // 数组中找到目标值,直接返回下标
}
}
return -1;
}
};
二分法从原理上来理解不难,容易写错的地方就是区间的问题。要注意。
补充(太久没看了,都是自己忘得很基础的知识呀)
c++只会有一个返回值
题目35
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
题目链接:https://leetcode.cn/problems/search-insert-position/
文章讲解:
https://programmercarl.com/0035.%E6%90%9C%E7%B4%A2%E6%8F%92%E5%85%A5%E4%BD%8D%E7%BD%AE.html#%E6%80%9D%E8%B7%AF
思路
学习了704后,看到了有序、无重复的数组,就想到了使用二分法
很顺利的就写出了代码
但在最后写返回按顺序插入的位置的时候,没明白怎么写才好
看了讲解之后,发现最后是返回left或者right+1,一开始还不明白为什么会这样写。
后来画图看了看才明白
解题
class Solution {
public:
int searchInsert(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1;
while(left <= right)
{
int middle = (left+right)/2;
if(nums[middle] > target)
{
right = middle - 1;
}
else if(nums[middle] < target)
{
left = middle +1;
}
else
return middle;
}
//left>right跳出了循环
return right+1;
}
};
题目27
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
题目链接:https://leetcode.cn/problems/remove-element/
文章讲解:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html
视频讲解:https://www.bilibili.com/video/BV12A4y1Z7LP
思路
看到这道题的第一时间,想到的就是对数组进行循环遍历,判断数组中的每一个元素是否与目标值相等,然后将不相等的元素都取出来构成新的数组。
但是题目不能用另外一个新数组
后面有想到用不重复的元素去覆盖重复的元素
因为要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
解答
有两种方法
第一种暴力求解,具体实现过程和思路中描述的差不多
是把数组中与目标数相同的元素给覆盖掉
class Solution {
public:
int removeElement(vector<int>& nums, int val)
{
int len = nums.size();
for(int i = 0; i < len; i++)
{
if(nums[i]==val)
{
for(int j = i+1; j < len; j++)
{
nums[j-1]=nums[j];
}
i--;
len--;
}
}
return len;
}
};
第二种使用了双指针
是把数组中与目标数不同的元素取出来构成新的数组
真的是一种很妙的方法
class Solution {
public:
int removeElement(vector<int>& nums, int val)
{
int len = nums.size();
int slow = 0;
for(int fast=0; fast < len; fast++)
{
if(nums[fast]!=val)
{
nums[slow] =nums[fast];
slow++;
}
}
return slow;
}
};
双指针法使用了两个指针——一个快指针和一个慢指针
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置
一定要搞清楚这两个指针的定义才能看懂解题过程
今日总结
今天是练习的第一天。
对于c++来说,自己已经很久没有写过了,平时连看的都不多。之前也只跟着书本学过一遍,只敲过案例,很多基础语法也都忘了。后面一点一点的学习。只要学习了就有收获。