2-2 字符串反转(ReversingString)
字符串反转(Reversing a String)
字符串反转是将一个字符串中的字符顺序颠倒的操作。例如将 "hello" 反转后得到 "olleh"。
字符串反转有多种实现方式,常见方法包括:
- 双指针法:使用两个指针分别从字符串的首尾向中间移动,交换字符。
- 新建字符串法:从原字符串末尾开始,逐个字符拼接到新字符串中。
- 递归法:利用递归将问题分解为反转子串。
- 栈方法:利用栈的后进先出(LIFO)特性,将字符依次压栈再弹栈。
双指针法反转字符串
双指针法是最常用的原地(in-place)反转方法,不需要额外空间。
基本思路:
- 设置左指针
left = 0,右指针right = len - 1。 - 交换
left和right位置的字符。 - 左指针右移,右指针左移,直到两指针相遇或交叉。
以 "hello" 为例:
初始: h e l l o
^ ^
left right
第1步: o e l l h 交换 h 和 o
^ ^
left right
第2步: o l l e h 交换 e 和 l
^
left=right 相遇,结束
C++ 实现
#include <iostream>
#include <string>
void reverseString(std::string &s)
{
int left = 0;
int right = s.length() - 1;
while (left < right)
{
// swap characters
char temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
int main()
{
std::string s = "hello";
std::cout << "Original: " << s << "\n";
reverseString(s);
std::cout << "Reversed: " << s << "\n";
return 0;
}
运行该程序将输出
Original: hello
Reversed: olleh
我们通过 reverseString(s) 对字符串 s 进行原地反转。std::string 以引用方式传递,函数直接修改原字符串。
C 实现
在 C 语言中,字符串以字符数组(char[])表示,以空字符 '\0' 结尾。
#include <stdio.h>
#include <string.h>
void reverseString(char s[])
{
int left = 0;
int right = strlen(s) - 1;
while (left < right)
{
// swap characters
char temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
int main()
{
char s[] = "hello";
printf("Original: %s\n", s);
reverseString(s);
printf("Reversed: %s\n", s);
return 0;
}
运行该程序将输出
Original: hello
Reversed: olleh
注意:C 语言中 char s[] = "hello" 会将字符串字面量复制到栈上的数组中,因此可以修改。但如果使用 char *s = "hello",字符串存储在只读段,尝试修改会导致未定义行为。
Python 实现
def reverse_string(s):
# convert to list since strings are immutable
chars = list(s)
left = 0
right = len(chars) - 1
while left < right:
chars[left], chars[right] = chars[right], chars[left]
left += 1
right -= 1
return ''.join(chars)
s = "hello"
print(f"Original: {s}")
print(f"Reversed: {reverse_string(s)}")
运行该程序将输出
Original: hello
Reversed: olleh
Python 中字符串是不可变的(immutable),因此我们先将字符串转为列表,原地交换后再用 ''.join() 拼接回字符串。
Go 实现
Go 中字符串是不可变的字节切片,且使用 UTF-8 编码。为了正确处理 Unicode 字符,我们先将字符串转换为 []rune 切片,再进行原地交换。
package main
import "fmt"
func reverseString(s *string) {
runes := []rune(*s)
left := 0
right := len(runes) - 1
for left < right {
runes[left], runes[right] = runes[right], runes[left]
left++
right--
}
*s = string(runes)
}
func main() {
s := "hello"
fmt.Println("Original:", s)
reverseString(&s)
fmt.Println("Reversed:", s)
}
运行该程序将输出
Original: hello
Reversed: olleh
Go 通过 []rune 将字符串转换为 Unicode 码点切片,支持多字节字符(如中文)。使用指针 *string 模拟原地修改。runes[left], runes[right] = runes[right], runes[left] 是 Go 的多元赋值,无需临时变量即可完成交换。
新建字符串法反转
新建字符串法从原字符串末尾开始逐个读取字符,拼接到一个新字符串中。这种方法简单直观,但需要 O(n) 的额外空间。
C++ 实现
#include <iostream>
#include <string>
std::string reverseString(const std::string &s)
{
std::string result = "";
for (int i = s.length() - 1; i >= 0; i--)
{
result += s[i];
}
return result;
}
int main()
{
std::string s = "world";
std::cout << "Original: " << s << "\n";
std::cout << "Reversed: " << reverseString(s) << "\n";
return 0;
}
运行该程序将输出
Original: world
Reversed: dlrow
C 实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void reverseString(const char src[], char dest[])
{
int len = strlen(src);
for (int i = 0; i < len; i++)
{
dest[i] = src[len - 1 - i];
}
dest[len] = '\0';
}
int main()
{
const char *src = "world";
// allocate destination with same length + null terminator
char dest[6];
printf("Original: %s\n", src);
reverseString(src, dest);
printf("Reversed: %s\n", dest);
return 0;
}
运行该程序将输出
Original: world
Reversed: dlrow
Python 实现
def reverse_string(s):
result = ""
for i in range(len(s) - 1, -1, -1):
result += s[i]
return result
s = "world"
print(f"Original: {s}")
print(f"Reversed: {reverse_string(s)}")
运行该程序将输出
Original: world
Reversed: dlrow
Go 实现
package main
import "fmt"
func reverseString(s string) string {
runes := []rune(s)
result := make([]rune, 0, len(runes))
for i := len(runes) - 1; i >= 0; i-- {
result = append(result, runes[i])
}
return string(result)
}
func main() {
s := "world"
fmt.Println("Original:", s)
fmt.Println("Reversed:", reverseString(s))
}
运行该程序将输出
Original: world
Reversed: dlrow
Go 中先将字符串转为 []rune 以支持 Unicode,然后从末尾向前遍历,将字符逐个追加到新切片中,最后用 string() 转换回字符串。
递归法将字符串反转分解为:先反转除第一个字符外的子串,再将第一个字符放到末尾。
递归的两个要素:
- 基准情形:当字符串为空或只有一个字符时,直接返回。
- 递推步骤:反转除首字符外的子串,再将首字符拼接到末尾。
以 "abc" 为例:
reverse("abc")
= reverse("bc") + 'a'
= (reverse("c") + 'b') + 'a'
= ("c" + 'b') + 'a'
= "cb" + 'a'
= "cba"
C++ 实现
#include <iostream>
#include <string>
std::string reverseString(const std::string &s)
{
// base case: empty or single character
if (s.length() <= 1)
{
return s;
}
// recursive step: reverse substring + first char
return reverseString(s.substr(1)) + s[0];
}
int main()
{
std::string s = "abcde";
std::cout << "Original: " << s << "\n";
std::cout << "Reversed: " << reverseString(s) << "\n";
return 0;
}
运行该程序将输出
Original: abcde
Reversed: edcba
C 实现
在 C 中,递归反转通常通过原地交换字符实现,传递左右索引。
#include <stdio.h>
#include <string.h>
void reverseRecursive(char s[], int left, int right)
{
// base case: pointers meet or cross
if (left >= right)
{
return;
}
// swap
char temp = s[left];
s[left] = s[right];
s[right] = temp;
// recursive step
reverseRecursive(s, left + 1, right - 1);
}
int main()
{
char s[] = "abcde";
printf("Original: %s\n", s);
reverseRecursive(s, 0, strlen(s) - 1);
printf("Reversed: %s\n", s);
return 0;
}
运行该程序将输出
Original: abcde
Reversed: edcba
Python 实现
def reverse_string(s):
# base case: empty or single character
if len(s) <= 1:
return s
# recursive step
return reverse_string(s[1:]) + s[0]
s = "abcde"
print(f"Original: {s}")
print(f"Reversed: {reverse_string(s)}")
运行该程序将输出
Original: abcde
Reversed: edcba
Go 实现
package main
import "fmt"
func reverseString(s string) string {
runes := []rune(s)
reverseRecursive(runes, 0, len(runes)-1)
return string(runes)
}
func reverseRecursive(runes []rune, left, right int) {
// base case: pointers meet or cross
if left >= right {
return
}
// swap
runes[left], runes[right] = runes[right], runes[left]
// recursive step
reverseRecursive(runes, left+1, right-1)
}
func main() {
s := "abcde"
fmt.Println("Original:", s)
fmt.Println("Reversed:", reverseString(s))
}
运行该程序将输出
Original: abcde
Reversed: edcba
Go 的递归反转采用原地交换方式,通过传递 []rune 切片和左右索引实现。切片在 Go 中是引用类型,对切片元素的修改在函数外可见,因此不需要返回切片。
栈方法反转字符串
利用栈的后进先出(LIFO)特性:先将所有字符依次压入栈,再依次弹出,自然得到反转顺序。
C++ 实现
#include <iostream>
#include <string>
#include <stack>
std::string reverseString(const std::string &s)
{
std::stack<char> st;
// push all characters onto stack
for (char c : s)
{
st.push(c);
}
// pop all characters to build reversed string
std::string result = "";
while (!st.empty())
{
result += st.top();
st.pop();
}
return result;
}
int main()
{
std::string s = "hello";
std::cout << "Original: " << s << "\n";
std::cout << "Reversed: " << reverseString(s) << "\n";
return 0;
}
运行该程序将输出
Original: hello
Reversed: olleh
C 实现
C 语言没有内置栈,我们可以用数组和栈顶指针模拟栈。关于栈的数组实现的详细说明,可参考 栈_数组实现。
#include <stdio.h>
#include <string.h>
void reverseString(char s[])
{
int len = strlen(s);
// use array as stack
char stack[256];
int top = -1;
// push all characters onto stack
for (int i = 0; i < len; i++)
{
stack[++top] = s[i];
}
// pop all characters back into string
for (int i = 0; i < len; i++)
{
s[i] = stack[top--];
}
}
int main()
{
char s[] = "hello";
printf("Original: %s\n", s);
reverseString(s);
printf("Reversed: %s\n", s);
return 0;
}
运行该程序将输出
Original: hello
Reversed: olleh
Python 实现
def reverse_string(s):
stack = []
# push all characters onto stack
for c in s:
stack.append(c)
# pop all characters to build reversed string
result = ""
while stack:
result += stack.pop()
return result
s = "hello"
print(f"Original: {s}")
print(f"Reversed: {reverse_string(s)}")
运行该程序将输出
Original: hello
Reversed: olleh
Go 实现
Go 没有内置的泛型栈,我们可以使用切片模拟栈的行为。
package main
import "fmt"
func reverseString(s string) string {
runes := []rune(s)
stack := make([]rune, 0, len(runes))
// push all characters onto stack
for _, r := range runes {
stack = append(stack, r)
}
// pop all characters to build reversed string
result := make([]rune, 0, len(runes))
for len(stack) > 0 {
// pop from top
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
result = append(result, top)
}
return string(result)
}
func main() {
s := "hello"
fmt.Println("Original:", s)
fmt.Println("Reversed:", reverseString(s))
}
运行该程序将输出
Original: hello
Reversed: olleh
Go 中使用切片模拟栈:append 相当于压栈,stack[:len(stack)-1] 相当于弹栈。同样先转为 []rune 以确保 Unicode 安全。
各方法对比
| 方法 | 时间复杂度 | 空间复杂度 | 是否原地 | 特点 |
|---|---|---|---|---|
| 双指针法 | O(n) | O(1) | 是 | 最高效,推荐使用 |
| 新建字符串法 | O(n) | O(n) | 否 | 简单直观,不修改原字符串 |
| 递归法 | O(n) | O(n) | C 版本是 | 优雅但可能栈溢出 |
| 栈方法 | O(n) | O(n) | 否 | 展示栈的应用,教学价值高 |
注意:Python 中字符串反转最简洁的写法是切片
s[::-1],以上实现主要是为了演示算法原理。

浙公网安备 33010602011771号