【经典算法】LeetCode 151. 反转字符串中的单词(Java/C/Python3实现含注释说明,中等)

简介: 【经典算法】LeetCode 151. 反转字符串中的单词(Java/C/Python3实现含注释说明,中等)

题目描述

给定一个字符串 s,反转字符串中每个单词的字符顺序,同时保留空格和单词的初始顺序。

示例 1:

输入: s = "Let's code in Python"
输出: "s'teL edoc ni nohtyP"

示例 2:

输入: s = "a good   example"
输出: "a doog   elpmaxe"

原题:151. 反转字符串中的单词

思路及实现

方式一:使用双指针反转字符串

思路

  1. 首先将字符串按空格分割成单词数组。
  2. 遍历单词数组,对每个单词使用双指针进行反转。
  3. 将反转后的单词重新拼接成字符串。

代码实现

Java版本
public class Solution {  
    public String reverseWords(String s) {  
        // 先将字符串按空格分割成单词数组  
        String[] words = s.split("\\s+");  
        // 反转单词数组  
        reverse(words, 0, words.length - 1);  
        // 使用StringBuilder将单词数组重新组合成字符串  
        StringBuilder sb = new StringBuilder();  
        for (String word : words) {  
            sb.append(word).append(" ");  
        }  
        // 移除末尾多余的空格并返回结果  
        return sb.toString().trim();  
    }  
  
    // 反转数组指定区间的元素  
    private void reverse(String[] arr, int start, int end) {  
        while (start < end) {  
            String temp = arr[start];  
            arr[start] = arr[end];  
            arr[end] = temp;  
            start++;  
            end--;  
        }  
    }  
  
    /**
    public static void main(String[] args) {  
        Solution solution = new Solution();  
        String s = "the sky is blue";  
        String reversed = solution.reverseWords(s);  
        System.out.println(reversed); // 输出: "blue is sky the"  
    } 
     */ 
}

说明:

Java中使用StringBuilderreverse方法反转字符串,然后遍历单词数组,将反转后的单词依次添加到StringBuilder中,最后返回去除末尾空格的字符串。

C语言版本
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void reverse(char *start, char *end) {
    char temp;
    while (start < end) {
        temp = *start;
        *start++ = *end;
        *end-- = temp;
    }
}
char *reverseWords(char *s) {
    char *word_start = NULL;
    char *temp = s;
    int len = strlen(s);
    for (int i = 0; i <= len; i++) {
        if (s[i] == ' ' || s[i] == '\0') {
            if (word_start) {
                reverse(word_start, temp - 1);
                word_start = NULL;
            }
        } else if (!word_start) {
            word_start = &s[i];
        }
        if (s[i] == '\0') {
            if (word_start) {
                reverse(word_start, temp - 1);
            }
        }
    }
    return s;
}

说明:

C语言中没有直接反转字符串的函数,所以需要自己实现reverse函数。通过遍历字符串,找到每个单词的起始和结束位置,然后调用reverse函数反转单词。

Python3版本
def reverseWords(s: str) -> str:
    words = s.split()
    reversed_words = [word[::-1] for word in words]
    return ' '.join(reversed_words)

说明:

Python3中利用列表推导式和字符串切片[::-1]来反转每个单词,然后使用join方法将反转后的单词拼接成字符串。

Golang版本
package main
import (
  "fmt"
  "strings"
)
func reverseWords(s string) string {
  words := strings.Fields(s)
  reversedWords := make([]string, len(words))
  for i, word := range words {
    runes := []rune(word)
    for j, k := 0, len(runes)-1; j < k; j, k = j+1, k-1 {
      runes[j], runes[k] = runes[k], runes[j]
    }
    reversedWords[i] = string(runes)
  }
  return strings.Join(reversedWords, " ")
}
func main() {
  s := "Let's code in Python"
  fmt.Println(reverseWords(s))
}

说明:

Golang中同样使用strings.Fields分割字符串,然后遍历单词数组,对每个单词使用双指针反转字符顺序,最后使用strings.Join拼接反转后的单词。

复杂度分析

复杂度分析

时间复杂度:O(n)
  • Java: O(n),其中 n 是字符串 s 的长度。这是因为我们需要遍历整个字符串来分割单词,并且对于每个单词,反转操作也是线性的。
  • C: O(n),原因与 Java 类似,需要遍历整个字符串进行单词分割和反转。
  • Python3: O(n),Python 的字符串操作也是线性的。
  • Golang: O(n),同样是线性的时间复杂度,因为需要遍历字符串和反转每个单词。
