反转字符串中的单词(Reverse Words)

前言:在 前面一篇文章中,我们在反转长度不等的两块连续内存块的算法的基础上推导出了反转长度不等的两块不连续内存块的算法。在本文中,我将使用前文分析的算法去解决一个更复杂的问题:反转字符串中的单词。

引子
话说有这样一道算法题:
PROBLEM: Reverse Words
COMMENT: Write a function that reverses the order of the words in a string.For instance,your function should transform the string "Do or do not,there is no try." to "try.no is there not,do or Do". Assume that all words are space delimited and treat punctuation the same as letters.

这个问题常见国内外各大IT论坛中,而且被多家著名的IT公司频繁拿来用来作为面试题去考察面试者的基本功。从题目涉及到的内容来看,这个题并不是很难,但是如果对时间复杂度和空间复杂度进行限制的话,在短时间内设计出一个让面试官满意的算法也并不是一件简单的事情。接下来,我首先简单介绍一下最常见的算法,这个算法思路比较直接,但是在时间复杂度和空间复杂度上很难满足条件。然后我就"隆重"介绍我所采用的算法,这个算法是前篇文章介绍的算法的一个延伸,并采用一个重要的解决问题的技巧-"Divide-and-Conquer"。

分析篇
当我们初次接触这个题的时候,我相信我们的脑海了已经浮现出一个完整的解决方案:
1。开辟一段内存用来存放反转后的结果,这段内存的大小应该不小于源字符串的大小。
2。由于我们要反转字符串,我们很自然的就会想到从字符串的尾部向字符串的头部遍历。
3。在遍历的过程中,当我们找到一个单词的时候,就把这个单词拷贝到目标内存块中。
4。当我们遍历了字符串中所有的字符时,遍历过程中。由于是从后向前遍历,这时并没有什么标识符标识结束,我们只有先记录下字符串的长度,当剩下的字符串长度为0时,遍历结束。
这个解决方案虽然简单,但是在实现的时候却总能碰到一些困难。由于我们是从后向前遍历,我们按顺序获得的单词中的字符是反序的,但是当我们把这个单词拷贝到目标内存块时却要正序拷贝。我们同时还要记录要拷贝单词的首地址和末地址,还要提防"Off-By-One"的错误。
除了实现起来会遇到点麻烦外,这个算法最大的问题就是在于目标内存块的开辟,导致在空间复杂度上不够理想。由于我们事先不可能知道源字符串的长度,我们更不能假定源字符串的最大长度(还记得"缓冲区溢出"么?),所以我们至少要开辟一段和源字符串一样长度的内存块,这样算法的空间复杂度就达到O(n)。同时这个算法首先要遍历字符串来获得字符串的长度,然后还要对字符串再进行一次遍历来获得字符串中的单词,这样算法的时间复杂度就达到O(n)。

是不是还存在优化的空间呢?答案当然时肯定的。如果我们跳出我们熟悉的思路框架,站的更高一点,从更高一级的抽象去分析这个问题,我们获得了一个完全不同于传统算法的解决方案。
从整体来说,我们要处理的是由多个单词(单词之间用空格分割)构成的字符串,这个字符串中基本的元素就是单词和空格。我首先拿几个简单的特例来分析一下,看看有没有什么收获。
1>  字符串中没有空格。这样字符串中的所有元素可以被看成是一个单词。这时候,就没有既要进行任何操作。
2>  字符串中有一个空格(此时暂时不考虑空格出现在字符串最前面或者最后面的情况)。这是反转两个单词的操作就等价于反转长度不等的两个不连续内存块的操作,这个操作我们已经在 前篇文章中实现了。
3>  字符串中有两个空格。例如这样的字符串"Tom Jerry Mike",我们此时该如何处理这个字符串呢?结合前两个特例的分析,我立即想到了一种最常见的解决问题的思路-"Divide-and-Conquer"。总体来说,我们可以将包含多于一个空格的字符串划分成三个部分:
     | 位于空格左边的子字符串 | 空格 | 位于空格右边的子字符串 |

这样我们就要完成下面的处理:
step1。对位于空格左边的子字符串进行"Reverse Words"操作;
step2。对位于空格右边的子字符串进行"Reverse Words"操作;
step3。对整个字符串进行分转处理。
如果左右字符串中还包含多于一个空格,我们可以分别对他们再进行这样的分割+反转的操作,直到字符串中空格的数量不大于1。
看到这里,你是不是联想到一个常见的算法和这个算法有惊人的相似?对,就是"Merge Sort"算法,它们之间唯一的区别就在于"Merge Sort"中是进行排序操作,而这里需要进行反转操作。借用"Merge Sort"算法的人气,我把本文的算法称为 "Merge Reverse"算法。
这里还剩下一个问题,对于字符串中的多个空格,我们该选用那个空格做分割点?如果你联想到了"Binary Search",答案就霍然明朗了,我们可以选用处于中间位置(median)的空格作为分割点。

实现篇
有了上面的分析,再结合 前篇文章中实现的算法,本文的算法实现起来就显得十分简单了:
void *  reverseStringByWord( void *  pMemory,size_t memTotalSize)
{
    
if (NULL == pMemory)    return<
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值