第16周项目2-用指针玩字符串(统计单词个数)

本文介绍了一个简单的C++程序,用于统计给定字符串中的单词数量。通过遍历字符串并计算空格数来实现,最终返回空格数加一作为单词总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题及代码:

/*
*Copyright (c)2014,烟台大学计算机与控制工程学院
*All rights reserved.
*文件名称:text.cpp
*作    者:赵敏
*完成日期:2014年12月12日
*版 本 号:v1.0
*
*问题描述:统计字符串str的单词个数。
*/
#include<iostream>
#include<cstdio>
using namespace std;
int awordnum(char str[]);
int main()
{
    char s1[50]="I am happy!";
    awordnum(s1);
    cout<<"单词个数为:"<<endl;
    cout<<awordnum(s1)<<endl;
    return 0;
}
int awordnum(char str[])
{

   int i=0,n=0;
 while(str[i]!='\0')
 {
     if(str[i]==' ')
       {
           n++;
         i++;
         }
         else
     i++;
 }

   return n+1;
}


运行结果:

学习心得:

    先计算的空格的数,然后空格数加一,不知道对不对。。。

 

<think>我们被要求编写一个C++程序,反转字符串中的单词顺序并移除多余空格。 例如:输入字符串 " hello world " 应该输出 "world hello" 步骤: 1. 移除多余空格:将字符串中的空格规范化,即单词间只保留一个空格,并移除开头和结尾的空格。 2. 反转整个字符串:这样单词的顺序就反了,但每个单词内部的字母顺序也反了。 3. 反转每个单词:将每个单词的字母顺序再反转回来。 另一种方法(可能更直观): 1. 先移除多余空格(可以边处理边移除,或者先处理空格)。 2. 反转整个字符串。 3. 然后反转每个单词。 或者: 1. 使用双指针移除多余空格(包括开头、中间和结尾)。 2. 反转整个字符串。 3. 反转每个单词。 详细步骤: 步骤1:移除多余空格 我们可以遍历字符串,使用双指针技巧,快指针和慢指针。 同时,我们还要处理单词之间空格数量(只保留一个)和开头结尾无空格。 步骤2:反转整个字符串 使用双指针从两端向中间交换字符。 步骤3:反转每个单词 遍历字符串,当遇到空格或到达末尾时,反转从单词开头到当前空格前一个位置的子串。 但是,我们也可以先反转整个字符串,然后移除空格?这样可能不好处理,因为空格位置会影响单词边界。 因此,我们按以下顺序: 1. 移除多余空格(同时可以去掉首尾空格,并保证单词间只有一个空格)。 2. 反转整个字符串。 3. 反转每个单词。 或者,我们也可以: 1. 移除多余空格(得到规范字符串)。 2. 反转每个单词(这样每个单词都反了,但单词顺序没变)。 3. 反转整个字符串(得到单词顺序反转,且每个单词的字母顺序正确)。 但是,如果我们先反转每个单词,再反转整个字符串,那么结果就是单词顺序反转,但每个单词的字母顺序正确吗? 例如:原字符串 "hello world" -> 先反转每个单词: "olleh dlrow" -> 再反转整个字符串: "world hello" 所以步骤可以是: 1. 移除多余空格(得到"hello world")。 2. 反转每个单词(得到"olleh dlrow")。 3. 反转整个字符串(得到"world hello")。 两种方法都可以,我们选择第二种方法:先移除多余空格,然后反转每个单词,最后反转整个字符串。 具体实现: 移除多余空格函数(使用双指针): 初始化两个指针:慢指针slow指向0,快指针fast从0开始遍历。 我们还需要一个变量记录上一个字符是否是空格(用于跳过连续空格)。 同时,我们还要处理开头和结尾的空格。但双指针可以这样处理: - 跳过开头的空格:如果开头有空格,我们让fast先走,直到遇到非空格。 - 然后,在循环中,如果遇到空格,我们只保留一个空格(但要注意,在单词之间我们保留一个空格,而结尾不能有空格)。 具体步骤(移除多余空格): 1. 跳过开头空格:让fast指针先移动到第一个非空格字符。 2. 然后,开始循环,当fast小于字符串长度时: 如果当前字符不是空格,直接复制到slow位置,slow++,fast++。 如果当前字符是空格,那么我们需要检查下一个字符是否也是空格(连续空格),但我们只保留一个空格,所以先记录这个空格(复制到slow,然后slow++,fast++),然后跳过后面连续的空格(直到遇到非空格)。 但是这样可能会在结尾留下一个空格,所以最后需要处理结尾空格。 另一种更简单的方法(移除多余空格): 我们可以先移除开头的空格,然后处理中间,最后移除结尾空格。但这样可能需要多次遍历。 我们使用双指针一次遍历完成: 1. 设置slow=0, fast=0。 2. 循环:fast指针遍历整个字符串- 如果当前字符不是空格,那么直接复制到slow,slow++,fast++。 - 如果当前字符是空格,那么检查:如果这个空格是单词后的第一个空格(即上一个字符是非空格),那么我们保留它(复制到slow,slow++,fast++),然后跳过后面连续的空格(fast继续前进直到非空格或结尾)。如果上一个字符已经是空格,那么我们就跳过当前空格(不复制,只移动fast)。 3. 循环结束后,我们可能最后多了一个空格(如果原字符串结尾有空格,我们只保留了一个,但单词后面不应该有空格),所以检查slow指针最后一位如果是空格,那么将slow减1(即去掉这个空格)。 但是,在循环中,我们也可以避免在结尾添加空格:我们在循环中确保只在单词之间添加空格(即当遇到空格且后面还有非空格单词时才添加空格)。这需要我们在添加空格时判断后面是否还有单词。 实际上,我们可以这样: 在循环中,当遇到空格时,我们不立即添加,而是标记需要添加一个空格(但需要等到下一个单词出现时再添加)。这样我们可以避免结尾的空格。 但是,为了简单,我们可以先移除多余空格(包括中间连续空格)并保留一个空格分隔单词,然后检查最后是否有空格,如果有就删除。 具体实现(移除多余空格): 我们使用一个while循环跳过开头空格(fast指针移动到第一个非空格)。 然后,当fast小于n时: while (fast < n && s[fast] != ' ') { // 复制一个单词 s[slow++] = s[fast++]; } // 现在fast指向空格,跳过所有连续空格 while (fast < n && s[fast] == ' ') { fast++; } // 如果后面还有非空格字符(即fast没有到结尾),说明当前单词后面还有单词,那么我们在当前单词后面添加一个空格 if (fast < n) { s[slow++] = ' '; // 添加一个空格分隔 } 这样,最后就不会有结尾空格,因为只有当后面还有单词时才添加空格。 步骤: 1. 移除多余空格(使用上述方法)。 2. 反转每个单词:在移除空格后的字符串中,我们遍历字符串,对于每个单词,使用双指针反转(从单词开头到结尾)。 3. 反转整个字符串。 注意:由于我们移除了多余空格,字符串长度发生了变化,所以需要调整字符串大小。 我们写代码时,可以这样: 我们直接修改传入的字符串(假设我们可以修改,且使用std::string)。 代码结构: void reverseWords(string &s) { // 步骤1:移除多余空格(使用双指针) // 步骤2:反转每个单词 // 步骤3:反转整个字符串 } 但是,我们也可以使用标准库函数,但题目要求自己实现,所以我们自己写反转函数。 示例: 输入:s = " hello world " 移除多余空格后:s = "hello world"(注意,字符串长度变为11) 反转每个单词:"olleh dlrow" 反转整个字符串:"world hello" 反转函数(反转一个子串,从left到right): 我们可以写一个辅助函数:reverseSubstr(string &s, int left, int right) 注意:在反转每个单词时,我们需要找到每个单词的起始和结束位置。 详细步骤: 步骤1:移除多余空格 int n = s.size(); int slow = 0, fast = 0; // 跳过开头空格 while (fast < n && s[fast]==' ') fast++; while (fast < n) { // 复制一个单词 while (fast < n && s[fast]!=' ') { s[slow] = s[fast]; slow++; fast++; } // 跳过空格 while (fast < n && s[fast]==' ') fast++; // 如果后面还有单词,添加一个空格 if (fast < n) { s[slow] = ' '; slow++; } } // 现在slow就是新字符串的长度 s.resize(slow); 步骤2:反转每个单词 int start = 0; for (int i = 0; i <= slow; i++) { // 当遇到空格或字符串结尾时,反转上一个单词 if (i == slow || s[i]==' ') { // 反转从start到i-1 reverseSubstr(s, start, i-1); start = i+1; // 下一个单词的起始位置 } } 步骤3:反转整个字符串 reverseSubstr(s, 0, slow-1); 辅助函数reverseSubstr: void reverseSubstr(string &s, int left, int right) { while (left < right) { swap(s[left], s[right]); left++; right--; } } 但是注意:步骤2中,我们反转每个单词时,字符串长度已经是slow了,所以循环条件i<=slow(因为当i==slow时,我们要反转最后一个单词,因为最后一个单词后面没有空格)。 测试例子: 输入:s = "hello world"(移除空格后) 步骤2:反转每个单词: i=0~5: 当i=5时,s[5]==' ',反转[0,4] -> "olleh world" start=6,然后i=10时(slow=11,所以i<11,继续)直到i=11(等于slow),反转[6,10] -> "dlrow" 然后整个字符串变为 "olleh dlrow" 步骤3:反转整个字符串:从0到10,得到 "world hello" 注意:上面的例子中,移除空格后的字符串为"hello world",长度为11(索引0~10)。 但是,我们还需要考虑边界情况,比如空字符串,或者只有一个单词。 考虑只有一个单词的情况:例如输入 "hello" 移除多余空格后:s="hello"(长度为5) 步骤2:反转每个单词,当i=5(等于slow)时,反转[0,4] -> "olleh" 步骤3:反转整个字符串:反转"olleh"->"hello"?不对,我们需要反转整个字符串,但反转整个字符串后还是"hello"?不对,因为单词顺序反转(只有一个单词所以不变),但每个单词被反转了一次,整个字符串又被反转了一次,所以最终应该是:先反转单词(得到"olleh"),再反转整个字符串(得到"hello")?不对,我们要求输出是"hello"(单词顺序反转,但只有一个单词,所以还是hello)。但我们的程序会先反转每个单词(变成"olleh"),然后反转整个字符串(又变回"hello")。所以正确。 但是,如果我们要求反转单词顺序,那么只有一个单词时,反转后还是它自己。所以正确。 但是,我们再看两个单词的情况:输入"hello world" -> 输出"world hello",正确。 另一种情况:输入多个空格,如" ",那么移除空格后,字符串为空,所以后面反转就不需要操作。 因此,我们按照上述步骤编写代码。 注意:我们修改的是原字符串,使用引用传递。 代码实现: 我们按照上述步骤编写。 注意:在移除空格时,我们使用了双指针,并且最后resize了字符串。 但是,我们也可以不使用双指针,而是用标准库函数,但题目要求移除多余空格,所以我们需要自己实现。 完整代码: */ #include <iostream> #include <string> #include <algorithm> using namespace std; class Solution { public: void reverseWords(string &s) { // 步骤1:移除多余空格 int n = s.size(); int slow = 0, fast = 0; // 跳过开头空格 while (fast < n && s[fast] == ' ') { fast++; } while (fast < n) { // 复制一个单词 while (fast < n && s[fast] != ' ') { s[slow] = s[fast]; slow++; fast++; } // 跳过单词后面的空格 while (fast < n && s[fast] == ' ') { fast++; } // 如果后面还有非空格字符,则添加一个空格 if (fast < n) { s[slow] = ' '; slow++; } } // 现在slow是新字符串的长度 s.resize(slow); // 步骤2:反转每个单词 int start = 0; for (int i = 0; i <= slow; i++) { // 当遇到空格或字符串结尾时,反转上一个单词 if (i == slow || s[i] == ' ') { reverseSubstr(s, start, i - 1); start = i + 1; } } // 步骤3:反转整个字符串 reverseSubstr(s, 0, slow - 1); } private: void reverseSubstr(string &s, int left, int right) { while (left < right) { swap(s[left], s[right]); left++; right--; } } }; // 测试 int main() { Solution sol; string s = " hello world "; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望输出: "world hello" s = "a good example"; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望输出: "example good a" s = " "; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望输出: "" (字符串) s = "hello"; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望输出: "hello" return 0; } /* 运行测试: 第一个测试:" hello world " -> 移除空格后为"hello world"(长度为11),反转每个单词后为"olleh dlrow",再反转整个字符串为"world hello"。 第二个测试:"a good example" -> 移除多余空格后为"a good example",反转每个单词:'a'->'a', 'good'->'doog', 'example'->'elpmaxe',得到"a doog elpmaxe",然后反转整个字符串:"elpmaxe doog a" -> "example good a"(注意:单词反转后,每个单词被反转了,再整个反转,所以每个单词又变回原样?不对,我们看: 原始: "a good example" 移除空格后: "a good example" 反转每个单词: "a" -> "a" "good" -> "doog" "example" -> "elpmaxe" 所以字符串变成 "a doog elpmaxe" 反转整个字符串: "elpmaxe doog a" -> 然后我们写出来是 "example" 吗?注意,反转整个字符串时,我们反转的是整个字符串"a doog elpmaxe" -> "example good a" 吗? 反转整个字符串:从第一个字符'a'到最后一个字符'e',反转后: 原:a doog elpmaxe 反转整个:elpmaxe doog a -> 这确实是 "example good a" 吗?注意,字符串是连续的,所以反转整个字符串后得到:"elpmaxe doog a" -> 但是当我们输出时,它显示为:第一个单词是"elpmaxe",第二个单词是"doog",第三个是"a"。但实际上我们希望的是"example good a"。 问题出在哪里?我们反转每个单词时,把每个单词都反转了,然后整个字符串反转后,每个单词又反转了一次,所以每个单词应该变回原来的顺序?不对,我们期望的是单词顺序反转,但每个单词内部顺序不变。 但是我们的步骤: 原始: "a good example" 反转每个单词:得到 "a doog elpmaxe" -> 每个单词内部顺序被反转了 再反转整个字符串:得到 "elpmaxe doog a" -> 这个字符串中,每个单词内部顺序还是反的(如"elpmaxe"而不是"example") 所以,我们的方法错误! 正确的方法应该是: 方法一(之前提到过): 1. 移除多余空格。 2. 反转整个字符串。 3. 反转每个单词。 这样: 原始: "a good example" -> 移除空格后: "a good example" 反转整个字符串: "elpmaxe doog a" 然后反转每个单词:反转"elpmaxe" -> "example",反转"doog"->"good",反转"a"->"a",得到 "example good a" 所以,我们调整步骤: 步骤1:移除多余空格(同上)。 步骤2:反转整个字符串。 步骤3:反转每个单词。 修改代码: void reverseWords(string &s) { // 步骤1:移除多余空格(同上) // 步骤2:反转整个字符串 reverseSubstr(s, 0, s.size()-1); // 步骤3:反转每个单词 int start = 0; int n = s.size(); for (int i = 0; i <= n; i++) { if (i == n || s[i]==' ') { reverseSubstr(s, start, i-1); start = i+1; } } } 测试:输入"a good example" 步骤1:移除多余空格 -> "a good example"(假设已经移除多余空格,这里中间多余空格被移除,但原字符串中间只有一个空格?原字符串是"a good example",我们移除后变成"a good example",中间只有一个空格) 步骤2:反转整个字符串 -> "elpmaxe doog a" 步骤3:反转每个单词: i=0: 到第一个空格(在位置1)?不对,单词"elpmaxe"从0到6(长度为7)?注意,反转整个字符串后,字符串为:"elpmaxe doog a" 我们需要识别单词:当i=0时,直到i=7(空格)?但字符串是:'e','l','p','m','a','x','e',' ','d','o','o','g',' ','a' 所以,第一个单词从0到6(索引),然后空格在索引7。 反转第一个单词:0到6 -> "example" 然后下一个单词从8到11('d','o','o','g')-> 反转后"good" 然后下一个单词从13到13(只有一个字符)-> 反转后还是"a" 得到 "example good a" 注意:在反转整个字符串后,单词之间仍然只有一个空格,所以步骤3中我们仍然以空格为分隔。 因此,我们修改代码为: 步骤1:移除多余空格(同上) 步骤2:反转整个字符串 步骤3:反转每个单词(同上,但此时字符串是反转整个字符串后的字符串) 重新测试只有一个单词的情况:"hello" 步骤1:移除空格后为"hello" 步骤2:反转整个字符串->"olleh" 步骤3:反转每个单词:当i=5(等于n)时,反转[0,4] -> "hello" 所以输出"hello",正确。 测试两个单词:"hello world" 步骤1:移除空格后->"hello world" 步骤2:反转整个字符串->"dlrow olleh" 步骤3:反转每个单词:第一个单词[0,4] -> "world"(反转"dlrow"->"world"),第二个单词[6,10] -> "hello"(反转"olleh"->"hello"),得到"world hello" 但是,注意:反转整个字符串后,字符串是"dlrow olleh",然后我们反转每个单词: 第一个单词:从0到4(因为索引5是空格,所以第一个单词是"dlrow",反转后变成"world") 第二个单词:从6到10("olleh"反转后"hello") 所以整个字符串变成"world hello" 注意:在反转整个字符串后,单词之间空格的位置?在反转整个字符串后,空格也被反转了,但空格本身位置不变?实际上,反转整个字符串后,空格的位置会变到字符串的什么位置? 原始: "hello world" -> 反转整个字符串: "dlrow olleh" 原来空格在索引5(0-based),反转后整个字符串长度为11,空格应该在索引5(因为对称),但这里: 原始: [0:h,1:e,2:l,3:l,4:o,5: ,6:w,7:o,8:r,9:l,10:d] 反转整个: [0:d,1:l,2:r,3:o,4:w,5: ,6:o,7:l,8:l,9:e,10:h] 所以空格在索引5,所以我们在步骤3中,当i=5时,反转第一个单词(0到4)-> "dlrow"反转成"world",然后设置start=6,然后i=11时,反转6到10->"olleh"反转成"hello"。 因此,代码正确。 修改后的完整代码: */ #include <iostream> #include <string> #include <algorithm> using namespace std; class Solution { public: void reverseWords(string &s) { // 步骤1:移除多余空格 int n = s.size(); int slow = 0, fast = 0; // 跳过开头空格 while (fast < n && s[fast] == ' ') { fast++; } while (fast < n) { // 复制一个单词 while (fast < n && s[fast] != ' ') { s[slow] = s[fast]; slow++; fast++; } // 跳过单词后面的空格 while (fast < n && s[fast] == ' ') { fast++; } // 如果后面还有非空格字符,则添加一个空格 if (fast < n) { s[slow] = ' '; slow++; } } // 现在slow是新字符串的长度 s.resize(slow); n = slow; // 更新n // 步骤2:反转整个字符串 reverseSubstr(s, 0, n - 1); // 步骤3:反转每个单词 int start = 0; for (int i = 0; i <= n; i++) { // 当遇到空格或字符串结尾时,反转上一个单词 if (i == n || s[i] == ' ') { reverseSubstr(s, start, i - 1); start = i + 1; } } } private: void reverseSubstr(string &s, int left, int right) { while (left < right) { swap(s[left], s[right]); left++; right--; } } }; // 测试代码不变 /* 再次测试: 输入:s = " hello world " 步骤1:移除多余空格:得到"hello world",长度为11。 步骤2:反转整个字符串:得到 "dlrow olleh" 步骤3:反转每个单词: i=0~10: 当i=5(空格)时,反转[0,4] -> "dlrow"反转成"world",字符串变为"world olleh" 然后start=6,i=11(等于n)时,反转[6,10] -> "olleh"反转成"hello",字符串变为"world hello" 输出:"world hello",正确。 输入:"a good example" 步骤1:移除多余空格:得到"a good example",注意原字符串中"good"和"example"之间有多个空格,我们只保留一个,所以是"a good example"(长度为13:a,空格,g,o,o,d,空格,e,x,a,m,p,l,e) 步骤2:反转整个字符串:得到 "elpmaxe doog a" 步骤3:反转每个单词: i=0: 直到i=1(空格)?不对,第一个单词是"elpmaxe",它在反转整个字符串后是连续的,直到下一个空格(在索引7?不对,我们数一下:原始"a good example"反转整个字符串后: 原始:0:a, 1:空格, 2:g,3:o,4:o,5:d,6:空格,7:e,8:x,9:a,10:m,11:p,12:l,13:e -> 反转整个后:从后往前:e,l,p,m,a,x,e,空格,d,o,o,g,空格,a 所以:索引0:e,1:l,2:p,3:m,4:a,5:x,6:e,7:空格,8:d,9:o,10:o,11:g,12:空格,13:a -> 但我们的字符串长度是13(0~12)?不对,移除空格后字符串长度是13吗? 原字符串"a good example":字符数:a(1)+空格(1)+good(4)+空格(1)+example(7) = 1+1+4+1+7=14个字符?不对,字符串长度是:a(1),空格(1),g(1),o(1),o(1),d(1),空格(1),e(1),x(1),a(1),m(1),p(1),l(1),e(1) -> 14个字符(索引0~13)。 移除多余空格后,我们得到:原字符串"a good example" -> 移除后是"a good example",长度是14?不对,因为原字符串中"good"和"example"之间有多个空格,我们移除了多余的空格,所以中间只有一个空格,因此总长度是:1(a)+1(空格)+4(good)+1(空格)+7(example)=14?不对,1+1+4+1+7=14,但注意我们移除了两个空格(原字符串中三个空格变成1个,移除了2个空格),所以新字符串长度应该是原长度-2,即14-2=12?不对,原字符串长度是14(包括两个空格?不对,原字符串是"a good example":a,空格,g,o,o,d,空格,空格,空格,e,x,a,m,p,l,e -> 总字符数:a(1),空格(1),g(1),o(1),o(1),d(1),空格(1),空格(1),空格(1),e(1),x(1),a(1),m(1),p(1),l(1),e(1) -> 16个字符?不对,我们写一下: "a good example" -> a:1 空格:1 g,o,o,d:4 空格:3(连续三个) e,x,a,m,p,l,e:7 总共:1+1+4+3+7=16 移除多余空格后:a,空格,good,空格,example -> 1+1+4+1+7=14 所以移除后字符串长度14(索引0~13)。 反转整个字符串:从0~13反转:从后往前:e,l,p,m,a,x,e,空格,d,o,o,g,空格,a -> 所以字符串变为:"elpmaxe doog a"(注意,这里最后是空格a,但实际是a在最后?不对,反转整个字符串后,最后一个字符是原字符串的第一个字符'a',所以整个字符串反转后: 原:0:a,1: ,2:g,3:o,4:o,5:d,6: ,7:e,8:x,9:a,10:m,11:p,12:l,13:e 反转后:0:e,1:l,2:p,3:m,4:a,5:x,6:e,7: ,8:d,9:o,10:o,11:g,12: ,13:a 步骤3:反转每个单词: 第一个单词:从0到6(因为索引7是空格),反转[0,6]:子串"elpmaxe"反转成"example" 第二个单词:从8到11(因为索引12是空格),反转[8,11]:子串"doog"反转成"good" 第三个单词:从13到13(只有一个字符'a',反转后还是'a') 所以得到:"example" + 空格 + "good" + 空格 + "a" -> 但这不是我们想要的,我们想要"example good a",但这里第三个单词是"a",而原来第一个单词是"a",现在变成了最后一个单词,但顺序是"example good a",正确。 但是,我们期望输出是"example good a",所以正确。 因此,我们使用第二种步骤。 注意:在反转整个字符串后,原来的空格位置会变成新字符串中的空格位置,所以我们在反转单词时,仍然可以用空格作为分隔。 代码修改后,重新运行测试。 最后,我们考虑空字符串和全空格字符串: 全空格字符串:如" ",在步骤1中,fast会跳过开头空格,然后fast等于n,所以slow=0,然后resize(0)后,字符串为空。然后步骤2:反转整个字符串(n=0,不操作),步骤3:反转每个单词(循环不会执行任何反转操作)。所以返回空字符串,正确。 因此,代码完成。 注意:我们使用了标准库的swap,也可以自己写交换函数。 我们将代码整理并测试。 测试用例: 1. " hello world " -> "world hello" 2. "a good example" -> "example good a" 3. " " -> "" 4. "hello" -> "hello" 5. " one " -> "one" 测试第五个例子:" one " 步骤1:移除空格:得到"one"(长度为3) 步骤2:反转整个字符串->"eno" 步骤3:反转每个单词:当i=3时,反转[0,2]->"one" 输出:"one" 但是,我们希望反转单词顺序,只有一个单词,所以输出"one",正确。 因此,我们输出完整代码。 注意:由于题目要求用C++,我们使用std::string。 另外,我们也可以使用标准库的reverse,但为了展示原理,我们自己实现了reverseSubstr。 最终代码: */ #include <iostream> #include <string> using namespace std; class Solution { public: void reverseWords(string &s) { // 移除多余空格 int n = s.size(); int slow = 0, fast = 0; // 跳过开头空格 while (fast < n && s[fast] == ' ') { fast++; } while (fast < n) { // 复制一个单词 while (fast < n && s[fast] != ' ') { s[slow++] = s[fast++]; } // 跳过单词后面的空格(连续空格) while (fast < n && s[fast] == ' ') { fast++; } // 如果后面还有单词,则添加一个空格 if (fast < n) { s[slow++] = ' '; } } // 调整字符串大小 s.resize(slow); n = slow; // 更新n // 反转整个字符串 reverseSubstr(s, 0, n - 1); // 反转每个单词 int start = 0; for (int i = 0; i <= n; i++) { if (i == n || s[i] == ' ') { reverseSubstr(s, start, i - 1); start = i + 1; } } } private: void reverseSubstr(string &s, int left, int right) { while (left < right) { char temp = s[left]; s[left] = s[right]; s[right] = temp; left++; right--; } } }; // 测试主函数 int main() { Solution sol; string s; s = " hello world "; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望: "world hello" s = "a good example"; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望: "example good a" s = " "; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望: "" s = "hello"; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望: "hello" s = " one "; sol.reverseWords(s); cout << "\"" << s << "\"" << endl; // 期望: "one" return 0; } /* 运行测试,应该通过。 注意:在移除空格的部分,我们确保了单词之间只有一个空格,且开头结尾无空格。 因此,我们完成了程序。 */</think>以下是一个用C++编写的程序,用于反转字符串中的单词顺序并移除多余空格: ```cpp #include <iostream> #include <string> using namespace std; class Solution { public: void reverseWords(string &s) { // 1. 移除多余空格 int n = s.size(); int slow = 0, fast = 0; // 跳过开头空格 while (fast < n && s[fast] == ' ') fast++; while (fast < n) { // 复制单词字符 while (fast < n && s[fast] != ' ') { s[slow++] = s[fast++]; } // 跳过连续空格 while (fast < n && s[fast] == ' ') fast++; // 添加单词间单空格(如果还有单词) if (fast < n) { s[slow++] = ' '; } } s.resize(slow); // 调整字符串大小 n = slow; // 更新长度 // 2. 反转整个字符串 reverseRange(s, 0, n - 1); // 3. 反转每个单词 int start = 0; for (int i = 0; i <= n; i++) { if (i == n || s[i] == ' ') { reverseRange(s, start, i - 1); start = i + 1; } } } private: // 反转子串 [left, right] void reverseRange(string &s, int left, int right) { while (left < right) { swap(s[left], s[right]); left++; right--; } } }; // 测试用例 int main() { Solution sol; string test; test = " hello world "; sol.reverseWords(test); cout << "测试1: \"" << test << "\"" << endl; // 输出: "world hello" test = "the sky is blue"; sol.reverseWords(test); cout << "测试2: \"" << test << "\"" << endl; // 输出: "blue is sky the" test = " "; sol.reverseWords(test); cout << "测试3: \"" << test << "\"" << endl; // 输出: "" test = "single"; sol.reverseWords(test); cout << "测试4: \"" << test << "\"" << endl; // 输出: "single" test = "a b c d"; sol.reverseWords(test); cout << "测试5: \"" << test << "\"" << endl; // 输出: "d c b a" return 0; } ``` ### 程序说明 1. **移除多余空格**: - 使用双指针技巧(`slow`和`fast`): - `fast`指针跳过开头空格和连续空格 - 只保留单词间单个空格 - 最后调整字符串大小 2. **反转整个字符串**: - 通过`reverseRange()`函数实现整个字符串的反转 - 例如`"hello world"`变为`"dlrow olleh"` 3. **反转每个单词**: - 遍历字符串识别单词边界(空格或结尾) - 对每个单词单独反转 - 例如`"dlrow olleh"`变为`"world hello"` ### 时间复杂度 - **$O(n)$时间复杂度**:所有操作(移除空格、整体反转、单词反转)均只需单次遍历 - **$O(1)$空间复杂度**:原地修改字符串,无需额外空间 ### 测试用例 | 输入 | 输出 | 说明 | |------|------|------| | `" hello world "` | `"world hello"` | 处理首尾/中间多余空格 | | `"the sky is blue"` | `"blue is sky the"` | 多单词反转 | | `" "` | `""` | 全空格返回空 | | `"single"` | `"single"` | 单单词不变 | | `"a b c d"` | `"d c b a"` | 短单词处理 | ### 相关问题 1. 如何修改程序以保留标点符号的位置(如`"hello, world!"`变为`"world! hello,"`)? 2. 如果要求不使用额外空间(包括`swap`函数),如何优化反转操作? 3. 如何扩展程序以处理制表符`\t`等其他空白字符? 4. 在内存受限环境中,如何优化该算法以适应超大字符串? 5. 如何修改程序使其支持双向迭代器(如链表存储的字符串)? > 引用说明:本实现参考了字符串处理中的双指针技巧和分段反转思想,这是处理原地字符串操作的常用范式[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值