当前位置: 首页 > news >正文

【LeetCode】151.翻转字符串里的单词(三种方法,java实现)

分析

方法一:使用语言特性

思路和算法

很多语言对字符串提供了 split(拆分),reverse(翻转)和 join(连接)等方法,因此我们可以简单的调用内置的 API 完成操作:

  1. 使用 split 将字符串按空格分割成字符串数组;
  2. 使用 reverse 将字符串数组进行反转;
  3. 使用 join 方法将字符串数组拼成一个字符串。

fig

class Solution {
    public String reverseWords(String s) {
        // 除去开头和末尾的空白字符
        s = s.trim();
        // 正则匹配连续的空白字符作为分隔符分割
        List<String> wordList = Arrays.asList(s.split("\\s+"));
        Collections.reverse(wordList);
        return String.join(" ", wordList);
    }
}

复杂度分析

  • 时间复杂度:O(N),其中 N 为输入字符串的长度。
  • 空间复杂度:O(N),用来存储字符串分割之后的结果。

方法二:自行编写对应的函数

思路和算法

我们也可以不使用语言中的 API,而是自己编写对应的函数。在不同语言中,这些函数实现是不一样的,主要的差别是有些语言的字符串不可变(如 Java 和 Python),有些语言的字符串可变(如 C++)。

对于字符串不可变的语言,首先得把字符串转化成其他可变的数据结构,同时还需要在转化的过程中去除空格。

fig

对于字符串可变的语言,就不需要再额外开辟空间了,直接在字符串上原地实现。在这种情况下,反转字符和去除空格可以一起完成。

fig

class Solution {
    public StringBuilder trimSpaces(String s) {
        int left = 0, right = s.length() - 1;
        // 去掉字符串开头的空白字符
        while (left <= right && s.charAt(left) == ' ') ++left;

        // 去掉字符串末尾的空白字符
        while (left <= right && s.charAt(right) == ' ') --right;

        // 将字符串间多余的空白字符去除
        StringBuilder sb = new StringBuilder();
        while (left <= right) {
            char c = s.charAt(left);

            if (c != ' ') sb.append(c);
            else if (sb.charAt(sb.length() - 1) != ' ') sb.append(c);

            ++left;
        }
        return sb;
    }

    public void reverse(StringBuilder sb, int left, int right) {
        while (left < right) {
            char tmp = sb.charAt(left);
            sb.setCharAt(left++, sb.charAt(right));
            sb.setCharAt(right--, tmp);
        }
    }

    public void reverseEachWord(StringBuilder sb) {
        int n = sb.length();
        int start = 0, end = 0;

        while (start < n) {
            // 循环至单词的末尾
            while (end < n && sb.charAt(end) != ' ') ++end;
            // 翻转单词
            reverse(sb, start, end - 1);
            // 更新start,去找下一个单词
            start = end + 1;
            ++end;
        }
    }

    public String reverseWords(String s) {
        StringBuilder sb = trimSpaces(s);

        // 翻转字符串
        reverse(sb, 0, sb.length() - 1);

        // 翻转每个单词
        reverseEachWord(sb);

        return sb.toString();
    }
}

复杂度分析

  • 时间复杂度:O(N),其中 N 为输入字符串的长度。
  • 空间复杂度:JavaPython 的方法需要 O(N)O(N) 的空间来存储字符串,而 C++ 方法只需要 O(1) 的额外空间来存放若干变量。

方法三:双端队列

思路和算法

由于双端队列支持从队列头部插入的方法,因此我们可以沿着字符串一个一个单词处理,然后将单词压入队列的头部,再将队列转成字符串即可。

fig

class Solution {
    public String reverseWords(String s) {
        int left = 0, right = s.length() - 1;
        // 去掉字符串开头的空白字符
        while (left <= right && s.charAt(left) == ' ') ++left;

        // 去掉字符串末尾的空白字符
        while (left <= right && s.charAt(right) == ' ') --right;

        Deque<String> d = new ArrayDeque();
        StringBuilder word = new StringBuilder();
        
        while (left <= right) {
            char c = s.charAt(left);
            if ((word.length() != 0) && (c == ' ')) {
                // 将单词 push 到队列的头部
                d.offerFirst(word.toString());
                word.setLength(0);
            } else if (c != ' ') {
                word.append(c);
            }
            ++left;
        }
        d.offerFirst(word.toString());

        return String.join(" ", d);
    }
}

复杂度分析

  • 时间复杂度:O(N),其中 N 为输入字符串的长度。
  • 空间复杂度:O(N),双端队列存储单词需要 O(N) 的空间。

相关文章:

  • 【LeetCode】28.实现strstr() (KMP超详细讲解,sunday解法等五种方法,java实现)
  • 【LeetCode】209.长度最小的子数组(滑动窗口,双指针等五种方法助你开阔思路,java实现)
  • Typora快捷键大全(含Windows和mac)!提升你的写作效率
  • Java基础(七):栈 Stack(使用方法详解)
  • Java基础(六):HashMap(使用方法详解)
  • Java基础 (三):LinkedList(含使用方法详解)
  • Java基础(二):迭代器(Iterator)(含使用方法详解)
  • Java基础(一):Java集合框架(超详细解析,看完面试不再怕)
  • 【LeetCode】206.反转链表(动图解析,双指针+递归,java实现)
  • 【LeetCode】21.合并两个有序列表(递归+迭代,java实现,含gif动图)
  • 一套通用的Java后台管理系统,采用springboot实现(附带源码地址)
  • HashMap方法之computeIfAbsent()用法详解,含案例分析
  • 【LeetCode】219.存在重复元素 II (四种解法开拓思路,java实现)
  • 【LeetCode】49.字母异位词分组 (三种解法开拓思路,java实现)
  • 【LeetCode】36.有效的数独 (一次迭代,使用哈希表,java实现)
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • [nginx文档翻译系列] 控制nginx
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 【Linux系统编程】快速查找errno错误码信息
  • Java小白进阶笔记(3)-初级面向对象
  • js 实现textarea输入字数提示
  • Mithril.js 入门介绍
  • Xmanager 远程桌面 CentOS 7
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 看域名解析域名安全对SEO的影响
  • 使用SAX解析XML
  • 思维导图—你不知道的JavaScript中卷
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 小试R空间处理新库sf
  • 如何在招聘中考核.NET架构师
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #Linux(Source Insight安装及工程建立)
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (转)jdk与jre的区别
  • (转)详解PHP处理密码的几种方式
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .NET 设计模式初探
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .NET中两种OCR方式对比
  • /proc/vmstat 详解
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [ vulhub漏洞复现篇 ] Apache Flink目录遍历(CVE-2020-17519)
  • [ACTF2020 新生赛]Include
  • [Android]一个简单使用Handler做Timer的例子
  • [BIZ] - 1.金融交易系统特点
  • [C]编译和预处理详解
  • [c++] C++多态(虚函数和虚继承)