704. 二分查找
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
示例 1:
输入:nums
= [-1,0,3,5,9,12],target
= 9 输出: 4 解释: 9 出现在nums
中并且下标为 4
示例 2:
输入:nums
= [-1,0,3,5,9,12],target
= 2 输出: -1 解释: 2 不存在nums
中因此返回 -1
看到题目后想,不就是一道简单的二分查找,看我手拿把掐,只需要设置左右指针,就可以轻松解决。
代码:
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
if(left==right&&target==nums[0])return 0;
while(left<right){
int middle=(left+right)/2;
if(nums[middle]==target)return middle;
else if(nums[middle]>target)right=middle-1;
else if(nums[middle]<target)left=middle+1;
if(left==right&&target==nums[left])return left;
}
return -1;
}
};
但是这个是自己写的代码,因为没搞清楚数组下标变化以及判断条件,所以修改了很多次才ac,这个代码的运行内存占比过大,效率不高,只能说勉强是对的。啊~这个丑陋的代码。
所以后面又去看了看评论区大神的题解。不得不说,大神将空间换时间思想体现得淋漓尽致。
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left<=right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target) {
return mid;
} else if(nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
作者:画手大鹏
之后看了代码随想录提供的几种方法,真的有种打开新世界的感觉,以前只知道左右指针,根本没仔细想过区间开闭问题。
27. 移除元素
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素。元素的顺序可能发生改变。然后返回 nums
中与 val
不同的元素的数量。
假设 nums
中不等于 val
的元素数量为 k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。 - 返回
k
。
用户评测:
评测机将使用以下代码测试您的解决方案:
int[] nums = [...]; // 输入数组 int val = ...; // 要移除的值 int[] expectedNums = [...]; // 长度正确的预期答案。 // 它以不等于 val 的值排序。 int k = removeElement(nums, val); // 调用你的实现 assert k == expectedNums.length; sort(nums, 0, k); // 排序 nums 的前 k 个元素 for (int i = 0; i < actualLength; i++) { assert nums[i] == expectedNums[i]; }
如果所有的断言都通过,你的解决方案将会 通过。
示例 1:
输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2,_,_] 解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3,_,_,_] 解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。 注意这五个元素可以任意顺序返回。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。 第一次看到这个题目还有点看不懂,为什么莫名奇妙给了我一大串代码,后面懂了,那是测试代码。那么回到题目本身,他是要求算出给出的数组中有多少个元素是非target元素,并且将这些元素集中放在数组的前面就可以。我首先想到的是左右指针,然后通过判断条件来将左右指针指向的值互换。 下面是我的代码:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int left=0;
int right=nums.size()-1;
int k=0;
while(left<=right){
if(nums[left]==val&&nums[right]!=val){
nums[left]=nums[right];
left++;
right--;
k++;
}
else if(nums[right]==val){
right--;
k++;
}
else if(nums[left]!=val){
left++;
}
}
return nums.size()-k;
}
};
效率也许是比较高,只是也许,因为有时候执行用时也会到3ms,而且消耗内存分布较高。
接下来看看代码随想录给出的几种解法:
双指针法
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
很多同学这道题目做的很懵,就是不理解 快慢指针究竟都是什么含义,所以一定要明确含义,后面的思路就更容易理解了。
删除过程如下:
可以看出来就是慢指针在碰到target数据时会停下来,等待快指针找到后面的非target数据并将该数据覆盖target数据。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
};
时间复杂度:O(n)
977. 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]
本题主要是掌握双指针法,以及抓住一个点,就是最大平方数只能在两边取得,这道题我自己没想出来用双指针的好办法,所以直接看了代码随想录的解题方法。
数组其实是有序的, 只不过负数平方之后可能成为最大数了。
那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法了,i指向起始位置,j指向终止位置。
定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
如果A[i] * A[i] < A[j] * A[j]
那么result[k--] = A[j] * A[j];
。
如果A[i] * A[i] >= A[j] * A[j]
那么result[k--] = A[i] * A[i];
。
如动画所示:
代码如下:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int k=nums.size()-1;
vector<int>results(nums.size(),0);
for(int i=0,j=nums.size()-1;i<=j;){
if((nums[i]*nums[i])>=(nums[j]*nums[j])){
results[k--]=nums[i]*nums[i];
i++;
}
else if(nums[i]*nums[i]<nums[j]*nums[j]){
results[k--]=nums[j]*nums[j];
j--;
}
}
return results;
}
};
时间复杂度:O(n);
空间复杂度:O(1);