空间复杂度: O(n)/O(1)
  • Java: O(n),StringBuilder 和 单词数组都需要额外的空间来存储结果。
  • C: O(1),除了输入字符串本身占用的空间外,没有使用额外的动态分配空间。这里假设反转操作是在原字符串上进行的。
  • Python3: O(n),列表推导式和字符串连接都会使用额外的空间。
  • Golang: O(n),reversedWords 切片需要额外的空间来存储反转后的单词。

方式二:递归

思路

使用递归来实现字符串中单词的反转,我们可以将问题分解为两个子问题:

  1. 反转字符串中的最后一个单词。
  2. 递归地反转除最后一个单词之外的其他部分,并将反转后的结果与第1步中反转的最后一个单词连接起来。

具体步骤如下:

  1. 找到最后一个单词的起始位置:我们可以从字符串的末尾开始遍历,找到最后一个单词的起始位置。这通常是通过找到最后一个非空格字符的位置,然后向前遍历直到遇到空格或字符串的起始位置。
  2. 反转最后一个单词:使用双指针法,一个指针指向单词的起始位置,另一个指针指向单词的末尾位置,然后交换两个指针所指向的字符,直到两个指针相遇或交错。
  3. 递归处理剩余部分:将最后一个单词之后的所有字符(包括空格)作为新的字符串,递归调用反转函数。
  4. 拼接结果:将递归返回的结果与第2步中反转的最后一个单词拼接起来,得到最终的结果。

代码实现

Java版本
public class ReverseWordsRecursively {
    public String reverseWords(String s) {
        // 处理边界情况
        if (s == null || s.isEmpty()) {
            return s;
        }
        
        // 去除字符串两端的空格
        s = s.trim();
        
        // 递归反转字符串中的单词
        return reverseWordsRecursiveHelper(s, s.length() - 1);
    }
    
    private String reverseWordsRecursiveHelper(String s, int endIndex) {
        if (endIndex < 0) {
            return "";
        }
        
        // 找到最后一个单词的起始位置
        int startIndex = endIndex;
        while (startIndex >= 0 && s.charAt(startIndex) != ' ') {
            startIndex--;
        }
        
        // 反转最后一个单词
        StringBuilder reversedWord = new StringBuilder();
        for (int i = startIndex + 1; i <= endIndex; i++) {
            reversedWord.append(s.charAt(i));
        }
        
        // 递归处理剩余部分,并与反转的单词拼接
        String remaining = reverseWordsRecursiveHelper(s, startIndex - 1);
        return (remaining.isEmpty() ? "" : remaining + " ") + reversedWord.toString();
    }
    
    public static void main(String[] args) {
        ReverseWordsRecursively solution = new ReverseWordsRecursively();
        String s = "Let's code together";
        String reversed = solution.reverseWords(s);
        System.out.println(reversed); // 输出 "s'teL edoc rehtegot"
    }
}
C语言版本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
void reverseRange(char *start, char *end) {
    char temp;
    while (start < end) {
        temp = *start;
        *start++ = *end;
        *end-- = temp;
    }
}
char *reverseWordsRecursive(char *s, int endIndex) {
    if (endIndex < 0) {
        return strdup("");
    }
    
    // 找到最后一个单词的起始位置
    int startIndex = endIndex;
    while (startIndex >= 0 && s[startIndex] != ' ') {
        startIndex--;
    }
    
    // 反转最后一个单词
    reverseRange(&s[startIndex + 1], &s[endIndex]);
    
    // 递归处理剩余部分
    char *remaining = reverseWordsRecursive(s, startIndex - 1);
    
    // 拼接结果
    int remainingLength = strlen(remaining);
    int newLength = endIndex - startIndex + 1 + remainingLength;
    char *result = (char *)malloc(newLength + 1); // +1 for null terminator
    if (remainingLength > 0) {
        strcpy(result, remaining);
        strcat(result, " ");
    }
    strcat(result, &s[startIndex + 1]); // 拼接反转后的最后一个单词
    result[newLength] = '\0'; // 添加字符串结束符
    
    // 释放递归调用中分配的内存
    free(remaining);
    
    return result;
}
char *reverseWords(char *s) {
    // 去除字符串两端的空格
    int length = strlen(s);
    int start = 0;
    while (start < length && s[start] == ' ') {
        start++;
    }
    int end = length - 1;
    while (end >= start && s[end] == ' ') {
        end--;
    }
    
    // 复制处理后的字符串到新数组(可选,取决于原字符串是否可修改)
    char *processed = (char *)malloc(end - start + 2); // +2 for possible spaces and null terminator
    strncpy(processed, s + start, end - start + 1);
    processed[end - start + 1] = '\0';
    
    // 递归反转单词
    char *reversed = reverseWordsRecursive(processed, end - start);
    
    // 释放处理后的字符串内存
    free(processed);
    
    return reversed;
}
int main() {
    char s[] = "Let's code together ";
    char *reversed = reverseWords(s);
    printf("%s\n", reversed); // 输出 "s'teL edoc rehtegot"
    
    // 释放最终结果的内存
    free(reversed);
    
    return 0;
}
Python3
def reverse_range(s, start, end):
    return s[:start] + s[start:end][::-1] + s[end:]
