解题思路
1.题目描述
给你一个字符串 s ,颠倒字符串中单词的顺序。单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的单词分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例1:
输入:s = “the sky is blue”
输出:“blue is sky the”
示例2:
输入:s = " hello world "
输出:“world hello”
解释:颠倒后的字符串中不能存在前导空格和尾随空格
示例3:
输入:s = “a good example”
输出:“example good a”
解释:如果两个单词间有多余的空格,颠倒后的字符串需要将单词间的空格减少到仅有一个。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-words-in-a-string
2.题目分析
2.1 整体思路描述
按照题意,要求我们进行的其实是两个工作,其一是去除字符串中不规则的空格字符,不规则的定义就是空格重复或者首尾的空格。其二就是进行字符串中每个单词的倒置,注意这里提到的是单词的倒置,单词内的字符顺序需要保持先后的一致性。这似乎是有点复杂的,但其实我们可以将其进行两次倒置。采取负负得正的效果将单词进行倒置。
对上述的倒置思路我们给出一个例子。假设有字符串" hello world "
我们需要进行单词的倒置,我们就可以将其分为两个步骤,我们首先将整个字符串进行倒置,这时就变成了"dlrow olleh"
,此时我们只需要在对这个字符串中的每个单词进行局部的倒置就可以完成题目要求达到修改字符串为"world hello"
似乎此时解题步骤已经出来了,那就是下面三个步骤
- 不规则空格去除
- 字符串倒置
- 字符串内局部单词倒置
我们只需要按照上述步骤编码就可以了,其实上述三个不走的先后顺序是可以随意调换的,这对最后结果是没有影响的。但是在操作的时候对于字符串的操作是需要比较仔细的,因为很容易就会出错。
2.2 实施
2.2.1 去除不规则空格
我们在去除字符串的不规则空格时一般会想到使用string自带的erase
函数,我们这里来看一下erase函数的使用规则
形式 | 说明 |
---|---|
string& erase (size_t pos = 0, size_t len = npos) | 删除从pos开始的len个字符 |
iterator erase (const_iterator p); | 删除迭代器p指向的元素 |
iterator erase (const_iterator first, const_iterator last) | 删除[first,last)范围的元素 |
利用上面的erase
函数的话我们就可以采用一次循环,在遍历到重复空格时进行重复值的删除。但是使用这个形式的话我们会发现首尾的单个空格去除需要额外处理,为了方便起见,在实现时我选择将首尾位置的空格放在循环外单独处理。
在本题中使用erase函数其实效率比较低,因为erase
本身就是一个O(N)
的操作,所以我们当然可以尝试通过其他方法进行优化,我们前面了解过,在处理数组类似的数据结构时如果使用双指针可以达到**复杂度降重**
的效果。在本题中我们亦可以以双指针的思想进行不规则空格去除。具体思想类似前面的题目中的27_移除元素。我们对其思想进行一个基本的描述
- 定义快慢指针遍历字符串,初始化快慢指针为首位置,
fast
作为工作指针,slow
作为赋值指针。 - 以快指针
fast
作为工作指针循环遍历,当快指针遍历到一个空格,则说明这是单词之间的分隔,不是单词的内容,所以此时fast
指针继续向前移动直到遇到一个非空格字符,而slow指针则不动。 - 如果fast指向的不为空,说明要处理一个新的单词了,所以给
slow
赋值一个空格作为分隔然后移动slow
指针,然后将fast
的值赋值给slow
的值,slow
和fast
同时移动 - 去重部分代码如下:
if(s[fast] != ' '){
if(slow !=0 ) s[slow++]=' ';
while(fast<s.size() && s[fast]!=' '){
s[slow++]=s[fast++]