C++ stringstream 实战:5个LeetCode算法题高效输入解析与类型转换
在算法竞赛和技术面试中,输入解析往往是解题的第一步,也是最容易被忽视的环节。许多C++程序员习惯使用传统的cin和getline,但当面对复杂输入格式时,这些方法往往显得力不从心。本文将深入探讨stringstream这一强大工具,通过5个LeetCode典型题目,展示如何利用它高效处理各种棘手的输入场景。
1. stringstream核心机制解析
stringstream是C++标准库<sstream>中提供的字符串流类,它将字符串与流操作完美结合。与文件流(fstream)类似,但操作对象是内存中的字符串而非物理文件。
关键特性对比:
| 特性 | stringstream优势 | 传统方法局限 |
|---|---|---|
| 类型安全 | 自动类型推导,避免格式错误 | scanf需手动匹配格式 |
| 内存管理 | 自动扩容,无缓冲区溢出风险 | 字符数组需预分配固定大小 |
| 操作灵活性 | 支持流式连续操作 | 多次调用解析函数增加复杂度 |
| 错误处理 | 内置状态检测机制 | 需额外检查返回值或异常处理 |
基础用法示例:
#include <sstream> #include <string> // 字符串分割基础实现 void splitString(const std::string& input) { std::stringstream ss(input); std::string token; while (ss >> token) { // 默认按空格分割 std::cout << token << std::endl; } }2. LeetCode典型输入处理场景
2.1 不定长数字列表解析(LeetCode 297. 二叉树的序列化)
二叉树序列化格式示例:[1,2,3,null,null,4,5]。这种混合了符号和数字的输入需要特殊处理。
高效解析方案:
vector<string> deserialize(string data) { vector<string> tokens; stringstream ss(data.substr(1, data.size()-2)); // 去除方括号 string item; while (getline(ss, item, ',')) { // 按逗号分割 tokens.push_back(item); } return tokens; }性能优化点:
- 使用
substr避免临时字符串拷贝 - 流对象复用减少构造开销
- 预分配vector空间避免多次扩容
2.2 混合类型数据行处理(LeetCode 68. 文本左右对齐)
输入可能包含字符串和数字混合的情况,如:["This", "is", "an", "example", 2]
类型安全解析:
void processLine(const string& line) { stringstream ss(line); string word; int num; while (ss >> word) { if (stringstream(word) >> num) { // 尝试转换为数字 handleNumber(num); } else { handleWord(word); } } }2.3 多分隔符复杂字符串(LeetCode 819. 最常见的单词)
输入示例:"Bob hit a ball, the hit BALL flew far after it was hit.",需要处理多种标点符号。
正则表达式替代方案:
string mostCommonWord(string paragraph, vector<string>& banned) { for (auto& c : paragraph) { c = isalpha(c) ? tolower(c) : ' '; // 非字母转为空格 } stringstream ss(paragraph); string word; unordered_map<string, int> freq; while (ss >> word) { if (find(banned.begin(), banned.end(), word) == banned.end()) { freq[word]++; } } return max_element(freq.begin(), freq.end())->first; }3. 高级应用技巧
3.1 流状态控制与错误处理
stringstream提供了完善的状态检测机制:
stringstream ss("123 abc 45.6"); int x; double y; string z; ss >> x; // 成功读取123 if (ss.fail()) { ss.clear(); // 清除错误状态 } ss >> z; // 读取"abc" ss >> y; // 成功读取45.6 cout << "Read values: " << x << ", " << z << ", " << y << endl;状态标志说明:
good():操作成功eof():到达流末尾fail():逻辑错误(如类型不匹配)bad():流损坏(如内存不足)
3.2 高效类型转换模板
封装可复用的类型转换工具:
template<typename T> T convertTo(const string& str) { stringstream ss(str); T value; if (!(ss >> value)) { throw runtime_error("Conversion failed"); } return value; } // 使用示例 int num = convertTo<int>("42"); double pi = convertTo<double>("3.14159");4. 性能优化实践
4.1 对象复用与内存管理
频繁创建stringstream对象会导致性能下降,正确的做法是:
class Parser { stringstream ss; public: template<typename T> T parse(const string& input) { ss.str(input); // 重用现有流对象 ss.clear(); // 清除状态标志 T result; ss >> result; return result; } };4.2 与标准算法结合
配合STL算法实现高效处理:
vector<int> parseNumbers(const string& line) { vector<int> numbers; stringstream ss(line); // 使用istream_iterator简化输入 copy(istream_iterator<int>(ss), istream_iterator<int>(), back_inserter(numbers)); return numbers; }5. 完整解决方案与模板
5.1 通用输入解析模板
class InputParser { stringstream ss; public: explicit InputParser(const string& input) : ss(input) {} // 解析定长数组 template<typename T> vector<T> parseArray(char delim = ',') { vector<T> result; string item; while (getline(ss, item, delim)) { stringstream item_ss(item); T value; if (item_ss >> value) { result.push_back(value); } } return result; } // 解析键值对 template<typename K, typename V> pair<K, V> parsePair(char pair_delim = ':', char item_delim = ',') { string pair_str; getline(ss, pair_str, item_delim); stringstream pair_ss(pair_str); K key; V value; getline(pair_ss, pair_str, pair_delim); stringstream(pair_str) >> key; pair_ss >> value; return {key, value}; } };5.2 LeetCode 151实战(翻转字符串中的单词)
string reverseWords(string s) { stringstream ss(s); string word, result; while (ss >> word) { result = word + " " + result; } if (!result.empty()) { result.pop_back(); // 移除末尾空格 } return result; }在处理算法题目时,stringstream的价值不仅体现在输入解析阶段。它的类型转换能力可以大幅简化代码,例如将数值转换为字符串进行比较或拼接:
string numToStr(int num) { stringstream ss; ss << num; return ss.str(); }相比传统的to_string(),这种方法在需要格式化输出时更为灵活:
string formatTime(int hours, int minutes) { stringstream ss; ss << setw(2) << setfill('0') << hours << ":" << setw(2) << setfill('0') << minutes; return ss.str(); }