代码随想录算法训练营day08 | 344.反转字符串,541. 反转字符串II,剑指Offer 05.替换空格,151.翻转字符串里的单词,剑指Offer58-II.左旋转字符串
344.反转字符串
教程视频:https://www.bilibili.com/video/BV1fV4y17748
解法一:双指针
public void reverseString(char[] s) {
//左右指针
int i=0,j=s.length-1;
char temp;
while(i<j){
temp = s[i];
s[i] = s[j];
s[j] = temp;
//s[l] ^= s[r]; //(^是 按位异或 操作)构造 a ^ b 的结果,并放在 a 中
//s[r] ^= s[l]; //将 a ^ b 这一结果再 ^ b ,存入b中,此时 b = a, a = a ^ b
//s[l] ^= s[r]; //a ^ b 的结果再 ^ a ,存入 a 中,此时 b = a, a = b 完成交换
i++;
j--;
}
}
541. 反转字符串II
教程视频:https://www.bilibili.com/video/BV1dT411j7NN/?vd_source=ddffd51aa532d23e6feac69924e20891
解法一:转成字符数组进行交换
public String reverseStr(String s, int k) {
char[] ch = s.toCharArray();
for(int i = 0; i < ch.length; i += 2 * k){
int start = i;
//这里是判断尾数够不够k个来取决end指针的位置
int end = Math.min(ch.length - 1, start + k - 1);
//用异或运算反转
while(start < end){
ch[start] ^= ch[end];
ch[end] ^= ch[start];
ch[start] ^= ch[end];
start++;
end--;
}
}
return new String(ch);
}
解法二:使用StringBuffer的 reverse() 方法
- 当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
- 和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
- 在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
- StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
使用StringBuffer要注意把不变的字符填充进去。
public String reverseStr(String s, int k) {
StringBuffer result = new StringBuffer();
int length = s.length();
int i = 0;
while(i<length){
// 与length进行比较,如果大于length了,那就将其置为length
int firstK = (i+k<=length)?i+k:length;//firstK 是i~i+k字符内的下标
int secondK = (i+2*k<=length)?i+2*k:length;//secondK 是i+k~i+2k字符内的下标
//反转firstK中字符
StringBuffer temp = new StringBuffer(s.substring(i,firstK));
result.append(temp.reverse());
// 如果firstK到secondK之间有元素,这些元素直接放入res里
if(firstK<secondK){
temp = new StringBuffer(s.substring(firstK,secondK));
result.append(temp);
}
i+=2*k;
}
return result.toString();
}
剑指Offer 05.替换空格
解法一:复制时替换空格
public String replaceSpace(String s) {
StringBuffer res = new StringBuffer();
for(int i = 0;i<s.length();i++){
if(s.charAt(i)==' '){
res.append("%20");
}else{
res.append(s.charAt(i));
}
}
return res.toString();
}
解法二:双指针(适合c++)
首先扩充数组到每个空格替换成"%20"之后的大小。然后从后向前替换空格。
优点:
- 不用申请新数组。降低了空间复杂度。
- 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。降低了时间复杂度。
但java中的String类用指针处理过程繁琐,不适合用此解法
151.翻转字符串里的单词
解法一:String类的 lastIndexOf() 方法
思路:利用String类的 lastIndexOf() 方法,将最后一个无空格字符串添加到新字符串中。
public String reverseWords(String s) {
//去除前导空格和尾随空格
s = s.trim();
StringBuffer result = new StringBuffer();
int i = s.lastIndexOf(' ');
while(i!=-1){
result.append(s.substring(i+1,s.length())+" ");
s = s.substring(0,i);
//排除连续空格的干扰
s = s.trim();
i = s.lastIndexOf(' ');
}
//添加最开头的无空格字符串到新字符串末尾
result.append(s);
return result.toString();
}
解法二:字符串整体翻转后,逐个单词翻转
如果字符串是可变数据类型,则能使得空间复杂度为O(1)。
这里给出本解法锻炼思维。
public String reverseWords(String s) {
//去除空格
char[] ch = removeSpace(s);
// System.out.println(ch);
//字符数组整体翻转
ch = reverseString(ch,0,ch.length-1);
// System.out.println(ch);
//翻转单个单词
ch = reverseEachWord(ch);
// System.out.println(ch);
return new String(ch);
}
//视频中去空格方法
public char[] removeSpace(String s){
char[] ch = s.toCharArray();
int slow=0;
for(int fast=0;fast<s.length();fast++){
if(ch[fast]!=' '){
if(slow!=0){
ch[slow]=' ';
slow++;
}
while(fast < s.length() && ch[fast]!=' '){
ch[slow] = ch[fast];
slow++;
fast++;//这里自增之后fast指向一个空字符" "
//外层for循环再次自增会跳过对于该字符串的判断
}
}
}
char[] res = new char[slow];
for(int i=0;i<slow;i++){
res[i] = ch[i];
}
return res;
}
//自己写的去空格方法(更容易理解)
// public String reverseWords(String s) {
// char[] ch = s.toCharArray();
// int slow=0,fast=0;
// while(fast<s.length()){
// if(ch[fast]!=' '){
// ch[slow] = ch[fast];
// fast++;
// slow++;
// }else{ // ch[fast]==' '
// while(ch[fast]==' '){
// fast++;
// }
// ch[slow] = ' ';
// slow++;
// }
// }
// char[] res = new char[slow];
// for(int i=0;i<slow;i++){
// res[i] = ch[i];
// }
// return new String(res);
// }
public char[] reverseString(char[] ch,int start,int end){
int l=start,r=end;
while(l<r){
char temp = ch[r];
ch[r] = ch[l];
ch[l] = temp;
l++;
r--;
}
return ch;
}
public char[] reverseEachWord(char[] ch){
int start = 0;
for(int i=0; i<ch.length; i++){
if(ch[i]==' '){
reverseString(ch,start,i-1);
start = i+1;
}
}
reverseString(ch,start,ch.length-1);
return ch;
}
剑指Offer58-II.左旋转字符串
解法一:String类的 substring() 方法
public String reverseLeftWords(String s, int n) {
return s.substring(n,s.length())+s.substring(0,n);
}
解法二:三次翻转
若String可变,则不需要申请额外空间,只在本串上操作。
不适合使用java做,主要领会其方法。
public String reverseLeftWords(String s, int n) {
//不申请额外空间,只在本串上操作。
//先整体翻转
String s1 = reverse(s);
//翻转后n个字符
String s2 = reverse(s1.substring(s1.length()-n));
//翻转前面的字符
String s3 = reverse(s1.substring(0,s1.length()-n));
return s3+s2;
}
public String reverse(String s){
char[] str = s.toCharArray();
int i=0,j=str.length-1;
while(i<j){
str[i]^=str[j];
str[j]^=str[i];
str[i]^=str[j];
i++;
j--;
}
return new String(str);
}
总结
1、java字符串不可变,若向使用指针,需要先使用 toCharArray()方法 转换成字符数组chart[]。
2、寻找特定字符的索引可使用 StringBuilder(高效线程不安全)或者StringBuffer(线程安全)。
3、截取子字符串可使用String类的 substring()方法(注意方法名全小写),此操作时间复杂度为O(n)。