文章目录
344.反转字符串
原题链接
解题思路:
双指针:自己的
- 双指针,左指针指向开头,右指针指向末尾。
- 交换两个左右指针。
- 左右指针向中间移动。
时间复杂度:O(n);
空间复杂度:O(1);
实现代码:
class Solution {
public:
void reverseString(vector<char>& s) {
int n = s.size();
int l = 0, r = n - 1;
while (l < r) {
swap(s[l ++], s[r --]);
}
return ;
}
};
541. 反转字符串II
原题链接
解题思路:
分类讨论:自己的
分类讨论:
- 如果剩余字符少于k个,则将剩余字符全部反转。
- 如果剩余字符大于或等于k个,则反转前k个字符,其余字符保持原样。可以每次处理2k个字符,判断并调用库函数reverse进行反转操作。
时间复杂度:O(n/2) = O(n);
空间复杂度:O(1);
实现代码:
class Solution {
public:
string reverseStr(string s, int k) {
int n = s.size();
for (int i=0; i < n; i += 2*k) {
//2k < n 的情况:
if (2*k <= n - i) {
reverse (s.begin() + i, s.begin() + i + k);
}
// k <= n < 2k
else if (n-i >= k) {
reverse(s.begin() + i, s.begin() + i + k);
}
// n < k;
else {
reverse (s.begin() + i, s.end());
}
}
return s;
}
};
优化:
class Solution {
public:
string reverseStr(string s, int k) {
int n = s.size();
for (int i=0; i < n; i += 2*k) {
if (k <= n - i) {
reverse (s.begin() + i, s.begin() + i + k);
}
else {
reverse (s.begin() + i, s.end());
}
}
return s;
}
};
剑指Offer 05.替换空格
原题链接
解题思路:
原数组上替换空格:自己的
- 扩充字符串的长度,由于一个空格 = “%20”,即相当于三个字符,等价于多了两个字符。
- 通过resize()函数,重新设置大小。即统计空格的数量,新字符串的大小 = 原字符串长度的大小 + 空格数量 * 2;
- 所以第一步是统计空格的数量。
- 第二步扩容。
- 第三步:从后往前遍历字符串,将一个指针指向新字符串的末尾。另一个指针指向旧字符串的末尾,然后若当前旧字符串的指针指向的是字母,则将其丢给新的字符串。如果是空格的话,则先处理新字符串的指针,从后往前填充三个字符:“%20”;然后再使得新旧字符串的指针同时向前移动!
实现代码:
class Solution {
public:
string replaceSpace(string s) {
int cnt=0;
int old = s.size();
for (int i=0; i < old; i ++) {
if (s[i] == ' ') {
cnt ++;
}
}
s.resize(old + cnt * 2);
int n = s.size();
for (int l = old-1, r=n-1; l < r; l --, r --) {
if (s[l] != ' ') {
s[r] = s[l];
}
else {
s[r--] = '0';
s[r--] = '2';
s[r] = '%';
}
}
return s;
}
};
151.翻转字符串里的单词
原题链接
解题思路:
双指针:别人的
本题要求返回单词顺序颠倒且单词之间用单个空格连接的结果字符串。仅反转字符串中单词的顺序,单词本身并没有反转。也就是说,最后一个单词反转到第一个位置,单词本身没有变。可以先反转整个字符串,然后再反转每个单词,在反转过程中,去掉多余空格。
如何反转单词 :
用start变量扫描字符串,遇到第一个非空格字符时,end=start;读取字符s[end],放入s[i],然后end++,i++,i用来控制反转后的字符串下标,当s[end]为空格时,就找到了一个单词。使用reverse函数反转该单词更新start为end,反转下一个单词。
实现代码:
class Solution {
public:
string reverseWords(string s) {
int n = s.size();
reverse(s.begin(), s.end());
int i=0;
for (int start = 0; start < n; start ++) {
//先找到第一个不为空格的字符,等于跳过行首空格!
if (s[start] != ' ') {
//单词之间加个空格:
if (i != 0) { //i!=0,是为了排除行首添加空格!
s[i ++] = ' ';
}
int end = start;
//不停寻找当前单词的末尾,直到找到空格为止,则当前单词一定是在[start, end);
while (end < n && s[end] != ' ') {
s[i ++] = s[end ++];
}
//翻转单词,由于i指向的是末尾,所以通过计算当前单词的长度,然后求得左右端点
//然后反转区间内的单词。
reverse (s.begin() + i - (end - start), s.begin() + i);
// 处理下一个单词
start = end;
}
}
s.erase (s.begin() + i, s.end());
return s;
}
};
剑指Offer58-II.左旋转字符串
原题链接
解题思路:
局部反转 ->整体反转:
题意:将字符串的前n个字符移动到末尾。
而我们的reverse函数翻转的话,会改变单词之间的顺序。
但很重要的一点是:一个字符串,进行两次reverse翻转的话,等价于没有变动!
所以我们可以先将前 n 个字符串进行翻转,然后再将 第 n 个 之后的字符串进行翻转。(两次局部的翻转)
然后再整体翻转:则得到理想的效果!
若先整体翻转的话,的确,我们想要的字符串部分,是移动到了末尾,但是字符串内部的字符顺序是颠倒的,也可以这时候来索引n,划分区间,然后进行两次局部翻转。不过这时候的话:区间为:[… ,倒数第n个],[倒数第n个,之后];
不过倒数的n,不如正数的n;
总之:两次局部翻转 + 一次整体翻转!
实现代码:
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(), s.begin() + n);
reverse(s.begin() + n, s.end());
reverse(s.begin(), s.end());
return s;
}
};
总结:
双指针还可以用来翻转字符串、索引区间、原数组上更新、