news 2026/10/16 1:26:32

PTA 7-1 统计单词数量并输出最后一个字符:vector C++ 实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PTA 7-1 统计单词数量并输出最后一个字符:vector C++ 实战解析

1. 这道题到底在考什么

1.1 从标题拆解真实需求

“PTA 7-1 统计单词的数量并输出单词的最后一个字符”,这个标题信息量其实挺大。拆开来看,它至少包含三个独立的任务:第一,统计一段文本里有多少个单词;第二,把每个单词的最后一个字符取出来;第三,按题目要求的格式输出。而标题后半句“就是非得用vector c++”才是真正的情绪出口——很多同学看到这道题的第一反应是“我用stringstream不就搞定了吗”,结果题目或者老师偏偏要求用vector来做,于是就有了这句吐槽。

我完全理解这种感受。PTA上的题目有时候确实会给人一种“为了考某个知识点而强行设计”的感觉。但换个角度想,这道题其实是一个非常好的vector容器练习场景,因为它同时涉及了字符串分割、容器存储、遍历输出三个环节,而vector恰好是串联这三个环节最自然的工具。

这道题适合谁看?如果你是刚学完C++基础语法、正在刷PTA基础题目的同学,或者你对vector的用法还停留在“知道有这么个东西但不知道怎么用”的阶段,那这篇内容应该能帮你把这道题彻底吃透。我会从思路设计讲到代码实现,再讲到实际踩过的坑,尽量把每个细节都说清楚。

1.2 为什么“非得用vector”

先说说为什么这道题用vector是合理的,而不是纯粹为了刁难人。

统计单词数量这件事,本质上是在做分词。分词的结果是一组单词,这组单词的数量在读取之前是未知的,而且需要保存下来供后续使用(因为还要输出每个单词的最后一个字符)。这种“数量未知、需要动态增长、需要随机访问”的场景,正是vector最擅长的。

对比一下其他方案:用固定大小的数组,你得预估一个最大单词数,估小了会越界,估大了浪费空间;用list或deque,虽然也能动态增长,但随机访问效率不如vector,而且对于这道题来说没必要用链式结构;用stringstream配合>>运算符确实可以逐个读取单词,但如果不把单词存下来,你就没法在统计数量的同时输出最后一个字符——除非你读两遍,但那又需要重新处理输入流,反而更麻烦。

所以vector在这里的角色是:一个动态的单词仓库。先把所有单词收进来,然后仓库里有多少个单词就是答案,再遍历仓库取出每个单词的尾巴字符。逻辑非常顺。

提示:PTA的题目描述有时候不会明确说“必须用vector”,但如果你在提交时发现某个测试点过不了,或者老师明确要求用vector,那就老老实实用vector。这不是妥协,这是练习容器思维的好机会。

2. vector方案的核心思路与代码骨架

2.1 整体流程设计

拿到这道题,我习惯先在纸上把流程画出来,不写代码。流程大概是这样的:

  1. 读入一行字符串(可能包含空格,所以不能用cin >> string直接读,得用getline)。
  2. 遍历这个字符串,按照空格(或题目指定的分隔符)把单词切出来。
  3. 每切出一个单词,就把它push_back进一个vector<string>。
  4. 遍历这个vector,输出每个单词的最后一个字符。
  5. 最后输出单词的总数。

这里有一个关键决策点:怎么切分单词。常见的有两种做法,一种是手动遍历字符,遇到空格就认为一个单词结束;另一种是用stringstream配合>>自动跳过空白字符。两种方法各有优劣,我后面会详细对比。

另一个决策点是:输出格式。题目说“输出单词的最后一个字符”,但没说是一个一个输出还是连在一起输出,也没说单词之间要不要加空格。这种细节必须看题目的样例输入输出,不能凭感觉。我见过太多同学因为输出格式差一个空格而反复提交不过,最后发现是格式问题。

2.2 手动遍历切分 vs stringstream切分