def reverse_words_recursive(s, end_index):
    if end_index < 0:
        return ""
    
    # 找到最后一个单词的起始位置
    start_index = end_index
    while start_index >= 0 and s[start_index] != ' ':
        start_index -= 1
    
    # 反转最后一个单词
    reversed_word = s[start_index + 1:end_index + 1][::-1]
    
    # 递归处理剩余部分,并与反转的单词拼接
    remaining = reverse_words_recursive(s, start_index - 1)
    return (remaining + " " if remaining else "") + reversed_word
def reverse_words(s):
    # 去除字符串两端的空格
    s = s.strip()
    
    # 递归反转单词
    reversed_s = reverse_words_recursive(s, len(s) - 1)
    
    return reversed_s
# 测试
s = "Let's code together"
reversed_s = reverse_words(s)
print(reversed_s)  # 输出 "s'teL edoc rehtegot"
Golang版本
package main
import (
  "fmt"
  "strings"
)
func reverseRange(s string, start, end int) string {
  runes := []rune(s)
  for i, j := start, end; i < j; i, j = i+1, j-1 {
    runes[i], runes[j] = runes[j], runes[i]
  }
  return string(runes)
}
func reverseWordsRecursive(s string, endIndex int) string {
  if endIndex < 0 {
    return ""
  }
  
  // 找到最后一个单词的起始位置
  startIndex := endIndex
  for startIndex >= 0 && s[startIndex] != ' ' {
    startIndex--
  }
  
  // 反转最后一个单词
  reversedWord := reverseRange(s, startIndex+1, endIndex)
  
  // 递归处理剩余部分,并与反转的单词拼接
  remaining := reverseWordsRecursive(s, startIndex-1)
  return remaining + " " + reversedWord
}
func reverseWords(s string) string {
  // 去除字符串两端的空格
  s = strings.TrimSpace(s)
  
  // 递归反转单词
  reversed := reverseWordsRecursive(s, len(s)-1)
  
  // 去除可能的首部空格(由于递归拼接时添加的)
  if len(reversed) > 0 && reversed[0] == ' ' {
    reversed = reversed[1:]
  }
  
  return reversed
}
func main() {
  s := "Let's code together "
  reversed := reverseWords(s)
  fmt.Println(reversed) // 输出 "s'teL edoc rehtegot"
}

说明

在上面的Go示例中,reverseWords 函数是入口点,它首先去除输入字符串两端的空格,然后调用 reverseWordsRecursive 函数来递归地反转字符串中的每个单词。reverseRange 是一个辅助函数,用于反转字符串中指定范围内的字符。

reverseWordsRecursive 函数中,我们找到最后一个单词的起始和结束位置,然后调用 reverseRange 来反转这个单词。之后,我们递归地对剩余部分的字符串进行相同的操作,并将反转后的单词与剩余部分拼接起来。

最后,在 main 函数中,我们调用 reverseWords 并打印出结果。

请注意,这些代码示例是为了演示如何递归地反转字符串中的单词,并可能不是最优或最高效的解决方案。在实际应用中,可能需要考虑性能和内存使用情况,并相应地优化代码。

复杂度分析

  • 时间复杂度:O(n),其中 n 是字符串 s 的长度。遍历字符串和栈操作都是线性的。
  • 空间复杂度:O(n),在最坏情况下,栈中可能存储了整个字符串的字符或单词,因此需要额外的空间。

总结

方式 优点 缺点 时间复杂度 空间复杂度
方式一 直观且易于理解 在 Java 和 Python 中可能需要额外的空间 O(n) Java, Python, Golang: O(n), C: O(1)
方式二 递归 实现相对复杂,可能导致栈溢出(在递归情况下) O(n) O(n)

相似题目

相似题目 难度 链接
翻转字符串中的元音字母 简单 力扣-345
字符串转换整数 (atoi) 中等 力扣-8
验证回文字符串 简单 力扣-125
最长回文子串 中等 力扣-5

请注意,上述相似题目和难度是基于 LeetCode 的分类和评级,并且可能随时间而有所变化。这些题目与反转字符串中的单词问题在字符串处理方面有一定的相关性,但解决方法和技术可能有所不同。

