344.反转字符串
是否能用库函数?
如果题目的关键部分直接用库函数就可以解决,那建议不要使用库函数,如果库函数仅仅是解题过程中的一部分,且十分清楚内部原理的话,可以考虑使用库函数
代码
class Solution {
public:
void reverseString(vector<char>& s) {
int first = 0;
int last = s.size()-1;
for(;first<last;first++, last--){
swap(s[first], s[last]);
}
}
};
541. 反转字符串II
代码
// 可以对字符串直接处理,不用转成数组
class Solution {
public:
string reverseStr(string s, int k) {
for(int i=0; i<s.size(); i += 2*k){
// 剩余字符少于k,则剩余字符全部反转
if(i+k > s.size()-1){
reverse(s.begin()+i, s.end());
}
// 否则翻转前k 注意第二项填的是下一个指针,即左闭右开
else{
reverse(s.begin()+i, s.begin()+i+k);
}
}
return s;
}
};
本题中,reverse不是关键,所以可以直接用库,库函数输入的是地址
如果要自己写reverse,如果写成左闭右闭,那输入时要记得-1
剑指Offer 05.替换空格
思路:
很多数组填充问题,都是预先给数组扩容填充后的大小,然后从后往前进行操作,两个好处
- 不用申请新数组
- 从后往前填充元素,避免从前往后填充时,每次添加元素都要在添加元素后的所有元素向后移动
具体步骤:
1-求空格数
2-扩充字符串,利用resize方法,扩充大小为空格数*2
3-快指针j和慢指针i,j从原字符串末尾开始,i从新字符串末尾开始,同时向左遍历,如果j遇到空格,则i填充0 2 %;如果j不是空格,则i填充与j相同的值
代码
class Solution {
public:
string replaceSpace(string s) {
// 求空格个数
int count = 0;
// for(int i=0; i<s.size(); i++){
// if(s[i] == ' '){
// count++;
// }
// }
for(char m:s){
if(m == ' '){
count++;
}
}
// 根据空格个数扩充为新的字符串
int oldLen = s.size(); // 慢指针要用
s.resize(s.size() + count*2);
int newLen = s.size();
// 定义快慢指针
int slow = oldLen-1;
int fast = newLen-1;
while(slow < fast){
if(s[slow] == ' '){
s[fast] = '0';
s[fast-1] = '2';
s[fast-2] = '%';
fast -= 3;
}
else{
s[fast] = s[slow];
fast--;
}
slow--;
}
return s;
}
};
151.翻转字符串里的单词
思路
分三步走:
1-移除多余空格,只剩下单词中间的一个空格
2-进行翻转,可以利用reverse(),时间复杂度也是O(n) (因为这一步不是关键步骤,所以可以用库函数,如果要写可以参照上面第一题)
3-每个单词内进行翻转
本题重点是移除多余空格,如果使用库函数erase(),则时间复杂度为O(n^2) (因为是在遍历中erase,纯erase是O(n)),所以应用双指针法,为O(n)
双指针移除多余空格
快指针要遍历全部元素
如果快指针指向元素不为空,就要进入一个循环,让fast后面的一个单词赋给slow(即fast指向空或者到头时跳出循环)
但在单词赋值之前要判断一下,这个单词是否为第一个单词,如果不是第一个单词,就要给slow先赋空格,再从fast把单词搬过来
代码
class Solution {
public:
// 注意自定义的两个函数传递的是地址
// 翻转函数,要输入头和尾是方便后面单词的翻转
void reverse(string& s, int begin, int end){
for(int i=begin, j=end; i<j; i++, j--){
swap(s[i], s[j]);
}
}
//去除多余空格函数 双指针法
void removeExaSpace(string& s){
int slow = 0;
for(int fast=0; fast < s.size(); fast++){
if(s[fast] != ' '){
if(slow != 0) s[slow++] = ' '; // 判断当赋值过来的单词不是第一个时,slow要先给个空格
// 让一个单词整个从fast赋值到slow while中之所以还要加这两个条件是因为上面for循环锁定具体fast,现在让这个具体的fast往后走,要设置循环跳出条件
while(fast<s.size() && s[fast]!=' '){
s[slow++] = s[fast++];
}
}
}
s.resize(slow); //slow大小即去除空格后的字符串大小(正好比索引值大1)
}
string reverseWords(string s) {
removeExaSpace(s);
reverse(s, 0, s.size()-1);
// 双指针法(自己写的)
// for(int slow=0, fast=0; fast<s.size(); fast++){
// if(s[fast] == ' '){
// reverse(s, slow, fast-1);
// slow = fast+1;
// }
// if(fast == s.size()-1){
// reverse(s, slow, fast);
// }
// }
// 其实也是双指针法,start相当于上面的slow,i相当于fast,只是巧的是i可以等于s.size()
int start = 0;
for(int i=0; i<=s.size(); i++){
if(s[i] == ' ' || i==s.size()){
reverse(s, start, i-1);
start = i+1;
}
}
return s;
}
};
包含三部分:翻转指定范围元素、去除多余空格(重点)、根据空格间隔翻转每个单词
在最后一部分中,巧妙的写法是i可以等于s.size(),这样把fast指向结尾的情况考虑进去,不用再分情况讨论
注意:自定义的两个函数传的是地址,不用return,而题目中给的函数是传入string类型返回string类型的,不是地址
剑指Offer58-II.左旋转字
思路:整体翻转+局部翻转的思路(实际为两个局部翻转,再整体翻转)
代码
class Solution {
public:
void reverse(string& s, int begin, int end){
for(int i=begin, j=end; i<j; i++, j--){
swap(s[i], s[j]);
}
}
string reverseLeftWords(string s, int n) {
reverse(s, 0, n-1);
reverse(s, n, s.size()-1);
reverse(s, 0, s.size()-1);
return s;
}
};
总结
今日四道题都是反转问题。344 反转字符串是最简单,利用双指针;541 反转字符串Ⅱ加上条件,利用规律一段一段反转;151反转字符串中单词,先整体反转,再把每个单词反转回来(但这道题中最难的是去除多余空格-双指针法);最后一题左旋转字也是利用整体反转+局部反转
剩下一道替换空格,为先扩充数组,再利用双指针法,从后往前填充(如果从前往后会每次都让后面元素全部后移,时间复杂度O(n^2))