先看手动遍历的写法。核心逻辑是维护一个string变量word,遍历输入字符串的每个字符,如果当前字符不是空格,就把它追加到word后面;如果当前字符是空格,并且word不为空,就把word存进vector,然后清空word。遍历结束后,如果word还不为空,说明最后一个单词还没存进去,要补上。

这种写法的好处是完全可控,你可以自己决定什么算分隔符(比如空格、制表符、逗号等),也可以处理连续多个空格的情况。坏处是代码稍微长一点,需要小心处理边界条件。

再看stringstream的写法。把输入字符串塞进一个stringstream对象,然后用>>运算符逐个读取单词,每读一个就push_back进vector。>>运算符会自动跳过空白字符(空格、制表符、换行符),所以不需要手动处理连续空格的问题。代码非常简洁,大概三四行就能完成切分。

那为什么还有人不用stringstream呢?我总结下来有几个原因:一是有些老师不允许用stringstream,觉得太“取巧”;二是有些题目对分隔符有特殊要求,比如只以空格分隔,逗号不算分隔符,这时候stringstream的默认行为可能不符合要求;三是有些同学对stringstream的底层机制不熟悉,用起来心里没底。

我的建议是:两种都要会。平时练习的时候用手动遍历,把基本功练扎实;实际做题的时候如果允许用stringstream,那就用,省时省力。但不管用哪种,都要理解背后的原理。

2.3 代码骨架与关键注释

下面给出一版基于手动遍历的代码骨架,关键地方我都加了注释:

#include <iostream> #include <vector> #include <string> using namespace std; int main() { string line; getline(cin, line); // 读入整行,保留空格 vector<string> words; // 单词仓库 string word; // 临时存放当前正在拼装的单词 for (char ch : line) { if (ch != ' ') { word += ch; // 非空格字符,追加到当前单词 } else { if (!word.empty()) { words.push_back(word); // 遇到空格且单词非空,存入仓库 word.clear(); // 清空临时单词,准备下一个 } // 如果word为空,说明是连续空格,直接跳过 } } // 循环结束后,最后一个单词可能还在word里 if (!word.empty()) { words.push_back(word); } // 输出每个单词的最后一个字符 for (const string& w : words) { cout << w.back(); // back()返回最后一个字符的引用 } cout << endl; // 输出单词总数 cout << words.size() << endl; return 0; }

这段代码里有两个地方容易出错。第一个是getline之前如果有cin >>操作,缓冲区里会残留一个换行符,导致getline读到空行。这道题如果只有一行输入,一般不会遇到这个问题,但如果你在本地测试时发现程序直接跳过了输入,那大概率就是这个原因。解决办法是在getline之前加一个cin.ignore()。

第二个是w.back()的使用。back()是C++11引入的成员函数,返回最后一个字符的引用。如果单词为空,调用back()是未定义行为。不过在我们的逻辑里,存进vector的单词都是非空的,所以安全。如果你不放心,可以用w[w.size() - 1]代替,效果一样,但要注意size()返回的是size_t类型,减法操作要小心下溢。

3. 实操过程中的关键细节与避坑指南

3.1 输入读取的坑:getline与cin混用

这道题最经典的坑就是输入读取。很多同学写代码的时候,习惯性地用cin >> line来读字符串,结果发现遇到空格就断了,只能读到第一个单词。这是因为cin >>以空白字符为分隔符,读到空格就停止。所以读整行必须用getline(cin, line)。

但getline也有自己的脾气。如果之前用了cin >>读取其他数据(比如题目如果有多个测试用例,先读一个整数n),那么cin >>会在缓冲区里留下一个换行符,getline一上来就读到这个换行符,直接返回空字符串。这时候需要在cin >>之后、getline之前加一句cin.ignore(),把那个换行符吃掉。

这道题如果只有一行输入,通常不会遇到这个问题。但PTA的题目有时候会有多组测试数据,或者输入格式比较复杂,所以养成好习惯:只要getline前面有cin >>,就加一句cin.ignore()。不加可能没事,加了肯定没事。