相关文章
|
19天前
|
机器学习/深度学习 人工智能 算法
猫狗宠物识别系统Python+TensorFlow+人工智能+深度学习+卷积网络算法
宠物识别系统使用Python和TensorFlow搭建卷积神经网络,基于37种常见猫狗数据集训练高精度模型,并保存为h5格式。通过Django框架搭建Web平台,用户上传宠物图片即可识别其名称,提供便捷的宠物识别服务。
212 55
|
7天前
|
存储 缓存 监控
局域网屏幕监控系统中的Python数据结构与算法实现
局域网屏幕监控系统用于实时捕获和监控局域网内多台设备的屏幕内容。本文介绍了一种基于Python双端队列(Deque)实现的滑动窗口数据缓存机制,以处理连续的屏幕帧数据流。通过固定长度的窗口,高效增删数据,确保低延迟显示和存储。该算法适用于数据压缩、异常检测等场景,保证系统在高负载下稳定运行。 本文转载自:https://www.vipshare.com
102 66
|
2月前
|
搜索推荐 Python
利用Python内置函数实现的冒泡排序算法
在上述代码中,`bubble_sort` 函数接受一个列表 `arr` 作为输入。通过两层循环,外层循环控制排序的轮数,内层循环用于比较相邻的元素并进行交换。如果前一个元素大于后一个元素,就将它们交换位置。
139 67
|
2月前
|
存储 搜索推荐 Python
用 Python 实现快速排序算法。
快速排序的平均时间复杂度为$O(nlogn)$,空间复杂度为$O(logn)$。它在大多数情况下表现良好,但在某些特殊情况下可能会退化为最坏情况,时间复杂度为$O(n^2)$。你可以根据实际需求对代码进行调整和修改,或者尝试使用其他优化策略来提高快速排序的性能
128 61
|
2月前
|
算法 数据安全/隐私保护 开发者
马特赛特旋转算法:Python的随机模块背后的力量
马特赛特旋转算法是Python `random`模块的核心,由松本真和西村拓士于1997年提出。它基于线性反馈移位寄存器,具有超长周期和高维均匀性,适用于模拟、密码学等领域。Python中通过设置种子值初始化状态数组,经状态更新和输出提取生成随机数,代码简单高效。
118 63
|
29天前
|
机器学习/深度学习 人工智能 算法
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
宠物识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了37种常见的猫狗宠物种类数据集【'阿比西尼亚猫(Abyssinian)', '孟加拉猫(Bengal)', '暹罗猫(Birman)', '孟买猫(Bombay)', '英国短毛猫(British Shorthair)', '埃及猫(Egyptian Mau)', '缅因猫(Maine Coon)', '波斯猫(Persian)', '布偶猫(Ragdoll)', '俄罗斯蓝猫(Russian Blue)', '暹罗猫(Siamese)', '斯芬克斯猫(Sphynx)', '美国斗牛犬
155 29
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
|
4天前
|
算法 网络协议 Python
探秘Win11共享文件夹之Python网络通信算法实现
本文探讨了Win11共享文件夹背后的网络通信算法,重点介绍基于TCP的文件传输机制,并提供Python代码示例。Win11共享文件夹利用SMB协议实现局域网内的文件共享,通过TCP协议确保文件传输的完整性和可靠性。服务器端监听客户端连接请求,接收文件请求并分块发送文件内容;客户端则连接服务器、接收数据并保存为本地文件。文中通过Python代码详细展示了这一过程,帮助读者理解并优化文件共享系统。
|
9天前
|
存储 算法 Python
文件管理系统中基于 Python 语言的二叉树查找算法探秘
在数字化时代,文件管理系统至关重要。本文探讨了二叉树查找算法在文件管理中的应用,并通过Python代码展示了其实现过程。二叉树是一种非线性数据结构,每个节点最多有两个子节点。通过文件名的字典序构建和查找二叉树,能高效地管理和检索文件。相较于顺序查找,二叉树查找每次比较可排除一半子树,极大提升了查找效率,尤其适用于海量文件管理。Python代码示例包括定义节点类、插入和查找函数,展示了如何快速定位目标文件。二叉树查找算法为文件管理系统的优化提供了有效途径。
41 5
|
9天前
|
存储 缓存 算法
探索企业文件管理软件:Python中的哈希表算法应用
企业文件管理软件依赖哈希表实现高效的数据管理和安全保障。哈希表通过键值映射,提供平均O(1)时间复杂度的快速访问,适用于海量文件处理。在Python中,字典类型基于哈希表实现,可用于管理文件元数据、缓存机制、版本控制及快速搜索等功能,极大提升工作效率和数据安全性。
43 0
|
1月前
|
Java
Java 中的注释
1. 单行注释:// 2. 多行注释:/* */ 3. 文档注释::/** **/