leetcode 344. Reverse String(字符串逆序)

本文介绍如何使用C++通过原地修改字符数组来实现字符串的逆序,利用双指针法在不额外分配内存的情况下完成操作。通过示例和代码展示如何在给定的字符数组s中进行高效逆序。

Write a function that reverses a string. The input string is given as an array of characters s.

You must do this by modifying the input array in-place with O(1) extra memory.

Example 1:

Input: s = [“h”,“e”,“l”,“l”,“o”]
Output: [“o”,“l”,“l”,“e”,“h”]
Example 2:

Input: s = [“H”,“a”,“n”,“n”,“a”,“h”]
Output: [“h”,“a”,“n”,“n”,“a”,“H”]

把字符串s逆序排列。

思路:
位置0和n-1交换,1和n-2交换。。
知道左右两指针相遇。

    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;
        
        while(left < right) {
            char tmp = s[left];
            s[left] = s[right];
            s[right] = tmp;
            
            left ++;
            right --;
        }
        
    }
<think>我们正在讨论如何在Rust中反转字符串。根据引用[2],我们可以使用`reverse`方法直接反转一个字符向量(`Vec<char>`)。但是,字符串在Rust中通常是`String`类型或`&str`类型,它们是以UTF-8编码的字节序列。因此,反转字符串需要考虑UTF-8编码的问题,因为直接按字节反转可能会破坏字符的编码(比如多字节字符)。 有两种主要方法: 1. 如果字符串只包含ASCII字符,那么可以直接按字节反转,因为每个字符都是一个字节。 2. 如果字符串包含非ASCII字符(比如多字节字符),那么应该按字符(char)反转,确保每个字符(Unicode标量值)作为一个整体反转,而不是反转其内部字节。 根据引用[2]中的示例,题目要求反转一个`Vec<char>`,这已经将字符串分解为字符数组,所以直接反转字符数组即可。但实际中我们可能面对的是`String`或`&str`。 步骤: 方法1:使用`chars`方法将字符串转换为字符迭代器,反转字符序列,再重新收集为字符串。 方法2:如果确定字符串是ASCII,可以直接反转字节数组(注意:必须确保是ASCII,否则会得到无效的UTF-8字符串)。 下面我们分别展示这两种方法。 注意:方法1是安全的,适用于任何字符串;方法2仅适用于ASCII字符串。 示例代码: 方法1(安全,通用): ```rust fn reverse_string(s: &str) -> String { s.chars().rev().collect() } ``` 方法2(仅ASCII): ```rust fn reverse_ascii_string(s: &str) -> String { // 首先检查是否全部是ASCII字符 if !s.is_ascii() { panic!("String contains non-ASCII characters"); } // 将字符串转换为字节数组,反转,然后重新构建字符串 let mut bytes = s.as_bytes().to_vec(); bytes.reverse(); // 因为我们已经检查过是ASCII,所以可以安全地转换为字符串 String::from_utf8(bytes).expect("Invalid UTF-8") } ``` 但是,引用[2]中的题目要求是原地修改一个`Vec<char>`,所以我们可以直接使用`reverse`方法: ```rust impl Solution { pub fn reverse_string(s: &mut Vec<char>) { s.reverse(); } } ``` 如果用户有一个`String`类型,并希望原地反转,那么需要先将其转换为`Vec<char>`,然后再转换回`String`。但这样会有额外的分配。如果字符串是ASCII的,我们可以原地操作字节数组以避免分配。 下面是一个原地反转ASCII字符串的函数: ```rust fn reverse_ascii_in_place(s: &mut String) { // 检查是否为ASCII if !s.is_ascii() { panic!("String contains non-ASCII characters"); } // 获取底层的字节数组(可变引用) let bytes = unsafe { s.as_bytes_mut() }; bytes.reverse(); } ``` 注意:上面的函数使用了`unsafe`,因为我们直接操作了字节数组。但由于我们确保了字符串是ASCII,所以这些字节构成有效的UTF-8(因为ASCII是UTF-8的子集)。因此,这个操作是安全的。 但是,对于非ASCII字符串,我们不能简单地按字节反转。例如,字符串"नमस्ते"(印地语): 按字节反转会得到无效的UTF-8,因为多字节字符的字节顺序会被打乱。 因此,对于非ASCII字符串,我们只能使用按字符反转的方法,但要注意: - 字符(char)是Unicode标量值,但有些字符由多个Unicode标量值组成(比如带重音符号的字符),反转后可能会分离(比如"e\u{301}"反转后变成"\u{301}e",这可能会改变显示顺序)。 - 更复杂的文本(如阿拉伯语、希伯来语)有从右向左的书写顺序,反转可能会破坏其含义。 所以,在实际应用中,反转字符串通常只用于ASCII字符串,或者明确知道字符串不包含复杂字符的场景。 总结: 1. 对于任意字符串,使用`chars().rev().collect()`来反转,但注意这可能会破坏组合字符(虽然不常见)。 2. 对于已知是ASCII的字符串,可以按字节反转以提高效率。 下面我们给出一个完整的示例: ```rust fn main() { let s1 = "hello"; // ASCII let reversed1 = s1.chars().rev().collect::<String>(); println!("{} reversed: {}", s1, reversed1); // "olleh" // 对于非ASCII let s2 = "नमस्ते"; let reversed2 = s2.chars().rev().collect::<String>(); println!("{} reversed: {}", s2, reversed2); // "े त्समन" 但显示可能不正确,因为组合标记的问题 // 原地反转ASCII字符串 let mut s3 = String::from("hello"); reverse_ascii_in_place(&mut s3); println!("{}", s3); // "olleh" } fn reverse_ascii_in_place(s: &mut String) { if !s.is_ascii() { panic!("String contains non-ASCII characters"); } unsafe { let bytes = s.as_bytes_mut(); bytes.reverse(); } } ``` 注意:上面的`reverse_ascii_in_place`使用了`unsafe`,但在确保是ASCII的情况下是安全的。 另外,引用[3]和[4]提到了字节数组和字符串的转换,我们可以利用这些知识来处理字节级别的操作。 最后,根据引用[2]中的多种语言实现,Rust的`Vec<char>`反转是最直接的,因为已经分解为字符数组。 如果用户的问题是如何反转一个`String`,那么我们可以这样写: ```rust let s = String::from("hello"); let reversed: String = s.chars().rev().collect(); ``` 或者,如果用户有一个`&str`: ```rust let s = "hello"; let reversed: String = s.chars().rev().collect(); ``` 如果用户想要原地修改`String`(仅ASCII): 如上所述,使用`reverse_ascii_in_place`函数。 如果用户想要原地修改非ASCII字符串(分解为字符数组再反转): 我们可以将字符串转换为`Vec<char>`,反转后再转回`String`,但这会分配新的内存,并不是严格意义上的原地(因为`Vec<char>`和`String`内部表示不同)。 严格原地反转非ASCII字符串(按字符)在Rust中比较困难,因为`String`内部是`Vec<u8>`,而字符(char)是4字节。所以,我们无法在不改变内存布局的情况下原地操作。因此,通常的做法是分配一个新的字符串。 因此,我们推荐使用`chars().rev().collect()`,它简单且通用。 根据引用[4],我们还可以编写测试用例来验证我们的反转函数。 下面是一个测试例子: ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_reverse_ascii() { let s = "hello"; assert_eq!(reverse_string(s), "olleh"); } #[test] fn test_reverse_non_ascii() { let s = "नमस्ते"; // 反转后的字符串应该是字符的逆序,但注意:这里每个字符都是Unicode标量值 // 所以反转后应该是:['े', 'त', '्', 'स', '्', 'म', 'न'] -> 但注意,这里有些字符是组合标记,所以显示可能不正常 let reversed = s.chars().rev().collect::<String>(); // 实际结果:"े त्समन"(这里用空格分隔只是为了显示,实际没有空格) // 我们可以用十六进制或直接写出来 // 原字符串:['न','म','स','्','त','े'] -> 反转后:['े','त','्','स','म','न'] // 注意:在内存中,组合标记(如्)和前面的字符组合,但反转后它们分开了,所以显示会异常。 // 因此,对于非ASCII字符串的反转,通常没有实际意义,除非是字符序列的简单逆序。 assert_eq!(reversed, "े त्समन".replace(" ", "")); // 实际中去掉空格 } } ``` 注意:上面的测试用例中,非ASCII字符串反转后显示可能不是预期的,因为组合字符被拆开了。所以,除非有特殊需求,否则不要轻易反转非ASCII字符串。 结论: 在Rust中反转字符串: 1. 对于ASCII字符串,可以按字节反转(高效,原地)。 2. 对于非ASCII字符串,按字符反转(`chars().rev().collect()`),但注意组合字符可能被破坏。 最后,我们根据用户的问题,提供不同的方法。 如果用户的问题来自LeetCode 344(引用[2]),那么题目输入是`Vec<char>`,所以直接使用`s.reverse()`即可。 如果用户有`String`,那么根据情况选择方法。 相关问题: 1. Rust中如何判断一个字符串是否为ASCII? 2. Rust中`String`和`Vec<char>`如何相互转换? 3. 反转非ASCII字符串时,如何保留组合字符(如重音符号)?</think>在 Rust 中反转字符串需要特别注意 UTF-8 编码特性,以下是完整方法和最佳实践: ### 方法 1:使用 `chars().rev().collect()`(推荐) 最安全通用的方法,正确处理多字节字符: ```rust fn reverse_string(s: &str) -> String { s.chars().rev().collect::<String>() } // 使用示例 let original = "Rust编程"; let reversed = reverse_string(original); println!("{} -> {}", original, reversed); // 输出:Rust编程 -> 程编tsuR ``` **原理**: 1. `chars()` 将 UTF-8 字符串分解为 Unicode 字符序列 2. `rev()` 反转字符顺序 3. `collect()` 重新组合为 `String` **时间复杂度**:$O(n)$,空间复杂度 $O(n)$ ### 方法 2:原地修改(仅限 ASCII) 当确定字符串只含 ASCII 字符时,可原地操作: ```rust fn reverse_ascii_in_place(s: &mut String) { let bytes = unsafe { s.as_bytes_mut() }; bytes.reverse(); } // 使用示例 let mut ascii_str = String::from("hello"); reverse_ascii_in_place(&mut ascii_str); println!("{}", ascii_str); // 输出 "olleh" ``` **注意事项**: - 必须先用 `s.is_ascii()` 验证 - 非 ASCII 字符串使用此方法会产生无效 UTF-8 - 使用 `unsafe` 需确保安全边界 ### 方法 3:处理 `Vec<char>`(LeetCode 344 解法) 当输入是字符数组时(如 LeetCode 344 题): ```rust // 解法 1:使用标准库 pub fn reverse_string(s: &mut Vec<char>) { s.reverse(); // 直接调用 Vec 的 reverse 方法 } // 解法 2:手动双指针交换 pub fn reverse_string_manual(s: &mut Vec<char>) { let (mut left, mut right) = (0, s.len() - 1); while left < right { s.swap(left, right); // 更安全的交换方式 left += 1; right -= 1; } } ``` [^2] ### 多字节字符处理示例 ```rust let emoji = "😀🚀🌟"; println!("{}", reverse_string(emoji)); // 输出 "🌟🚀😀" let hindi = "नमस्ते"; println!("{}", reverse_string(hindi)); // 输出 "ेत्समन" ``` ### 性能对比表 | 方法 | 适用场景 | 是否原地 | 是否安全 | UTF-8 支持 | |------|----------|----------|----------|------------| | `chars().rev().collect()` | 通用 | ❌ | ✅ | ✅ | | 字节数组反转 | 纯 ASCII | ✅ | ⚠️(需验证) | ❌ | | `Vec<char>.reverse()` | 字符数组 | ✅ | ✅ | ✅ | ### 常见错误 1. **直接反转字节数组(破坏 UTF-8)** ```rust // 错误示例:会导致无效 UTF-8 let mut s = "编程".to_string(); unsafe { s.as_bytes_mut().reverse() }; // 产生乱码 ``` 2. **错误处理组合字符** ```rust let s = "e\u{301}"; // é 的分解形式 println!("{}", reverse_string(s)); // 输出 "́e" 而非 "é" ``` ### 最佳实践建议 1. 优先使用 `chars().rev().collect()` 保证安全 2. 操作前用 `s.is_ascii()` 检测字符集 3. 需要最高性能时,对 ASCII 文本使用字节操作 4. 组合字符(如带重音符号的字母)需特殊处理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值