目录
一、引入
先看一道例题 例题
对于这道题,我们最先想到的方式是另开一个数组,将该数组的各个数的平方一一存入该数组,之后重新排序。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> num(nums.size()); //开辟新数组
for(int i = 0; i < nums.size(); ++i)//将原数组的平方处理后一一存入新数组
{
num[i] = nums[i] * nums[i];
}
sort(num.begin(), num.end(), less());//对新数组排序
return num;//返回新数组
}
};
对这个解法我们做点改动
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
// vector<int> num(nums.size());
// for(int i = 0; i < nums.size(); ++i)
// {
// num[i] = nums[i] * nums[i];
// }
// sort(num.begin(), num.end(), less());
// return num;
vector<int > num(nums.size());//新的空间
int i = 0, j = nums.size() - 1;
for(int k = nums.size() - 1; i <= j;)//遍历数组
{
if(nums[i] * nums[i] < nums[j] * nums[j])
{
num[k] = nums[j] * nums[j];
j--;
}
else
{
num[k] = nums[i] * nums[i];
i++;
}
k--;
}
return num;
}
};
在这个解法中,因为包含负数,所以平方数最大值可能出现在头与尾;因此在遍历过程中,逐步比较头一个与最后一个。
二、算法思想
双指针其实是对原有算法的一种优化方式,常见的有快慢指针,对撞指针。
三、例题
第一题
第一题
先一般性的解法
class Solution {
public:
void moveZeroes(vector<int>& nums) {
vector<int > num;
for(int i = 0, j = 0; i < nums.size(); ++i)//将nums中非0数排入num
{
if(nums[i] != 0)
{
num.push_back(nums[i]);
j++;
}
}
for(int i = 0, j = 0; i < nums.size(); ++i)//将nums中0排入num
{
if(nums[i] == 0)
{
num.push_back(nums[i]);
j++;
}
}
nums.assign(num.begin(), num.end());//将num复制进nums中
}
};
双指针解法
我们设想如果两个指针,一个指针在左边,一个在右边查找非0数,如果右边的数字不是0,就将右边的数字换到左边。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
// vector<int > num;
// for(int i = 0, j = 0; i < nums.size(); ++i)
// {
// if(nums[i] != 0)
// {
// num.push_back(nums[i]);
// j++;
// }
// }
// for(int i = 0, j = 0; i < nums.size(); ++i)
// {
// if(nums[i] == 0)
// {
// num.push_back(nums[i]);
// j++;
// }
// }
// nums.assign(num.begin(), num.end());
int i = 0; //指向非0数
int j = 0; //
while(i < nums.size())//遍历整个数组
{
if (nums[i])//右边遇到非0数
{
swap(nums[i], nums[j]);
j++;
}
i++;
}
}
};
做个动画演示一个整个过程
第二题
第二题
这题就不用一般性解法演示了,比较复杂,难度不大
定义两个指针,一个指向字符串的首位,一个指向字符串的末尾,逐步将首位与末尾交换
class Solution {
public:
void reverseString(vector<char>& s) {
int i = 0, j = s.size() - 1;
for(; i <= j; i++, j--)
{
swap(s[i], s[j]); //交换头与尾
}
}
};
第三题
第三题
对于这题我们很容易能想的方法就是直接找到中间的那个数,但是链表又无法直接定位,因此我们可以把该链表转换为数组,再定位到中间的数。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* middleNode(ListNode* head) {
vector <ListNode* > L = {head};
while (L.back()->next != NULL)
{
L.push_back(L.back()->next);
}
return L[L.size() / 2];
}
};
这个是把链表的数据转换到数组中,再直接输出中间节点。我们做点改进,在原有的链表中,找到中间的节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* middleNode(ListNode* head) {
/*
* 第一种
*/
// vector <ListNode* > L = {head};
// while (L.back()->next != NULL)
// {
// L.push_back(L.back()->next);
// }
// return L[L.size() / 2]
/*
* 第二种
*/
int n = 0;//初始化
ListNode* cur = head;
while(cur != nullptr)//确定节点数量
{
++n;
cur = cur -> next;
}
int i = 0;
cur = head;//初始化cur
while (i < n / 2)//找到中间节点
{
i++;
cur = cur->next;
}
return cur;
}
};
再用双指针的方式优化一下,这里用到的是快慢指针。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* middleNode(ListNode* head) {
/*
* 第一种
*/
// vector <ListNode* > L = {head};
// while (L.back()->next != NULL)
// {
// L.push_back(L.back()->next);
// }
// return L[L.size() / 2]
/*
* 第二种
*/
// int n = 0;
// ListNode* cur = head;
// while(cur != nullptr)//确定节点数量
// {
// ++n;
// cur = cur->next;
// }
// int i = 0;
// cur = head;//初始化cur
// while (i < n / 2)//找到中间节点
// {
// i++;
// cur = cur->next;
// }
// return cur;
/*
* 第三种
*/
ListNode* slow = head;
ListNode* fast = head;
while (fast != nullptr && fast->next != nullptr)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};
快慢指针是利用两个指针的速度不一致,比如这题中,快指针的速度是慢指针的两倍,快指针到末尾的时候,恰好慢指针到达链表的中间。
第四题
第四题
class Solution {
public:
void rotate(vector<int>& nums, int k) {
//int i = 0; int j = nums.zise() - k;
vector<int > num(nums.size());//开辟一个数组空间
int temp;
for(int i = 0; i < nums.size(); i++)
{
if(i < nums.size() - k)
{
num[(i + k) % nums.size()] = nums[i];//对nums.size()取模,防止溢出
}
else
{
temp = nums.size() - i - k;
temp = fabs(temp);
temp = temp % nums.size();//同样取模,防止溢出
num[temp] = nums[i];
}
}
nums.assign(num.begin(), num.end());
}
};
后面看官方解答,似乎并不需要分类,于是乎
class Solution {
public:
void rotate(vector<int>& nums, int k) {
//int i = 0; int j = nums.zise() - k;
vector<int > num(nums.size());//开辟一个数组空间
int temp;
for(int i = 0; i < nums.size(); i++)
{
// if(i < nums.size() - k)
// {
// num[(i + k) % nums.size()] = nums[i];//对nums.size()取模,防止溢出
// }
// else
// {
// temp = nums.size() - i - k;
// temp = fabs(temp);
// temp = temp % nums.size();//同样取模
// num[temp] = nums[i];
// }
num[(i + k) % nums.size()] = nums[i];
}
nums.assign(num.begin(), num.end());
}
};
这个也是可以AC的。
class Solution {
public:
void rotate(vector<int>& nums, int k) {
//int i = 0; int j = nums.zise() - k;
// vector<int > num(nums.size());//开辟一个数组空间
// int temp;
// for(int i = 0; i < nums.size(); i++)
// {
// // if(i < nums.size() - k)
// // {
// // num[(i + k) % nums.size()] = nums[i];//对nums.size()取模,防止溢出
// // }
// // else
// // {
// // temp = nums.size() - i - k;
// // temp = fabs(temp);
// // temp = temp % nums.size();//同样取模
// // num[temp] = nums[i];
// // }
// num[(i + k) % nums.size()] = nums[i];
// }
// nums.assign(num.begin(), num.end());
int i = 0, j = nums.size() - 1;
while(i <= j)//将所有的数调转顺序。
{
swap(nums[i], nums[j]);
i++;
j--;
}
i = 0;
j = k % nums.size() - 1;//取模,防止数据溢出
while(i <= j)//调转前数列的顺序
{
swap(nums[i], nums[j]);
i++;
j--;
}
i = k % nums.size();//取模,防止数据溢出
j = nums.size() - 1;
while(i <= j)//调转后序列的顺序
{
swap(nums[i], nums[j]);
i++;
j--;
}
}
};
用双指针的方法,我们可以这样,将整个数列翻转,之后以k为界,分别将前后数列翻转。
官方解答
class Solution {
public:
void reverse(vector<int>& nums, int start, int end) {
while (start < end) {
swap(nums[start], nums[end]);
start += 1;
end -= 1;
}
}
void rotate(vector<int>& nums, int k) {
k %= nums.size();
reverse(nums, 0, nums.size() - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.size() - 1);
}
};
第五题
第五题
在写这题的时候,我们想的就是,针对每一个单词分别调换。
class Solution {
public:
string reverseWords(string s) {
//string str;//新的空间,用于储存已经反转好的单词
int i;
int begin = 0, end = 0;//交换时的开始位置与结束位置
int sLength = s.length();//字符串长度
int count = 0;//定义遍历字母的数量
while(count < sLength) //count的值不可以随意的变动,它表示的是遍历的字母位置
{
begin = count;//将i初始化至当前位置
while(s[count] != ' ' && count < sLength)//确定j的位置,每个单词的结尾
{
count++;
}
end = count - 1;
while(begin <= end)//颠倒单词的字母顺序
{
swap(s[begin], s[end]);
begin++;
end--;
}
while(s[count] == ' ' && count < sLength)//定位到下一个单词
{
count++;
}
}
return s;
}
};