【问题标题】:Reversing order of words in a sentence颠倒句子中单词的顺序
【发布时间】:2013-06-10 14:51:57
【问题描述】:
#include <iostream>
#include <cstring>
using namespace std;
void reverse(char* sentence)
{
    int index = strlen(sentence) - 1;
    char last = '\0';
    int hold = 0;
    while ( index != 0){
        while (sentence[index] != ' ')
            index--;
        hold = index; //keeps the index of whitespace
        while (sentence[index] != last){
            cout << sentence[index]; //printing till it either hits end character or whitespace.
            index++;
        }
        last = sentence[hold]; //Keeps the whitespace
        index = hold; //
    }

}
int main()
{
    char* sentence = new char[256];
    cin.getline(sentence, 256);
    reverse(sentence);
}

我想颠倒一个句子中的单词顺序,你可以在上面看到我的尝试。

示例输入和输出应该是这样的:

Howdy Mr. Mcfly? 

Mcfly? Mr. Howdy

我在哪里得到:

Howdy Mr. Mcfly?
 Mcfly?

互联网上有很多类似的问题,但我想要的是在我自己的代码中找到错误。

【问题讨论】:

  • 如果您使用 C++,为什么要使用 char*?使用字符串会容易得多。我注意到您标记为“动态数组”是否有某些特定原因您使用 char* 会导致无法接受切换到使用字符串的答案?
  • 当您通过调试器运行示例时会看到什么?
  • 现在是开始学习如何使用调试器的好时机。通过单步执行代码并检查变量,您应该能够很快找到自己的错误。
  • 是的,我知道使用字符串和其他方式会更容易,但我只是作为初学者练习。
  • 将每个单词添加到堆栈并开始弹出那些傻瓜

标签: c++ arrays


【解决方案1】:

您可以使用std::string std::vectorstd::reverse 让事情变得更简单:

std::string sentence = "Your sentence which contains ten words, two of them numbers";
std::stringstream stream(sentence);
std::vector<std::string> words;
for ( std::string word; stream >> word; )
{
    words.push_back(word);
}

现在您将所有内容都分解为单词。您现在可能想要删除问号或其他标点符号,因为当单词仍然处于正确顺序时,逻辑将更容易实现。要倒车,请执行以下操作:

std::reverse(words.begin(), word.end());

您需要包含几个标题:

#include <string> // for storing strings in a C++ way
#include <sstream> // to easily separate sentences into words
#include <vector> // to dynamically store arbitrary amounts of words
#include <algorithm> // for std::reverse

您可以通过demo on ideone.com 看到此代码的运行情况

【讨论】:

  • /* 第一个单词后不打印空格*/
    if(index > 0)
    如果句子以单个字母开头,则上述检查中不会打印空格.
    应该是if(index>=0)
  • @inquisitive_mind 打扰一下?我的答案/代码中没有类似的东西。你能告诉我你的评论应该告诉我什么吗?
【解决方案2】:

正如其他答案所建议的那样,您应该使用std::string,这可以节省很多麻烦。 但只是为了说明,

void reverse(char* sentence)
{
    int index = strlen(sentence) - 1,hold,last = '\0';
    /*For the 1st iteration last is `\0` for all others it is ` `*/
    while (index >= 0)
    {
        /*
        In your original code,
        This while loop(below) will continue to keep decrementing index 
        even below `0`,You wont exit this while loop until you encounter a ` `.
        For the 1st word of the sentence you will never come out of the loop.
        Hence the check, index>=0
        */

        while (index>=0 && sentence[index] != ' ')
        index--;

    /* You can print the whitespace later*/

    hold = index - 1;  // This keeps track of the last character 
                       // of preceding word 

    index++; //character after space

        while (sentence[index] != last)
    {
            cout << sentence[index]; 
            index++;
        }
    last = ' '; 
        index = hold; 

        /* Dont print space after 1st word*/
    if(index > 0)
    cout<<" ";
    }

}
int main()
{
    char* sentence = new char[256];
    cin.getline(sentence, 256);
    reverse(sentence);
    delete[] sentence; // Delete the allocated memory
}

尽量让它接近你的逻辑

【讨论】:

  • 正是我想要的,非常喜欢。
【解决方案3】:

当你说

index = hold;

你有一个无限循环。我相信你总是会回到你找到你的 '\0' 字符的地方。你应该做的是有两个独立的while循环。一个让你到达你的字符数组的末尾,找到'\0'。然后另一组循环返回空白,然后向前循环打印出字符。

