第三天-双指针
283-移动零
思路1:类似冒泡排序的思路
每次遇到零则将其通过逐个交换的方式,将其交换到末尾,同时末尾的指针往左移动,直到末尾的指针与相交
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int n = nums.size();
int rear = n;
for(int i=0;i<n;)
{
if(nums[i]==0)
{
for(int j=i;j<n-1;j++)
swap(nums[j], nums[j+1]);
n--;
if(n<=0)
break;
}
else
i++;
}
}
};
思路2:类似快排的思想
将所有不等于0的数放在右边,所有0放在左边,则partition为0——即中间数为0进行左右的排列。
- 利用两个指针同时从左往右
- 一个指针仅指向非零元素,另一个指针在每次交换后就往右移动一格
- 当该指针指向非零元素时,两个指针所指元素进行交换,使得非零元素往右边移动,另一个指针往后移动——表示前一位的元素已经确定
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int j=0;
for(int i=0;i<nums.size();i++)
{
if(nums[i]!=0)
{
//swap(nums[i], nums[j++]);
int temp = nums[i];
nums[i] = nums[j];
nums[j++] = temp;
//j能够按顺序移动是因为如果该位为非零数则在前面的操作中已经进行了移动
}
}
}
};
注意:自己写的三行交换的函数比直接调用swap会快一点
思路3:双指针
同样是将非零的数往左移动,最后在末尾补零即可
能够移动的原因也是因为前面的非零数都进行了移动,所以后面的非零数并不会覆盖前面的数据导致数据的丢失
public:
void moveZeroes(vector<int>& nums) {
int j=0;
int n = nums.size();
for(int i=0;i<n;i++)
{
if(nums[i]!=0)
{
nums[j++] = nums[i];
}
}
for(int i=j;i<n;i++)
{
nums[i] = 0;
}
}
};
167-两数之和II-输入有序数组
思路1:暴力解法
定一动一,寻找和为target的两个数,返回其下标
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int n = numbers.size();
int i = 0;
while(i<n-1)
{
int j = i+1;
while(j<n)
{
if(numbers[i]+numbers[j]==target)
{
return {i+1, j+1};
}
j++;
}
i++;
}
return {};
}
};
当然暴力不能出奇迹——会超时!
思路2:二分查找
注意到数组有序
我们可以从前往后遍历nums[i] 则另一个数为target-nums[i],可以在其i后面的数据中进行二分查找,时间复杂度为O(logn)
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int n = numbers.size();
for(int i=0;i<n;i++)
{
int num = target-numbers[i];
int left = i+1;
int right = n-1;
while(left<=right)
{
int mid = (left+right)/2;
if(numbers[mid]==num)
return {i+1, mid+1};
else if(numbers[mid]>num)
right = mid-1;
else
left = mid+1;
}
}
return {};
}
};
思路3:双指针
由于数组有序,我们可以通过收尾两个指针的移动来进行和的求解
- 当两数之和为target时,返回其下标
- 当两数之和大于target时,需要一个数变小,考虑到是非降序排列,则右边的数变小,rear-1
- 当两数之和小于target时,需要一个数变大,则左边的数变大,front+1
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int front=0;
int rear = numbers.size()-1;
while(front<=rear)
{
if(numbers[front]+numbers[rear]==target)
{
return {front+1, rear+1};
}
else if(numbers[front]+numbers[rear]>target)
{
rear--;
}
else
{
front++;
}
}
return {};
}
};
时间复杂度为O(n)
第四天-双指针
344-翻转字符串
思路1:reverse()函数
class Solution {
public:
void reverseString(vector<char>& s) {
reverse(s.begin(), s.end());
}
};
reverse大法好!
思路2:双指针首尾交换位置,直到两个指针相遇
class Solution {
public:
void reverseString(vector<char>& s) {
int front = 0;
int rear = s.size()-1;
while(front<=rear)
{
char temp = s[front];
s[front] = s[rear];
s[rear] = temp;
front++;
rear--;
}
}
};
577-翻转字符串中的单词III
由于题目规定了所有单词仅用一个空格隔开,所以每个单词结束的标志就是空格——除了最后一个是字符串到末尾为结束的标志。
所以思路就是:找到一个单词进行翻转,跳过空格,再找下一个单词,同样利用双指针来确定单词的位置,利用reverse进行翻转
class Solution {
public:
string reverseWords(string s) {
string sub;
int front = 0;
int rear = 0;
for(;rear<=s.size();)
{
if(s[rear]==' '||rear==s.size())
{
reverse(s.begin()+front, s.begin()+rear);
rear++;//跳过空格
front = rear;
}
else
{
rear++;
}
}
return s;
}
};