Leetcode 344. 反转字符串
思路
在前面的反转链表中,我们使用了双指针的解法,这里反转字符串,仍然是用双指针的方法。但字符串的反转,相较于链表更简单。
因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。
对于字符串,我们定义两个指针(也可以说是索引下标),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。
代码(Java)
写法1:双指针法
时间复杂度: O(n)
空间复杂度: O(1)
class Solution {
public void reverseString(char[] s) {
int left = 0;
int right = s.length-1;
while (left<right){
char temp=s[left];
s[left]=s[right];
s[right]=temp;
left++;
right--;
}
}
}
代码随想录链接
下面是代码随想录的文章链接与视频链接,帮助大家更好地理解这道题。
文章
视频
Leetcode 541. 反转字符串II
思路
这道题仍然是一道反转字符串题,和344不同的是,需要处理每隔开2k个字符的前k个字符,可能有同学会想做一个计数器来统计2k,再统计前k个字符。其实在遍历字符串的过程中,只需要让i+=2*k即可,i每次向右移动2k就可以。这里我们仍然采用双指针法,left指针作为反转的左端,right指针作为反转的右端。因为剩余指针有两种情况:一种为少于k个,另一种为大于k个,所以需要分开处理。
代码(Java)
写法1:双指针法
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public String reverseStr(String s, int k) {
char[] sarr=s.toCharArray();
for (int i=0;i<sarr.length;i+=2*k){
int left=i;
int right=Math.min(i+k-1,sarr.length-1);//这里是判断尾数够不够k个来取决right指针的位置
while (left<right){
char temp=sarr[left];
sarr[left]=sarr[right];
sarr[right]=temp;
left++;
right--;
}
}
return new String(sarr);
}
}
代码随想录链接
下面是代码随想录的文章链接与视频链接,帮助大家更好地理解这道题。
文章
视频
Leetcode 剑指 Offer 05. 替换空格
思路
这道题要想做到极致,首先扩充数组到每个空格替换成"%20"后的大小。
然后从后向前替换空格,也就是双指针法,i指向新长度的末尾,j指向旧长度的末尾。
难点
- 为什么要从后向前填充
从前向后填充就是O(n^2)的算法了,因为每次添加元素后需要将该元素之后的所有元素向后移动。
其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后再从后向前进行操作。
这么做有两个好处:
- 不用申请新数组。
- 避免了每次添加元素后需要将该元素之后的所有元素向后移动。
代码(Java)
写法1:双指针法
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public String replaceSpace(String s) {
StringBuilder str = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i)==' ')
str.append(" ");
}
if (str.length()==0)//字符串s没有空格
return s;
int left = s.length()-1;//左指针:指向原始字符串最后一个位置
s+=str.toString();
int right = s.length()-1;//右指针:指向扩展字符串的最后一个位置
char[] chars=s.toCharArray();
while (left>=0){
if (chars[left]==' '){
chars[right--]='0';
chars[right--]='2';
chars[right]='%';
}else {
chars[right]=chars[left];
}
left--;
right--;
}
return new String(chars);
}
}
代码随想录链接
下面是代码随想录的文章链接,帮助大家更好地理解这道题。
文章
Leetcode 151. 反转字符串中的单词
思路
这道题目相较于之前的反转字符串来说,我们不仅需要反转字符串里的单词,还需要将多余的空格都删掉,单词与单词间只保留一个空格。
当我们对字符串整体进行反转后,这时每个单词的位置对了,但还需要再对每个单词进行反转,从而得到目标字符串。
难点
- 删除多余的空格
之前在数组章节中,我们利用双指针法学会了如何移除元素,在本道题中,我们可以将空格看作元素。
所以定义快慢指针,快指针获取字母,慢指针得到字母后的更新位置。
代码(Java)
写法1:双指针法
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public String reverseWords(String s){
//1、移除多余的空格
char[] sarr=s.toCharArray();
sarr=removeExtraSpaces(sarr);
//2、整个字符串反转
reverse(sarr,0,sarr.length-1);
//3、每个单词的反转
reverseEachWord(sarr);
return new String(sarr);
}
public char[] removeExtraSpaces(char[] sarr){
int fast,slow=0;
for (fast=0;fast< sarr.length;fast++){
if (sarr[fast]!=' '){
if (slow!=0)//除了第一个单词,其他单词末尾加空格
sarr[slow++]=' ';
while (fast<sarr.length && sarr[fast]!=' ')
sarr[slow++]=sarr[fast++];
}
}
char[] newArr= new char[slow];
System.arraycopy(sarr,0,newArr,0,slow);
return newArr;
}
public void reverse(char[] sarr, int left, int right) {
while(left<right){
sarr[left] ^= sarr[right];
sarr[right] ^= sarr[left];
sarr[left] ^= sarr[right];
left++;
right--;
}
}
public void reverseEachWord(char[] sarr){
int start=0;
for (int end=0;end<=sarr.length;end++){
if (end==sarr.length || sarr[end]==' '){
reverse(sarr,start,end-1);
start=end+1;
}
}
}
}
代码随想录链接
下面是代码随想录的文章链接和视频链接,帮助大家更好地理解这道题。
文章
视频
Leetcode 剑指 Offer 58 - II. 左旋转字符串
思路
这道题要想做到极致,我们可以不申请额外的空间,只在本串上操作。根据上道题 151. 反转字符串中的单词 的启发,我们仍然可以使用整体反转+局部反转的方式解决本题。
- 反转区间为前n的子串
- 反转区间为n到末尾的子串
- 反转整个字符串
下面有图片帮助大家理解:
示例1中 输入:字符串abcdefg,n=2
代码(Java)
写法1:整体反转+局部反转
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public String reverseLeftWords(String s, int n) {
char[] sarr=s.toCharArray();
reverse(sarr,0,n-1);
reverse(sarr,n,sarr.length-1);
reverse(sarr,0,sarr.length-1);
return new String(sarr);
}
public void reverse(char[] sarr,int left,int right){
while(left<right){
sarr[left]^=sarr[right];
sarr[right]^=sarr[left];
sarr[left]^=sarr[right];
left++;
right--;
}
}
}
代码随想录链接
下面是代码随想录的文章链接,帮助大家更好地理解这道题。
文章
总结
今天主要学习了双指针的解法,前两道题的反转都是基本的字符反转算法。151.翻转字符串里的单词是最具难度的一道题,视频看了好几遍才理解。但是搞懂后写左旋转字符串就很容易,最重要的思路在于局部反转+整体反转。