字符串
做字符串一般都要注意字符串为不可变对象,
1 替换空格
剑指 Offer 05. 替换空格https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/一开始是用了Java的replaceAll、replace ,^_^ 没想到居然也都给通过了~
public String replaceSpace(String s) {
// s = s.replaceAll("\u0020", "%20");
// s = s.replaceAll(" ", "%20");
s = s.replace(" ", "%20");
return s;
}
不过面试的时候这么写,当场估计就被面试官“撕”了......
一开始思路是根据空格split 然后一段一段加,效率高一些,但是遇到全空格的情况,split返回的是空的~
// 遇到 全空格无法通过
public String replaceSpace_0(String s) {
String[] s1 = s.split(" ");
// System.out.println(s1.length);
s = "";
for (int i = 0; i < s1.length; i++) {
s+= (i != s1.length - 1) ? s1[i]+"%20" : s1[i];
}
return s;
}
1. 循环 + StringBuilder
Java最好不要在for循环内使用+拼接字符串,这样每次都会new一个新的字符串,对性能有一定影jdk1.5编译后的字节码,jdk1.5+的优化是在每次循环体内 new 一个StringBuilder对象,再调用append方法,还是会产生垃圾对象, 所以还是推荐在循环体外手动new 一个 StringBuilder对象。
在 Python 和 Java 等语言中,字符串都被设计成「不可变」的类型,即无法直接修改字符串的某一位字符,需要新建一个字符串实现。
在 C++ 语言中, string 被设计成「可变」的类型(参考资料),因此可以在不新建字符串的情况下实现原地修改。
public String replaceSpace_1(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
sb.append((s.charAt(i) == ' ') ? "%20" : String.valueOf(s.charAt(i)));
}
return sb.toString();
}
public String replaceSpace_1_1(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == ' ') sb.append("%20");
else sb.append(c);
// sb.append((s.charAt(i) == ' ') ? "%20" : String.valueOf(s.charAt(i)));
}
return sb.toString();
}
2. 字符数组)_( 官方
开始也想到了感觉数组的插入效率有点低。。
由于每次替换从 1 个字符变成 3 个字符,使用字符数组可方便地进行替换。建立字符数组地长度为 s 的长度的 3 倍,这样可保证字符数组可以容纳所有替换后的字符。
...三倍 可还行
- 获得 s 的长度 length
- 创建字符数组 array,其长度为 length * 3
- 初始化 size 为 0,size 表示替换后的字符串的长度
- 从左到右遍历字符串 s
- 获得 s 的当前字符 c
- 如果字符 c 是空格,则令 array[size] = '%',array[size + 1] = '2',array[size + 2] = '0',并将 size 的值加 3
- 如果字符 c 不是空格,则令 array[size] = c,并将 size 的值加 1
- 遍历结束之后,size 的值等于替换后的字符串的长度,从 array 的前 size 个字符创建新字符串,并返回新字符串
public String replaceSpace_2(String s) {
char[] ss = new char[s.length() * 3];
int size = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == ' ') {
ss[size++] = '%';
ss[size++] = '2';
ss[size++] = '0';
}else{
ss[size++] = c;
}
}
return new String(ss, 0, size);
}
size使用++既同步了i的增加又记录了长度!一举两得!
- 时间复杂度:O(n)。遍历字符串 s 一遍。
- 空间复杂度:O(n)。额外创建字符数组,长度为 s 的长度的 3 倍。
2. 左旋转字符串
剑指 Offer 58 - II. 左旋转字符串https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/
1. 字符串切片
应用字符串切片函数,可方便实现左旋转字符串。
class Solution {
public String reverseLeftWords(String s, int n) {
return s.substring(n, s.length()) + s.substring(0, n);
}
}
复杂度分析:
- 时间复杂度 O(N): 其中 N 为字符串 s 的长度,字符串切片函数为线性时间复杂度;
- 空间复杂度 O(N): 两个字符串切片的总长度为 N。
2. 列表遍历拼接
若面试规定不允许使用 切片函数 ,则使用此方法。
public String reverseLeftWords_1(String s, int n) {
StringBuilder res = new StringBuilder();
for (int i = n; i < s.length(); i++) {
res.append(s.charAt(i));
}
for (int i = 0; i < n; i++) {
res.append(s.charAt(i));
}
return res.toString();
}
复杂度分析:
- 时间复杂度 O(N): 线性遍历 s 并添加,使用线性时间;
- 空间复杂度 O(N): 新建的辅助 res 使用 O(N)大小的额外空间。
2_1. 求与运算 简化
public String reverseLeftWords_1_1(String s, int n) {
StringBuilder res = new StringBuilder();
for (int i = n; i < n + s.length(); i++) {
res.append(s.charAt(i % s.length()));
}
return res.toString();
}
3. 字符串遍历拼接
若规定 Python 不能使用
join()
函数,或规定 Java 只能用 String ,则使用此方法。
此方法与 方法二 思路一致,区别是使用字符串代替列表。
复杂度分析:
- 时间复杂度 O(N) : 线性遍历 s 并添加,使用线性时间;
- 空间复杂度 O(N) : 假设循环过程中内存会被及时回收,内存中至少同时存在长度为 N 和 N−1 的两个字符串(新建长度为 N 的 res 需要使用前一个长度 N-1 的 res ),因此至少使用 O(N) 的额外空间。
示例图来自LeetCode精选回答~
我一开始就想到了LeetCode里189题的反转数组。。。利用的是stringbuilder
先将前n个进行翻转,再将后n个进行翻转,最后再整体翻转一遍
public String reverseLeftWords(String s, int n) {
String s_reverse = reverseString(s);
return reverseString(s_reverse.substring(0, s.length() - n)) + reverseString(s_reverse.substring(s.length() - n, s.length()));
}
public String reverseString(String s) {
StringBuilder s_reverse = new StringBuilder();
for (int i = s.length() - 1; i >= 0; i--) {
s_reverse.append(s.charAt(i));
}
return s_reverse.toString();
}
我看也有人用字符数组,原理是一样的。
class Solution {
public String reverseLeftWords(String s, int n) {
// 将String类型转换成char[]类型
char[] arr = s.toCharArray();
int length = arr.length;
// 翻转前半部分
reverse(arr, 0, n-1);
// 翻转后半部分
reverse(arr, n, length-1);
// 整体再翻转一遍
reverse(arr, 0, length-1);
// 将翻转结束的字符数组转化成字符串
return new String(arr);
}
// 翻转指定范围的字符串
public void reverse(char[] arr, int begin, int end) {
while (begin < end) {
char temp = arr[begin];
arr[begin] = arr[end];
arr[end] = temp;
begin++;
end--;
}
}
}