一、反转字符串
思路分析
双指针,一首一尾,同时移动。循环终止条件为两者不相遇,其实这个问题也可以被归纳为二分问题。
AC代码
class Solution {
public void reverseString(char[] s) {
int left=0;
int right=s.length-1;
while(left<right){
swap(s,left,right);
left++;
right--;
}
}
private void swap(char[] s,int a,int b){
char tmp=s[a];
s[a]=s[b];
s[b]=tmp;
}
}
二、反转字符串II
思路分析
本题实际上就是第一道题的拓展,还是转成字符数组,只不过是分组反转。那么问题的关键就在于我们怎么实现分组?
可以使用for循环控制,关键是循环头部的三条语句。判断条件是不到字符串的末尾,最为关键的就是每次循环之后循环变量的改变——i+=2*k;
循环体内部left每次初始值均为left,right是字符串末尾和left+k-1的较小值。
AC代码
class Solution {
public String reverseStr(String s, int k) {
char[] ch=s.toCharArray();
for(int i=0;i<ch.length;i+=2*k){//注意这句
int left=i;//注意这里的赋值
int right=Math.min(ch.length-1,left+k-1);//注意这句
while(left<right){
swap(ch,left,right);
left++;
right--;
}
}
return new String(ch);
}
private void swap(char[] s,int a,int b){
char tmp=s[a];
s[a]=s[b];
s[b]=tmp;
}
}
三、替换空格
思路分析
思路一:使用标准库函数中的replace函数s.replace(" “,”%20);
思路二:将字符串转换成为StringBuilder,进行遍历,之后使用sb.toString方法
思路三【重点理解】:使用双指针。根据空格的数量对转化出来的字符数组进行扩容。注意此时的指针一个指向原来的尾巴,一个指向扩容后的尾巴,对字符数组进行原地修改。
图示分析
这里思路三可能比较难理解,我们主要就这个解法进行解释。
AC代码
class Solution {
public String replaceSpace(String s) {
return s.replace(" ","%20");
}
}
class Solution {
public String replaceSpace(String s) {
StringBuilder sb=new StringBuilder();
char[] ch=s.toCharArray();
for(int i=0;i<ch.length;i++){
if(ch[i]==' '){
sb.append("%20");
}else{
sb.append(ch[i]);
}
}
return sb.toString();
}
}
class Solution {
public String replaceSpace(String s) {
char[] ch=s.toCharArray();
int left=ch.length-1;
//统计空格的个数,用于确定数组扩容
int cnt=0;
for(int i=0;i<ch.length;i++){
if(ch[i]==' '){
cnt++;
}
}
ch=Arrays.copyOf(ch,ch.length+2*cnt);
int right=ch.length-1;
while(left>=0){
if(ch[left]==' '){
ch[right--]='0';
ch[right--]='2';
ch[right--]='%';
}else{
ch[right--]=ch[left];
}
left--;
}
return new String(ch);
}
}
四、翻转字符串里的单词
思路分析
思路一:使用标准库函数split得到字符串数组,然后对单词倒序相加。需要注意的是,想通过trim来替代去除多余空格的功能是不可行的,因为它只能去除首尾的,这里中间可能有多个但是最终结果只能有一个。
思路二:不使用额外辅助空间,原地修改。移除多余空格—>将整个字符串反转---->对每个单词进行反转。
另外,这里的删除多余空格其实整体逻辑和我们之前原地删除重复元素思路是类似的;同时这里的reverseString,是对指定StringBuilder对象的指定区域反转,以后可能会经常用到。
AC代码
class Solution {
public String reverseWords(String s) {
StringBuilder sb=removeSpace(s);
reverseString(sb,0,sb.length()-1);
reverseEachWord(sb);
return sb.toString();
}
private StringBuilder removeSpace(String s){
int start=0;
int end=s.length()-1;
while(s.charAt(start)==' '){
start++;
}
while(s.charAt(end)==' '){
end--;
}
StringBuilder sb=new StringBuilder();
while(start<=end){
char c=s.charAt(start);
//重点理解这里删除元素的逻辑【类比之前数组的删除元素】
if(c != ' ' || sb.charAt(sb.length() - 1) != ' ') sb.append(c);
start++;
}
return sb;
}
private void reverseString(StringBuilder sb,int start,int end){
while(start<=end){
char tmp=sb.charAt(start);
sb.setCharAt(start,sb.charAt(end));
sb.setCharAt(end,tmp);
start++;
end--;
}
}
private void reverseEachWord(StringBuilder sb){
int start=0;
int end=1;
int n=sb.length();
while(start<n){
while(end<n&&sb.charAt(end)!=' '){
end++;
}
reverseString(sb,start,end-1);
start=end+1;
end=start+1;
}
}
}
五、左旋字符串
思路分析
思路一:使用标准库中的substring函数
思路二:先整体反转,再反转两个区间:0~len-1-n个 and len-n~len-1个字符。此时空间复杂度为O(1)
AC代码
class Solution {
public String reverseLeftWords(String s, int n) {
return s.substring(n)+s.substring(0,n);
}
}
class Solution {
public String reverseLeftWords(String s, int n) {
char[] chars = s.toCharArray();
reverse(chars, 0, chars.length - 1);
reverse(chars, 0, chars.length - 1 - n);
reverse(chars, chars.length - n, chars.length - 1);
return new String(chars);
}
public void reverse(char[] chars, int left, int right) {
while (left < right) {
chars[left] ^= chars[right];
chars[right] ^= chars[left];
chars[left] ^= chars[right];
left++;
right--;
}
}
}