还有一个细节:getline读到换行符会停止,并且把换行符从缓冲区里拿走(但不存进字符串)。所以如果你连续调用两次getline,第一次读到换行符结束,第二次会从下一行开始读。这个行为在多数情况下是符合预期的,但如果你需要保留空行,就要注意了。

3.2 单词切分的边界情况

手动遍历切分单词的时候,有几个边界情况必须考虑:

连续多个空格。比如输入是"hello world",中间有三个空格。按照我们的逻辑,第一个空格时word是"hello",存进去并清空;第二个空格时word是空的,跳过;第三个空格时word还是空的,跳过。所以最终只存了"hello"和"world"两个单词,正确。

开头有空格。比如输入是" hello world",开头两个空格。第一个空格时word为空,跳过;第二个空格时word还是空,跳过;然后读到h开始拼装"hello"。最终结果正确。

结尾有空格。比如输入是"hello world ",结尾两个空格。读到最后一个空格时,word是"world",存进去并清空;然后循环结束,word为空,不会重复存入。结果正确。

空行。如果输入就是一个空行,那么循环一次都不执行,word始终为空,vector也是空的。输出就是空行加一个0。这种情况题目一般不会考,但你的代码应该能正确处理,不崩溃。

只有空格的行。和空行类似,循环会执行,但每次word都是空的,不会存入任何单词。结果也是空vector。

这些边界情况看起来简单,但实际写代码的时候很容易漏掉某一个。我的习惯是:写完切分逻辑后,用几个极端输入测试一下,比如空字符串、全是空格、单词之间多个空格、开头结尾都有空格。这几个测试过了,基本就没问题了。

3.3 输出格式的确认

输出格式是PTA题目最容易丢分的地方。这道题说“输出单词的最后一个字符”,但具体怎么输出,必须看样例。我见过几种可能的格式:

第一种,每个单词的最后一个字符连在一起输出,比如"hello world"输出"od",然后换行输出单词数2。

第二种,每个单词的最后一个字符单独一行输出,最后再输出单词数。

第三种,输出成"最后一个字符1 最后一个字符2 ... 单词数"这样的格式。

不同的格式对应不同的代码写法。如果是第一种,直接遍历vector输出w.back()即可,不需要加空格或换行。如果是第二种,每个字符后面要加endl。如果是第三种,字符之间要加空格,最后再输出数量。

我的建议是:先把样例输入输出抄下来,手动模拟一遍你的代码,看看输出是否和样例完全一致。包括空格、换行、标点符号,一个都不能差。PTA的评测是严格字符串匹配,多一个空格都算错。

注意:有些题目的输出要求是“每个单词的最后一个字符占一行”,但样例里可能只给了一个单词,看不出换行规律。这时候要看题目描述的文字,或者去讨论区看看别人的经验。如果实在不确定,就按最直观的方式理解,然后提交试试,根据反馈调整。

3.4 vector的遍历方式选择

遍历vector输出最后一个字符,有几种写法:

第一种,用下标遍历:

for (size_t i = 0; i < words.size(); i++) { cout << words[i].back(); }

第二种,用迭代器:

for (auto it = words.begin(); it != words.end(); ++it) { cout << it->back(); }

第三种,用范围for循环(C++11):

for (const string& w : words) { cout << w.back(); }

三种写法功能上完全等价,但风格不同。下标遍历最直观,适合初学者;迭代器遍历是STL的标准用法,适合需要兼容老版本C++的场景;范围for循环最简洁,但需要C++11支持。PTA的编译器一般都支持C++11甚至更高版本,所以范围for循环是可以用的。

我个人的偏好是范围for循环,因为代码短、可读性好。但如果你在团队协作或者考试中,不确定编译器版本,用下标遍历最保险。另外,范围for循环里的const string&很重要,如果不加引用,每次迭代都会拷贝一个string对象,虽然对于这道题来说单词数量不多,拷贝开销可以忽略,但养成加引用的习惯总是好的。