注意:我更喜欢所有提供的答案,但这就是您发布的代码失败的原因。这是这个函数的一个版本,它只适用于 cstrings。

void reverse(char* sentence, const int START)
{
    if(sentence[START] == '\0') return;
    char last = '\0';
    int hold = 0;
    int index = START + 1;


    while(sentence[index] != '\0' && sentence[index] != ' ') {//There are better ways to do this logic, but I wanted to remain true to your implementation as much as possible
        index++;
    }

    reverse(sentence, index);
    for(int j = START; j < index; j++) {
        cout << sentence[j];
    }
    cout << endl;
    return;
}

我打印了一些额外的结束行,你当然可以随意格式化输出,困难的部分已经完成。

【讨论】:

    【解决方案4】:
    #include <sstream>
    #include <iostream>
    #include <list>
    
    int main() {
        char sentence[256];
        std::cin.getline(sentence, 256);
    
        std::istringstream f(sentence );
    
        std::string s;  
        std::list<std::string> strings;
    
        while (f >> s) 
        {
            strings.push_front(s);
        }
    }
    

    此时strings包含倒序的单词

    【讨论】:

      【解决方案5】:

      对早期版本的更正,坚决开玩笑 :)

      请注意,程序会读取所有标准输入,将每一行视为一个“句子”。

      #include <boost/spirit/include/qi.hpp>
      #include <boost/spirit/include/phoenix.hpp>
      
      namespace phx= boost::phoenix;
      using namespace boost::spirit::qi;
      
      int main()
      {
          auto out = std::ostream_iterator<std::string>(std::cout, " ");
      
          boost::spirit::istream_iterator f(std::cin), l;
          std::cin.unsetf(std::ios::skipws);
      
          parse(f, l, 
                  (
                    (as_string [ +~char_("\n\t ") ] % +char_("\t ")) 
                          [ phx::reverse(_1), phx::copy(_1, phx::ref(out)) ]
                  ) % eol [ phx::ref(std::cout) << "\n" ]
               );
      }
      

      【讨论】:

      • 这会逐个字符地反转整个句子,而不是逐个单词。
      • 啊,你说的很对 :) 哦,好吧,使用 Boost Range 我会修复 让它更优雅,但我想这超出了范围有用的答案...
      • 好吧,我个人不喜欢 boost,但其他人可能不同意。让我们看看你的提升方法;-)
      • @stefan 那里,晚餐来了:/ 然而,结果是一个近乎单线。这显然是最直接的解决方案:)
      • dafuq 我刚刚读过吗?我只是无法判断这是否正确。好吧,我只相信他实际上是工作代码(我没有安装 boost,所以我什至无法检查),但我绝不建议这样做:在我看来,代码的意图应该是直接可见的。
      【解决方案6】:

      我想使用堆栈。 1.使用 delimiter("") 对字符串进行标记 2.将单词压入堆栈 3. 弹出时,将该单词存储在新的字符串变量中。

      【讨论】:

        【解决方案7】:

        在这里,我使用将句子拆分(标记)成单词,然后将其用于反向打印。希望对您有所帮助-

        #include<iostream>
        #include<string>
        #include<algorithm>
        #include<vector>
        using namespace std;
        
        vector<string> split(char* str, char delimiter=' ')
        {
                vector<string> result;
                do
                {
                        char* begin =str;
                        while(*str != delimiter && *str)
                                str++;
                        result.push_back(string(begin, str));
                }while(0 != *str++);
                return result;
        }
        int main()
        {
                string str;
                vector<string> tempStr;
                cout<<"Enter The String: ";
                getline(cin, str);
                cout<<endl;
                tempStr=split((char*)str.c_str());
                str.clear();
                cout<<"Final Reverse: \n";
                for(int i=tempStr.size()-1; i>=0; i--) str+=tempStr.at(i)+" ";
                        //cout<<tempStr.at(i)<<" ";
                cout<<str<<endl;
                return 0;
        }
        

        【讨论】:

          【解决方案8】:

          你可以通过 C++ 的 istringstream 类来实现

          #include <bits/stdc++.h>
          using namespace std;
          int main()
          {
              string s,st,w;
              int i;
              getline(cin,s);
              istringstream is(s);
              st="";
              is>>st;
              while(is>>w)
              {
                  st=w+' '+st;
              }
              cout<<st<<endl;
              return 0;
          
          }
          

          【讨论】: