【字符串基础】
1、字符串的创建方式
常用的构造器:
2、字符串的常用方法
【344.反转字符串】- 简单题
方法 双指针
class Solution {
public void reverseString(char[] s) {
int left = 0;
int right = s.length - 1;
while (left < right){
char c = s[left];
s[left] = s[right];
s[right] = c;
left++;
right--;
}
}
}
时间复杂度: O(n)
空间复杂度: O(1)
【541. 反转字符串II】- 简单题
思路:以2k的步长遍历字符串,再利用左右指针反转前k个字符串
关键:当剩余字符串足够k个和不足k个时,右指针right的取值不同
注意:由于Java中String类为不可变类型,因此需要将其转换成字符数组 char[] 或者StringBuffer对象(线程安全)。
方法一 利用双指针和【StringBuffer】实现
StringBuffers的相关方法:
1、创建对象:StringBuffer sb = new StringBuffer(String s);
2、获取字符串的长度:sb.length();
3、获取指定索引的字符:sb.charAt(int idx);
4、设置指定索引的字符:sb.setCharAt(int idx, char c);
5、转化为字符串:sb.toString();
class Solution {
public String reverseStr(String s, int k) {
StringBuffer sb = new StringBuffer(s);
int left;
int right;
for (int start = 0; start < sb.length(); start += 2 * k){
left = start;
// 剩余字符串不足k个时对右指针的特殊处理
right = start + k - 1 < sb.length() ? start + k - 1 : sb.length() - 1;
while (left < right){
// 交换左右指针的字符
char c = sb.charAt(left);
sb.setCharAt(left, sb.charAt(right));
sb.setCharAt(right, c);
// 更新左右指针
left++;
right--;
}
}
return sb.toString();
}
}
时间复杂度: O(n),遍历次数n/2k,在每次遍历最多反转k个字符,O(k * n/2k)=O(n)
空间复杂度: O(n),利用StringBuffer对象存储长度为n的字符串
方法二 利用双指针和【字符数组】实现
字符数组的相关方法:
1、字符串 -> 字符数组:char[] sc = s.toCharArray();
2、获取字符数组的长度:sc.length; // 注意:数组获取长度都是没括号
3、获取指定索引的字符:sc[idx];
4、设置指定索引的字符:sc[idx] = c;
5、字符数组 -> 字符串:sb.toString();
class Solution {
public String reverseStr(String s, int k) {
char[] sc = s.toCharArray();
int left;
int right;
for (int start = 0; start < sc.length; start += 2 * k){
left = start;
// 剩余字符串不足k个时对右指针的特殊处理
right = start + k - 1 < sc.length ? start + k - 1 : sc.length - 1;
while (left < right){
// 交换左右指针的字符
char c = sc[left];
sc[left] = sc[right];
sc[right] = c;
// 更新左右指针
left++;
right--;
}
}
return new String(sc);
}
}
时间复杂度: O(n),遍历次数n/2k,在每次遍历最多反转k个字符,O(k * n/2k)=O(n)
空间复杂度: O(n),利用字符数组存储长度为n的字符串
【总结】
1、可变类型和不可变类型
由于Java中的String类为不可变类型,因此不能按指定索引修改字符,需要将字符串转换成
可变类型的变量(如:StringBuffer对象,字符数组等)。
2、获取不同类型的长度的方法汇总:
字符串:s.length()
数组:s.length
集合/映射:s.size()
【卡码网:54.替换数字】- 简单题
注意:
1、卡码网需要自己编写main函数,需要先定义Main类,再定义main函数
2、卡码网需要自己编写输入和输出,要熟悉不同类型的数据的输入方式
import java.util.*;
public class Main{
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < s.length(); i++){
// 当前遍历的字符是数字('0' = 48, 'a' = 97)
if (s.charAt(i) < 'a'){
sb.append("number");
}
// 当前遍历的是小写字母
else{
sb.append(s.charAt(i));
}
}
System.out.println(sb.toString());
}
}
时间复杂度: O(n)
空间复杂度: O(n)
【151.翻转字符串里的单词】- 中等题
方法一 利用String类的trim和split函数水题
思路:
1、利用trim()去除两端的空格,并利用split函数获取String数组
2、倒序遍历String数组,组装单词
class Solution {
public String reverseWords(String s) {
// System.out.println(words);
// 利用split函数获取单词数组(注意:一个空格是" ",多个空格是" +")
String[] words = s.trim().split(" +");
System.out.println(Arrays.toString(words));
// 遍历单词数组,组装所有单词,组合成要求的字符串
StringBuffer res = new StringBuffer();
for (int i = words.length - 1; i >= 0; i--){
res.append(words[i]);
if (i != 0){
res.append(" ");
}
}
return res.toString();
}
}
时间复杂度: O(n)
空间复杂度: O(n),创建了额外的List对象存储所有单词
方法二 不需要额外的List对象存储单词
思路:假设原来的字符串为 "the sky is blue "
1、移除首尾和中间多余的空格 : "the sky is blue" (利用创建的函数removeSpace实现)
2、反转整个字符串:"eulb si yks eht" (利用创建的函数reverseString实现)
3、反转各个单词:"blue is sky the" (利用创建的函数reverseWord实现)
注意:
removeSpace函数和reverseWord函数的条件判断十分巧妙,要仔细分析和理解。
class Solution {
// 1、去除首位及多余的空格
private StringBuffer removeSpace(String s){
int start = 0;
int end = s.length() - 1;
// 去除首尾多余的空格
while (s.charAt(start) == ' ') start++;
while (s.charAt(end) == ' ') end--;
// 2、去除中间多余空格的同时获取返回的字符串
StringBuffer sb = new StringBuffer();
for(int i = start; i <= end; i++){
// 只有两种情况符合拼接要求:a.当前字符不是空格 b.当前字符是空格,但上一个字符不是空格
if (s.charAt(i) != ' ' || s.charAt(i - 1) != ' '){
sb.append(s.charAt(i));
}
}
return sb;
}
// 2、反转指定范围的字符串
private StringBuffer reverseString(StringBuffer sb, int start, int end){
while(start < end){
char c = sb.charAt(start);
sb.setCharAt(start, sb.charAt(end));
sb.setCharAt(end, c);
start++;
end--;
}
return sb;
}
// 3、反转所有单词
private StringBuffer reverseWord(StringBuffer sb){
int start = 0;
int end = -1; // 注意end初始值应该从-1开始
for (int i = 0; i <= sb.length(); i++){ // 注意:此处能取=,是要判断是否已经遍历完字符串
if (i < sb.length() && sb.charAt(i) != ' '){
end++;
}
// 如果遇到空格或已经遍历完字符串(i = sb.length()),就要反转字符串
else{
reverseString(sb, start, end);
start = i + 1;
end = i;
}
}
return sb;
}
// 总的操作
public String reverseWords(String s) {
StringBuffer sb = removeSpace(s);
sb = reverseString(sb, 0, sb.length() - 1);
sb = reverseWord(sb);
return sb.toString();
}
}
时间复杂度: O(n),reverseWord函数中,找单词遍历整个字符串为O(n),所有单词反转了一次,也是O(n),所以加起来还是O(n)。
空间复杂度: O(n)
【卡码网:55.右旋转字符串】
方法一 利用StringBuffer+简单拼接
注意:将
sc.next();
替换为sc.nextLine();
会导致读取完数字后再读取字符串失败原因:
- 由于
nextLine()
方法会读取并返回输入中的下一行内容,包括换行符。输入了一个整数后按下了回车键,此时换行符被作为下一行的内容被nextLine()
读取了。- 在使用
next()
方法时,它会读取并返回下一个以空白符分隔的字符串,但不会读取换行符。
import java.util.*;
public class Main{
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
int k = sc.nextInt();
String s = sc.next();
StringBuffer sb = new StringBuffer();
// 先拼接后k个字符
int start = s.length() - k;
while (start < s.length()){
sb.append(s.charAt(start));
start++;
}
// 再拼接剩余的字符
int i = 0;
while (i < s.length() - k){
sb.append(s.charAt(i));
i++;
}
System.out.println(sb.toString());
}
}
时间复杂度: O(n)
空间复杂度: O(n)
方法二 利用字符数组,整体反转+分段反转
思路:
1、整体反转:如果输入的k=2,s="abcdefg",整体反转后为"gfedcba"
2、分段反转:反转前k个字符和反转剩余的字符,得到"fgabcde",就是想要的结果
3、写一个反转指定范围字符串的函数,再调用三次即可
注意:
main函数为静态方法,自定义的方法也需要是静态方法才能被main函数使用
import java.util.*;
public class Main{
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
int k = sc.nextInt();
String s = sc.next();
char[] res = s.toCharArray();
res = reverseString(res, 0, res.length - 1); // 整体反转
res = reverseString(res, 0, k - 1); // 反转前k个
res = reverseString(res, k, res.length - 1); // 反转剩余的
System.out.println(res);;
}
// 反转指定范围字符串
public static char[] reverseString(char[] cc, int start, int end){
while(start < end){
char c = cc[start];
cc[start] = cc[end];
cc[end] = c;
start++;
end--;
}
return cc;
}
}
时间复杂度: O(n)
空间复杂度: O(n)