4. 常见问题与排查技巧实录

4.1 提交后提示“答案错误”怎么办

“答案错误”(Wrong Answer)是PTA上最常见的错误类型,原因可能有很多。针对这道题,我整理了一个排查清单:

可能原因排查方法解决方法
输出格式不对对比样例输出,检查空格、换行严格按照样例格式调整
单词切分逻辑有误用边界输入测试检查连续空格、首尾空格的处理
最后一个单词丢失检查循环结束后是否补存在循环后加if (!word.empty())判断
单词数量统计错误输出vector大小验证确认push_back次数是否正确
输入读取不完整检查是否用了getline改用getline读整行

我印象比较深的一次是,有个同学提交后一直提示答案错误,他检查了好几遍逻辑都觉得没问题。后来我让他把输出内容打印出来看,发现他在输出最后一个字符时多打了一个空格。题目要求是连在一起输出,他习惯性地在每个字符后面加了空格。这种问题只能通过仔细对比样例来发现。

4.2 运行时错误(RE)的常见原因

运行时错误(Runtime Error)通常是因为程序崩溃了。这道题可能的原因有:

数组越界。如果你用了固定大小的数组而不是vector,单词数量超过数组容量就会越界。用vector的话一般不会越界,但如果你用words[i]访问时i超出了范围,也会崩溃。比如你想输出最后一个单词的最后一个字符,写成了words[words.size()].back(),这就是越界。

空vector访问。如果输入是空行,vector是空的,你调用words.back()或words[0]就会崩溃。虽然这道题可能不会给空输入,但保险起见,可以在访问前加一个判断:if (!words.empty())。

string的back()在空串上调用。前面说过,如果单词为空,调用back()是未定义行为。在我们的逻辑里不会出现空单词,但如果你在切分时不小心把空字符串存进去了,就会出问题。检查方法是在push_back之前加一个if (!word.empty())判断。

4.3 超时(TLE)的可能性

这道题的数据量一般不会太大,超时的可能性很低。但如果你用了非常低效的写法,比如在循环里反复调用strlen或者反复拷贝大字符串,可能会超时。用vector和string的标准操作,效率是足够的。

不过有一个细节值得注意:如果你在范围for循环里写的是for (string w : words)而不是for (const string& w : words),每次迭代都会拷贝一个string对象。对于这道题来说,单词数量可能就几十个,拷贝开销可以忽略。但如果单词数量上万,每个单词又很长,拷贝开销就会累积。所以养成加引用的习惯,不仅是为了效率,也是为了代码规范。

4.4 独家避坑技巧

分享几个我在刷PTA过程中总结的小技巧:

技巧一:本地测试时用文件输入。每次手动输入测试数据很麻烦,可以把测试数据写在一个input.txt文件里,然后用freopen("input.txt", "r", stdin)重定向输入。这样调试的时候就不用反复敲键盘了。不过提交之前记得把freopen注释掉,否则会读文件而不是标准输入。

技巧二:输出调试信息时用cerr。cout是标准输出,会被PTA评测;cerr是标准错误,不会被评测。所以调试的时候可以用cerr打印中间变量,提交时不用删掉也不会影响结果。当然,为了代码整洁,提交前最好还是清理一下。

技巧三:善用PTA的“自定义测试”功能。PTA允许你输入自定义的测试数据,然后看程序的输出。这个功能非常适合用来验证边界情况。你可以输入一个空行、一行空格、多个连续空格等,看看程序是否按预期工作。

技巧四:保存一份可复用的代码模板。这道题的切分逻辑在很多字符串处理题目里都会用到。你可以把“读整行、切分单词、存入vector”这部分代码保存成一个模板,以后遇到类似的题目直接复制过来改改就行。这样能节省不少时间。

5. 从这道题延伸出的vector使用心得

5.1 vector的常见操作速查

既然这道题的核心是vector,我顺便整理一下vector在刷题中最常用的操作,方便你以后查阅:

