翻转句子中单词的顺序。
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。
一开始看到这题其实可以很容易地想到将每个单词保存在一个字符串里面,整个句子用一个二维字符串数组来保存,然后反转这个二维数组就可以了。事实上,若真的根据这个想法来写代码,感觉真的是费力不讨好,特别还是在用C/C++的字符串、指针的情况下(当然也可以用STL中的vector、string,那样会容易很多,这里就先自虐一下),不过其实看看写好的代码也不是很繁琐,如下所示:
解法一
void reverseWord1(char* p)
{
int wordcount = 100;
int wordlen = 15;
char** rvWord = new char*[wordcount];
char* wordstart = p;
char* wordend = p;
int i = 0;
for (i=0; *wordend != '\0'; i++) {
while( *wordend != '\0' && *wordend != ' ')
++wordend;
rvWord[i] = new char[wordlen];
char* r = rvWord[i];
while(wordstart<wordend)
*r++ = *wordstart++;
*r = '\0';
if(*wordend != '\0')
wordstart = ++wordend;
}
wordcount = i--;
for (int j=0; j<i;j++,i--) {
char* temp = rvWord[j];
rvWord[j] = rvWord[i];
rvWord[i] = temp;
}
for (int j=0; j<wordcount; ++j)
cout << rvWord[j] << " ";
for (int j=0; j<wordcount; ++j)
delete [] rvWord[j];
delete [] rvWord;
}
上面的代码其实不太简洁明了,而且用到了额外的空间,所以说费力不讨好。那就来看看其他的方法。
如果真的要一个一个单词来看呢,说到反转你能想到到什么数据结构呢?先顺着一个单词一个单词地读,再反过来一个单词一个单词地读,这样子的特点你应该能够跟栈联系起来吧!没错,我们可以先将单词按顺序一个一个进栈,单词全部进栈之后再一个一个出栈,这样就实现了翻转句子中单词的顺序。这次就不用蛋疼的C字符串指针之流的了,用用C++高大上的STL:
解法二
void reverseWord2(char* p)
{
char* wordstart = p;
char* wordend = p;
stack<string> wordstack;
while(*wordend != '\0') {
while( *wordend != '\0' && *wordend != ' ')
++wordend;
string s(wordstart,wordend);
wordstack.push(s);
if(*wordend != '\0')
wordstart = ++wordend;
}
while(!wordstack.empty()) {
string s = wordstack.top();
cout << s;
wordstack.pop();
if(!wordstack.empty()) cout << " ";
}
}
另外我们也可以从句尾开始着手,从句尾到举手将单词一个一个搬到一个新的字符串上面,从而实现句子到此的翻转:
解法三:
void reverseWord3(char* p)
{
int slen = strlen(p);
char* rvWord = new char[slen];
char* temp = rvWord;
char* wordstart = p+slen-1;
char* wordend = p+slen-1;
while(wordstart != p-1) {
while(wordstart != p-1 && *wordstart != ' ')
--wordstart;
int wlen = wordend-wordstart;
strncpy(temp,wordstart+1,wlen);
temp += wlen;
*(temp++) = ' ';
if(wordstart != p-1)
wordend = --wordstart;
}
rvWord[slen] = '\0';
cout << rvWord << endl;
delete [] rvWord;
}
以上三种方法其实都是先找出单词然后再进行操作。联想到如果对一个字符串翻转两次还是得到原来的字符串,我们也可以先翻转整个句子,这样单词内字符的顺序也改变了,然后我们再翻转每个单词中字符的顺序,这样就得到原来的单词了,整个过程如下面的例子所示:
E.g. “I am a student.”—>“.tneduts a ma I”—>“student. a am I”
代码写起来也很简单:
解法四
void Reverse(char* s, int n)
{
if (s ==NULL) return;
char* begin = s;
char* end = s+n-1;
while(begin<end) {
char temp = *begin;
*begin = *end;
*end = temp;
begin++, end--;
}
}
void reverseWord4(char* p)
{
Reverse(p, strlen(p));
char* wordstart = p;
char* wordend = p;
while(*wordend != '\0') {
while( *wordend != '\0' && *wordend != ' ')
++wordend;
Reverse(wordstart, wordend-wordstart);
if(*wordend != '\0')
wordstart = ++wordend;
}
cout << p << endl;
}