阿翰 剑指offer 之 Day03 字符串

本文探讨了多种字符串处理方法,包括使用StringBuilder替换空格、字符数组进行替换、字符串切片与列表遍历拼接左旋转字符串,以及优化求与运算。强调了字符串不可变性对性能的影响,并提供了不同场景下的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字符串

1 替换空格

1. 循环 + StringBuilder  

2. 字符数组)_( 官方

 2. 左旋转字符串

 1. 字符串切片

2. 列表遍历拼接

2_1. 求与运算 简化

3.  字符串遍历拼接


字符串

做字符串一般都要注意字符串为不可变对象,

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--;
        }
    }
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值