操作代码示例说明
定义vector<string> words;定义一个存储string的vector
添加元素words.push_back(word);在末尾添加一个元素
获取大小words.size();返回元素个数,类型是size_t
访问元素words[i];下标访问,不检查越界
访问首元素words.front();返回第一个元素的引用
访问尾元素words.back();返回最后一个元素的引用
判断为空words.empty();为空返回true
清空words.clear();删除所有元素
遍历for (auto& w : words)范围for循环

这些操作覆盖了刷题中90%以上的vector使用场景。剩下的比如insert、erase、resize等,在基础题目里用得比较少,但了解一下也没坏处。

5.2 什么时候该用vector,什么时候不该用

vector虽然好用,但也不是万能的。我总结了几条经验:

该用vector的场景:元素数量未知、需要动态增长、需要随机访问、需要频繁在末尾添加元素。这道题就是典型的该用vector的场景。

不该用vector的场景:元素数量固定且较小(用数组更简单)、需要频繁在中间插入或删除(用list更合适)、需要频繁在头部插入(用deque更合适)、需要按键值查找(用map或unordered_map更合适)。

举个例子,如果你要统计一段文本中每个单词出现的次数,那用map<string, int>比vector更合适,因为你需要按键查找。但如果你只是把单词存起来然后遍历,那vector就是最佳选择。

5.3 从“会用”到“用得好”

很多同学学vector的时候,只记住了push_back和size,觉得这就是全部了。其实vector的很多细节值得深挖:

容量与大小的区别。size()返回当前元素个数,capacity()返回当前分配的存储空间能容纳多少个元素。当你push_back时,如果size等于capacity,vector会重新分配一块更大的内存,把原有元素拷贝过去,然后释放旧内存。这个过程叫做“扩容”。扩容会导致迭代器失效,所以如果你在遍历时push_back,要小心迭代器失效的问题。

预留空间。如果你提前知道大概要存多少个元素,可以用reserve(n)预留空间,避免多次扩容。比如你知道最多有1000个单词,就可以写words.reserve(1000)。这样vector一开始就分配足够的内存,后续push_back不会触发扩容,效率更高。

emplace_back vs push_back。C++11引入了emplace_back,它可以直接在容器末尾构造对象,避免了临时对象的拷贝。对于string来说,push_back(word)会拷贝一份word,而emplace_back(word)会直接在末尾构造,少一次拷贝。不过对于这道题来说,两种写法差别不大,用哪个都行。

迭代器失效。前面提到,扩容会导致迭代器失效。除此之外,insert和erase也可能导致迭代器失效。所以如果你在遍历vector的过程中要修改它,一定要小心。安全的做法是先用一个临时vector存结果,遍历完再替换。

这些细节在基础题目里可能用不到,但当你做更复杂的题目时,就会体现出差距。我建议你在刷题之余,花点时间看看vector的源码或者文档,理解它的内部实现,这样用起来才更有底气。

6. 完整代码实现与测试验证

6.1 最终版代码

综合前面的分析,我给出一版完整的、经过测试的代码。这版代码用手动遍历切分单词,输出格式假设是“每个单词的最后一个字符连在一起输出,然后换行输出单词数”。如果你的题目格式不同,只需要调整输出部分即可。

#include <iostream> #include <vector> #include <string> using namespace std; int main() { string line; getline(cin, line); vector<string> words; string word; for (char ch : line) { if (ch != ' ') { word += ch; } else { if (!word.empty()) { words.push_back(word); word.clear(); } } } if (!word.empty()) { words.push_back(word); } for (const string& w : words) { cout << w.back(); } cout << endl; cout << words.size() << endl; return 0; }

6.2 测试用例验证

我用几个测试用例验证一下这版代码:

测试用例1:输入hello world,预期输出od和2。实际运行:切分出["hello", "world"],输出o和d连在一起是od,单词数2。正确。

测试用例2:输入hello world(前后和中间都有多个空格),预期输出od和2。实际运行:切分出["hello", "world"],输出od和2。正确。

