字符串的反转以及巧用反转 ------关于反转,看这一篇就足够了
目录
一.本文介绍
二.反转字符串
1.题目描述
2.问题分析
3.代码实现
三.反转字符串 II
1.题目描述
2.问题分析
3.代码实现
三.反转字符串中的单词 I
1.题目描述
2.问题分析
3.代码实现
四.反转字符串中的单词 III
1.题目描述
2.问题分析
3.代码实现
五.仅仅反转字母
1.题目描述
2.问题分析
3.代码实现
六.旋转字符串
1.题目描述
2.问题分析
3.代码实现
七.轮转数组
1.题目描述
2.问题分析
3.代码实现
八.整数反转
1.题目描述
2.问题分析
3.代码实现
一.本文介绍
字符串的反转是一个不算难的问题,单纯的字符串整个反转是十分容易的,有难度的的是局部反转,但进入到局部反转之前,我们需要把字符串的整体反转搞清楚.这篇文章虽然主要介绍了字符串的反转,但是还包含了数组的反转,无论是字符数组和整数数组,都进行了讲解.接下来的一些将从力扣的一些题目由易到难的对字符串的反转问题进行系统化的讲解.
二.反转字符串
1.题目描述
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组
s
的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
力扣:力扣
2.问题分析
对于字符串整体的反转,主要就是把前边的字符和后边的字符进行交换,把索引位置为i的和索引位置为nums.length-i-1的字符进行交换,交换到字符串一半的位置,整个字符串的交换就可以完成.
例如:对如下的长度为奇数的i<nums.length/2 偶数的同样也是i<nums.length/2 ,因此终止条件确定
3.代码实现
public void reverseString(char[] s) {
for (int i = 0; i < s.length / 2; ++i) {
char temp = s[i];
s[i] = s[s.length - i - 1];
s[s.length - i - 1] = temp;
}
}
三.反转字符串 II
1.题目描述
给定一个字符串
s
和一个整数k
,从字符串开头算起,每计数至2k
个字符,就反转这2k
字符中的前k
个字符。
- 如果剩余字符少于
k
个,则将剩余字符全部反转。- 如果剩余字符小于
2k
但大于或等于k
个,则反转前k
个字符,其余字符保持原样。
力扣:力扣
2.问题分析
这一道题目就涉及了局部反转字符串,对于长度为2k的子字符串,只需要反转前k个字符,这个时候我们需要写一个反转的方法,方法的作用是将闭区间[start,end]的子字符串进行反转,其实和之前反转前部的类似,这个时候开始是从start位置开始,到(start+end)/2的位置结束,注意这个时候等于(start+end)/2的时候是继续循环的,只有当大于的时候,循环才结束,此时索引为i的位置对应索引为end-i+start的位置进行交换(循环终止的条件可以修改为i<(start+end+1)/2,这样和整体反转字符串相统一)
对于2k个子字符串,我们对前k个字符串进行反转,当剩余的字符串的长度不足k个的时候,我们对这剩余的字符串进行反转,这个时候便不需要反转k长度的字符串了.这个时候加一个判断条件,这一题便可以解决了
3.代码实现
public String reverseStr(String s, int k) {
char[] arr = s.toCharArray();
for (int i = 0; i < arr.length; i += 2 * k) {
if (i + k >= arr.length) {
reverse(arr, i, arr.length - 1);
} else {
reverse(arr, i, i + k - 1);
}
}
return new String(arr);
}
//将闭区间[start,end]的子字符串进行反转
public void reverse(char[] chars, int start, int end) {
for (int i = start; i <= (end + start) / 2; ++i) {
char temp = chars[i];
chars[i] = chars[end - i + start];
chars[end - i + start] = temp;
}
}
三.反转字符串中的单词 I
1.题目描述
给你一个字符串
s
,请你反转字符串中 单词 的顺序。单词 是由非空格字符组成的字符串。
s
中使用至少一个空格将字符串中的 单词 分隔开。返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串
s
中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
力扣:力扣
2.问题分析
这一题是一道十分综合的题目,对于这种问题我们一般采用双指针的方法进行解决(start,end),来找到一个单词的长度,然后进行反转即可,这道题目主要可以从以下三个步骤来解决:
1.去除多余的空格,单词前导和后导可能存在无意义的空格,例如" word in use ",需要把空格去掉,每个单词间也可能存在多个空格,需要把单词间的空格变为一个
2.将整个字符串进行反转
3.将反转后的字符串的每个单词进行反转.也就是找到空格的位置,然后反转start到end的位置
3.代码实现
public String reverseWords(String s) {
//去除多余的空格
StringBuilder sb = reverseSpace(s);
//将字符串反转
reverseString(sb, 0, sb.length() - 1);
//反转每个单词
reverseEachWord(sb);
return new String(sb);
}
public StringBuilder reverseSpace(String s) {
int start = 0, end = s.length() - 1;
while (s.charAt(start) == ' ') start++;
while (s.charAt(end) == ' ') end--;
StringBuilder sb = new StringBuilder();
while (start <= end) {
if (s.charAt(start) == ' ') {
sb.append(' ');
++start;
while (s.charAt(start) == ' ') {
++start;
}
}
sb.append(s.charAt(start++));
}
return sb;
}
public void reverseString(StringBuilder sb, int start, int end) {
while (start < end) {
char temp = sb.charAt(start);
sb.setCharAt(start, sb.charAt(end));
sb.setCharAt(end, temp);
++start;
--end;
}
}
public void reverseEachWord(StringBuilder sb) {
int start = 0, end = 0;
while (end < sb.length()) {
while (end < sb.length() && sb.charAt(end) != ' ') {
++end;
}
reverseString(sb, start, end - 1);
end += 1;
start = end;
}
}
四.反转字符串中的单词 III
1.题目描述
给定一个字符串
s
,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
力扣:力扣
2.问题分析
这一道题相对于上一道题就很简单了,还是使用双指针(start,end)每次只需要找到空格,然后将start到end-1的位置的子字符串进行反转,然后一直到字符串的末尾,具体的实现参考代码
3.代码实现
public String reverseWords(String s) {
char[] chars = s.toCharArray();
int start = 0, end = 0;
while (end < chars.length) {
while (end < chars.length && chars[end] != ' ') {
end++;
}
reverse(chars, start, end - 1);
end = end + 1;
start = end;
}
return new String(chars);
}
public void reverse(char[] s, int start, int end) {
while (start < end) {
char temp = s[start];
s[start] = s[end];
s[end] = temp;
start++;
end--;
}
}
五.仅仅反转字母
1.题目描述
给你一个字符串
s
,根据下述规则反转字符串:
- 所有非英文字母保留在原有位置。
- 所有英文字母(小写或大写)位置反转。
返回反转后的
s
。
力扣:力扣
2.问题分析
这一道题和普通的完全反转字符串其实就多了一个条件的判断,就是我们双指针(start,end)一定要落到字母字符的时候才能进行反转,并且当start>=end的时候立即终止反转,此时得到的结果就是最终的答案.
3.代码实现
public String reverseOnlyLetters(String s) {
if (s == "")
return s;
char[] arr = s.toCharArray();
int start = 0, end = s.length() - 1;
while (start < end) {
while (arr[start] < 'A' || (arr[start] > 'Z' && arr[start] < 'a')
|| arr[start] > 'z') {
start++;
if (start >= end) {
return new String(arr);
}
}
while (arr[end] < 'A' || (arr[end] > 'Z' && arr[end] < 'a')
|| arr[end] > 'z') {
end--;
if (start >= end) {
return new String(arr);
}
}
char temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
return new String(arr);
}
六.旋转字符串
1.题目描述
给定两个字符串,
s
和goal
。如果在若干次旋转操作之后,s
能变成goal
,那么返回true
。
s
的 旋转操作 就是将s
最左边的字符移动到最右边。
- 例如, 若
s = 'abcde'
,在旋转一次之后结果就是'bcdea'
。
力扣:力扣
2.问题分析
这一道题一行代码搞定,s进行若干次旋转是否能得到goal,其实就是把s拼接到s的后面,其中一定包含旋转之后的子字符串,这个时候判断拼接之后的字符串是否包含goal即可.
3.代码实现
public boolean rotateString(String s, String goal) {
return s.length()==goal.length()&&(s+s).contains(goal);
}
七.轮转数组
1.题目描述
给定一个整数数组
nums
,将数组中的元素向右轮转k
个位置,其中k
是非负数。
力扣:力扣
2.问题分析
这是一道十分意思的题目,数组元素向右轮转k个位置,当k>=nums.length的时候,其实就轮转了k%nums.length次,所以我们先这样处理k=k%nums.length.
然后就是这一道题目有意思的地方:
1.我们先反转整个数组
2.我们再反转[0,k-1]的元素
3.最后我们反转[k,nums.length-1]的元素
这个时候我们得到的就是答案了,具体为什么我们借用下面的图形来说明.
nums = "----->-->"; k =3
result = "-->----->";reverse "----->-->" we can get "<--<-----"
reverse "<--" we can get "--><-----"
reverse "<-----" we can get "-->----->"
3.代码实现
public void rotate(int[] nums, int k) {
k=k%nums.length;
reverse(nums,0,nums.length-1);
reverse(nums,0,k-1);
reverse(nums,k,nums.length-1);
}
public void reverse(int[] nums,int start,int end){
while(start<end){
int temp=nums[start];
nums[start]=nums[end];
nums[end]=temp;
start++;
end--;
}
}
八.整数反转
1.题目描述
给你一个 32 位的有符号整数
x
,返回将x
中的数字部分反转后的结果。如果反转后整数超过 32 位的有符号整数的范围
[−231, 231 − 1]
,就返回 0。假设环境不允许存储 64 位整数(有符号或无符号)。
力扣:力扣
2.问题分析
这一道题其实是很简单的,每一次需要将num乘以10加上x的最后一位,再将x的最后一位去掉,最主要的就是如何判断反转后的是否超过了32 位的有符号整数的范围,这个时候题目是要求我们返回0的,其实我们只需要在每次num*10之前判断是否num < Integer.MIN_VALUE / 10 || num > Integer.MAX_VALUE / 10 ,这样在之后的这样之后×10之后一定就不会超过最大的范围了
3.代码实现
public int reverse(int x) {
int num = 0;
while (x != 0) {
//判断num是否超过了32位有符号整数的范围
if (num < Integer.MIN_VALUE / 10 || num > Integer.MAX_VALUE / 10) {
return 0;
}
num *= 10;
num += x % 10;
x /= 10;
}
return num;
}