代码随想录算法训练营第一天|704.二分查找、27.移除元素、977.有序数组的平方
一、leetcode704.二分查找
题目链接:https://leetcode.cn/problems/binary-search/
1.左闭右闭
由于之前跟着王道系统地学过一遍并有笔记,在快速回顾笔记之后就立马做题了。
上来不到十分钟就把代码写好了,结果运行不了,开始自我怀疑。又看了一下,发现 mid = (low + high) / 2中的括号没加,改玩之后就能正常运行了,真是太粗心了。
int search(vector<int>& nums, int target) {
int low = 0, high = nums.size() , mid;
mid = (low + high)/2;
while(low <= high)
{
if(nums[mid]==target)
return mid;
else if(nums[mid]<target)
{
low = mid + 1;
mid = (low + high)/2;
}
else if(nums[mid]>target)
{
high = mid - 1;
mid = (low + high)/2;
}
}
return -1;
}
2.左闭右开
左闭右开的解法如下:
int search(vector<int>& nums, int target) {
int low = 0, high = nums.size() , mid;
mid = (low + high)/2;
while(low < high)
{
if(nums[mid]==target)
return mid;
else if(nums[mid]<target)
{
low = mid + 1;
mid = (low + high)/2;
}
else if(nums[mid]>target)
{
high = mid;
mid = (low + high)/2;
}
}
return -1;
}
左闭右开的不同点就是high指向的是原来的后一个元素,即:
1.在初始时high = nums.size();
2.在过程中,high = mid;
但是对于while循环的判断条件,我还是有疑问的,不确定加不加等号。在看完讲解之后,我意识到了:
左闭右开的本质就是在[low,high)这个区间内折半查找,所以low = high是没有意义的
。由此我也明白了为什么要令high = mid:
此时要mid的左边折半查找,由于右开又要求包含mid左边所有的元素所以此时只有让high =
mid才能成立。用卡哥的话说,重点还是对区间定义的理解。
二、leetcode27.移除元素
题目链接:https://leetcode.cn/problems/remove-element/
1.暴力(失败)
上来我先按提示用暴力解法解了一遍,代码如下:
int removeElement(vector<int>& nums, int val) {
int k = 0;
for(int i =0;i<nums.size();i++)
{
if(nums[i] != val)
k++;
}
vector<int> arr(nums.size(),0);
int j = 0;
for(int i = 0;i <nums.size();i++)
{
if(nums[i] != val)
{
arr[j] = nums[i];
j++;
}
}
for(int i = 0;i < arr.size();i++)
{
nums[i] = arr[i];
}
return k;
}
虽说是暴力,但是时间复杂度为O(n),空间复杂度也为O(n),借助了一个辅助数组。可惜并不能正确运行,而且我也找不到原因,哭死…
2.双指针法
看完卡哥介绍双指针法(快慢指针法)的视频和文字之后,实现的代码如下:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for(int fast = 0 ; fast < nums.size() ; fast++)
{
if(nums[fast] != val)
{
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
这里的快指针的作用是来选择我们真正需要的元素,即不等于val的元素。
而慢指针的作用是把选择好的元素依次放入新数组中(是同一个数组,但位置不同)
直观表现为:在相同时间内,fast指针从0扫到最后一个与元素,而slow指针从0扫到第k个元素
fast指针更像是去探索、判断,而slow指针更像是去接收探索、判断的结果
三、leetcode977.有序数组的平方
题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/
1.暴力
卡哥说这题要用双指针法,但我实在没想到怎么去用…太菜),只想了暴力解法的思路,代码如下:
vector<int> sortedSquares(vector<int>& nums) {
for(int i = 0; i<nums.size() ; i++)
{
nums[i] *= nums[i];
}
sort(nums.begin(),nums.end());
return nums;
}
时间复杂度为O(nlogn)
2.双指针法
与其说是双指针,不如说是三指针法。在原数组上有两个指针,分别指向数组的起始和结尾。由于实在没办法在原数组上动刀,只能牺牲一下空间,另外设置一个数组来接收最后的结果。
代码如下:
vector<int> sortedSquares(vector<int>& nums) {
int i = 0,j = nums.size() - 1,k=nums.size() - 1;
vector<int> result(nums.size(),0);
while(i <= j)
{
if(nums[j]*nums[j] >= nums[i]*nums[i])
{
result[k] = nums[j]*nums[j];
k--;
j--;
}else
{
result[k] = nums[i]*nums[i];
k--;
i++;
}
}
return result;
}
就和卡哥说的一样,数组的最大值只可能出现在两端,而且越往中间数字越小。于是分别依次对比首尾元素,可得到有序序列。
因为是先求最大值,然后依次往小了求,所以辅助数组的指针是从最后一个元素开始的,这与我们的惯性思维不同。