测试用例3:输入a,预期输出a和1。实际运行:切分出["a"],输出a和1。正确。

测试用例4:输入空行,预期输出空行和0。实际运行:vector为空,输出空行和0。正确。

测试用例5:输入 (全是空格),预期输出空行和0。实际运行:vector为空,输出空行和0。正确。

这几个测试用例覆盖了主要的边界情况,代码表现符合预期。

6.3 如果题目要求用stringstream的版本

如果题目允许用stringstream,代码可以简化成这样:

#include <iostream> #include <vector> #include <string> #include <sstream> using namespace std; int main() { string line; getline(cin, line); vector<string> words; string word; stringstream ss(line); while (ss >> word) { words.push_back(word); } for (const string& w : words) { cout << w.back(); } cout << endl; cout << words.size() << endl; return 0; }

这版代码更短,逻辑也更清晰。ss >> word会自动跳过空白字符,所以不需要手动处理连续空格。但前提是题目允许用stringstream,并且分隔符就是空白字符。如果题目要求只以空格分隔(制表符不算),那stringstream的默认行为可能不符合要求,还是得用手动遍历。

6.4 性能对比与选择建议

两种写法在性能上差别不大。stringstream的>>运算符内部也是逐个字符扫描,跳过空白字符,然后拼装单词。手动遍历也是逐个字符扫描,逻辑类似。对于PTA这种数据量不大的题目,两种写法的运行时间都在毫秒级别,不会成为瓶颈。

选择哪种写法,主要看题目要求和你的熟练程度。如果题目明确要求用vector,两种写法都用了vector,都符合要求。如果题目对分隔符有特殊要求,手动遍历更灵活。如果你赶时间,stringstream更快。我的建议是:平时练习用手动遍历,考试或比赛时用stringstream。这样既能练基本功,又能提高效率。

7. 从这道题看PTA刷题的正确姿势

7.1 不要为了AC而AC

PTA上的题目,很多同学的目标就是“过了就行”,提交看到“答案正确”就关掉页面。但这样刷题,效果其实很有限。一道题AC了,不代表你真的掌握了背后的知识点。下次遇到类似的题目,可能还是不会。

我的建议是:每道题AC之后,花五分钟复盘一下。想想这道题考了什么知识点,你的解法是不是最优的,有没有更简洁的写法,边界情况是否都考虑到了。如果能把这道题的思路讲给别人听,那才算真正掌握了。

7.2 善用讨论区但不依赖

PTA的讨论区是个好东西,遇到不会的题目可以去看看别人的思路。但不要直接抄代码,而是看别人的思路,然后自己实现。抄代码只能解决一时的问题,理解思路才能举一反三。

另外,讨论区里的代码质量参差不齐,有些代码虽然能AC,但写法不规范,甚至有些是“歪门邪道”。看的时候要有辨别能力,不要什么都学。

7.3 建立自己的代码模板库

刷题刷多了,你会发现很多题目的代码结构是相似的。比如字符串处理题目,经常需要读整行、切分单词、遍历输出。这些通用逻辑可以整理成模板,以后遇到类似的题目直接复制过来改改就行。

我自己的模板库里就有好几个常用的模板:字符串切分模板、vector遍历模板、map统计模板、排序比较函数模板等。每次遇到新题目,先看看能不能套用模板,能套用的就直接套,不能套用的再从头写。这样能节省不少时间。

7.4 定期回顾做过的题目

人的记忆是会衰退的。一道题今天AC了,过一个月可能就忘了怎么做了。所以定期回顾很重要。我的做法是:每周花一个小时,把这一周做过的题目重新看一遍,重点看那些当时卡了很久的题目。如果发现某道题现在还是不会做,就重新做一遍。

回顾的时候,不要只看代码,要回想当时的思路:为什么这么写?有没有更好的写法?边界情况是怎么处理的?这样回顾一遍,比做十道新题还有用。

8. 关于vector的进阶思考

8.1 vector的内存管理机制

vector之所以能动态增长,是因为它在内部维护了一块连续的内存。当元素数量超过当前容量时,它会申请一块更大的内存(通常是当前容量的两倍),把原有元素拷贝过去,然后释放旧内存。这个过程叫做“扩容”。

扩容是有代价的:申请内存、拷贝元素、释放内存都需要时间。所以如果你知道大概要存多少个元素,最好用reserve提前预留空间,避免多次扩容。比如你知道最多有1000个单词,就写words.reserve(1000)。

另外,扩容会导致所有指向vector元素的指针、引用、迭代器失效。所以如果你在遍历vector的过程中push_back,要特别小心。安全的做法是先用一个临时vector存结果,遍历完再替换。

8.2 vector与数组的取舍

vector和数组都能存储一组元素,但适用场景不同。数组的大小是固定的,定义时必须指定长度,而且不能动态增长。vector的大小是动态的,可以随时添加或删除元素。

在刷题中,如果元素数量固定且较小(比如存储10个整数),用数组更简单。如果元素数量未知或可能很大,用vector更合适。另外,vector支持赋值、拷贝、比较等操作,数组不支持。所以如果你需要这些操作,也得用vector。

这道题用vector是合适的,因为单词数量未知,而且需要动态增长。如果用数组,你得预估一个最大单词数,估小了会越界,估大了浪费空间。

8.3 vector的迭代器与范围for循环

vector的迭代器是随机访问迭代器,支持+、-、++、--、[]等操作。范围for循环底层就是通过迭代器实现的,所以用范围for循环遍历vector和用迭代器遍历是等价的。

范围for循环的写法更简洁,但有一个限制:它不能直接获取下标。如果你需要下标,还是得用传统的下标遍历或迭代器遍历。另外,范围for循环里的变量类型要注意,用auto&或const auto&可以避免拷贝,用auto会拷贝一份。

对于这道题来说,遍历vector只是为了输出最后一个字符,不需要下标,所以范围for循环是最合适的。

8.4 从vector到更复杂的容器

学会了vector之后,你可以进一步学习其他STL容器,比如list、deque、map、set、unordered_map等。每种容器都有自己的适用场景,理解它们的底层实现和性能特点,能帮助你在刷题时做出更好的选择。

比如,如果你需要频繁在中间插入或删除元素,list比vector更合适;如果你需要频繁在头部插入,deque比vector更合适;如果你需要按键查找,map或unordered_map比vector更合适。这些容器在PTA的进阶题目里经常出现,提前掌握能让你刷题更轻松。

我个人在实际刷题中的体会是,vector是使用频率最高的容器,大概能覆盖60%以上的场景。剩下的场景里,map和set各占15%左右,string相关的操作占10%左右。所以先把vector练熟,再逐步扩展其他容器,是比较合理的学习路径。

最后再分享一个小技巧:如果你在PTA上遇到一道题,不确定该用什么容器,可以先想想数据的操作特点——是频繁在末尾添加,还是频繁查找,还是频繁在中间插入?根据操作特点选容器,一般不会错。如果实在拿不准,先用vector试试,大多数情况下vector都能胜任。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 1:25:50

SSD生命周期预测实战:从SMART属性到RUL建模与落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:25:28

多Agent协作的通信契约与分层解耦设计

1. 多 Agent 协作不是“堆人”&#xff0c;而是构建可验证的通信契约多 Agent 协作到底靠什么&#xff1f;这个问题最近在技术圈被反复提起&#xff0c;但多数讨论停留在“用几个 LLM 模型串起来”“加个调度器就叫编排”这种表层认知。我去年带队落地一个面向金融风控场景的 A…

作者头像 李华
网站建设 2026/10/16 1:22:27

C++实战:从零构建轻量级音乐播放器,SDL2与音频解码全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:20:58

Android App签名降级处理实战:从定位到稳定运行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:20:43

推荐系统架构与机器学习基础理论:从零到一的全景拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华