1. 求最大值与最小值的三种实用方案
作为C++初学者,在算法题中遇到求极值问题简直就像每天要喝水一样常见。记得我刚刷题时,每次看到"求最大值"的题目都会下意识地写出那个经典的"if嵌套大法"。但随着经验积累,我发现其实有更优雅的解决方案。下面分享三种不同段位的解法,从新手到进阶总有一款适合你。
1.1 基础版:if比较法
cpp复制#include<iostream>
using namespace std;
int main() {
int a, b, c;
cin >> a >> b >> c;
int max = a;
if (b > max) max = b;
if (c > max) max = c;
cout << "最大值是:" << max << endl;
return 0;
}
这是最直观的解法,特别适合刚接触编程的同学理解比较逻辑。它的核心思想是:
- 先假设第一个数就是最大值
- 依次与其他数比较,遇到更大的就更新max值
- 最终max变量存储的就是最大值
注意:这种方法虽然易懂,但当比较的数字增多时,代码会变得冗长。我在实际刷题中发现,超过5个数的比较就会让代码看起来很臃肿。
1.2 进阶版:STL算法库
cpp复制#include <iostream>
#include <algorithm> // 必须包含这个头文件
using namespace std;
int main() {
int a, b, c;
cin >> a >> b >> c;
// 方法一:嵌套调用
int max_val = max(max(a, b), c);
// 方法二(C++11起支持):初始化列表
int max_val = max({a, b, c});
cout << "最大值是:" << max_val << endl;
return 0;
}
STL的max函数才是真正的"职业选手"选择。它有两大优势:
- 代码简洁,一行搞定
- 性能优化好,编译器会做特殊处理
特别说明第二种写法中的max({a,b,c}):
- 大括号{}创建了一个初始化列表
- 这种写法从C++11开始支持
- 可扩展性强,无论多少个参数都能处理
踩坑提醒:有些老旧的OJ系统可能不支持C++11特性,这时候就得用嵌套调用的写法了。我在Codeforces比赛中就遇到过这种情况。
1.3 极简版:三目运算符
cpp复制#include<iostream>
using namespace std;
int main() {
int a, b, c;
cin >> a >> b >> c;
a = a > b ? a : b;
a = a > c ? a : c;
cout << "最大值是:" << a << endl;
return 0;
}
三目运算符? :的妙用:
- 语法:
条件 ? 表达式1 : 表达式2 - 如果条件为真,返回表达式1的值,否则返回表达式2
- 适合对代码长度有严格要求的场景(比如某些编程竞赛)
个人心得:这种方法虽然简洁,但可读性稍差。建议在团队项目中慎用,除非队友都熟悉这种写法。
2. 处理含空格的字符串输入
算法题中经常需要处理字符串输入,而最让人头疼的就是字符串里包含空格的情况。还记得我第一次遇到这种题时,cin和scanf的表现让我百思不得其解——它们居然在空格处就停止读取了!
2.1 问题重现:cin的局限性
cpp复制#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cout << "输入字符串:";
cin >> s; // 输入"I love C++"
cout << "实际读取:" << s; // 只输出"I"
return 0;
}
这是因为:
- cin和scanf默认以空白字符(空格、制表符、换行符)作为分隔符
- 遇到第一个空格就会停止读取
- 剩余内容会留在输入缓冲区中
2.2 解决方案:getline函数
cpp复制#include <iostream>
#include <string>
#include <limits> // 需要这个头文件来使用numeric_limits
using namespace std;
int main() {
// 先读取一个整数
int n;
cout << "输入数字:";
cin >> n;
// 清除缓冲区中的换行符
cin.ignore(numeric_limits<streamsize>::max(), '\n');
// 再读取整行字符串
string s;
cout << "输入带空格的字符串:";
getline(cin, s); // 可以读取整行,包括空格
cout << "完整字符串:" << s << endl;
return 0;
}
关键点解析:
cin.ignore()用于清除缓冲区中的残留字符(特别是换行符)- 第一个参数表示要忽略的字符数量
numeric_limits<streamsize>::max()表示无限制- 第二个参数
'\n'表示遇到换行符就停止
getline(cin, str)会读取整行,直到遇到换行符- 换行符会被读取但不会存入字符串
- 可以正确处理包含空格的字符串
血泪教训:记得我在一次笔试中,因为没有处理cin后的换行符,导致getline直接读取了一个空字符串,整道题都白做了。从此以后,只要混用cin和getline,我一定会先调用cin.ignore()。
3. 向上取整的两种实现方式
在计算分页、任务分配等场景时,向上取整是常见需求。比如总共有103个任务,每个worker处理3个,需要多少个worker?显然103/3=34.333...,需要35个worker才能处理完。
3.1 数学技巧法
cpp复制#include<iostream>
using namespace std;
int main() {
int distance = 103;
int speed = 3;
int time = (distance + speed - 1) / speed;
cout << "需要的时间单位:" << time << endl; // 输出35
return 0;
}
这个经典公式(a + b - 1) / b的原理是:
- 分子加上分母减1,确保余数不为0时会进位
- 然后进行整数除法
- 相当于数学上的⌈a/b⌉
举例说明:
- 103 + 3 - 1 = 105
- 105 / 3 = 35
- 如果是100个任务:(100 + 3 - 1)/3 = 102/3 = 34
适用场景:这种方法特别适合不能使用浮点数运算的环境,比如某些嵌入式系统编程。
3.2 标准库函数法
cpp复制#include<iostream>
#include<cmath> // 必须包含这个头文件
using namespace std;
int main() {
int distance = 103;
int speed = 3;
// 向上取整
int time1 = ceil(distance * 1.0 / speed);
cout << "ceil结果:" << time1 << endl; // 35
// 向下取整
int time2 = floor(distance * 1.0 / speed);
cout << "floor结果:" << time2 << endl; // 34
return 0;
}
重要注意事项:
- 必须包含
<cmath>头文件 - 参数必须是浮点类型:
distance * 1.0将整数转为浮点数- 否则整数除法会先截断,ceil就无效了
- ceil和floor的区别:
- ceil(天花板):向正无穷方向取整
- floor(地板):向负无穷方向取整
性能对比:在leetcode等OJ系统上测试,数学技巧法通常比库函数法快10%-20%,因为避免了浮点数运算的开销。但在现代CPU上,这种差异往往可以忽略不计。
4. 常见问题与调试技巧
在实际刷题过程中,我积累了一些调试经验,特别适合初学者参考:
4.1 最大值求解的边界情况
- 所有数相同的情况:
cpp复制int a = 5, b = 5, c = 5; // 所有方法都应正确返回5 - 包含INT_MIN的情况:
cpp复制int a = INT_MIN, b = -1, c = 0; // 确保初始化max时不要用0
4.2 getline的常见陷阱
-
混合输入时的问题:
cpp复制int n; cin >> n; // 输入后按回车 string s; getline(cin, s); // 会立即读取到空字符串解决方法:
cpp复制cin >> n; cin.ignore(); // 简单的忽略一个字符 // 或者更彻底的: cin.ignore(numeric_limits<streamsize>::max(), '\n'); -
读取空行:
cpp复制// 如果getline读取到空行,s将是空字符串 if(s.empty()) { cout << "读取到空行!"; }
4.3 取整运算的精度问题
-
浮点数精度导致的错误:
cpp复制// 错误示例: int a = 1000000000, b = 3; double res = a / b; // 先做整数除法,精度已丢失正确做法:
cpp复制double res = static_cast<double>(a) / b; -
大数情况下的溢出:
cpp复制// 数学技巧法在大数时可能溢出 int a = INT_MAX, b = 1; int ceil = (a + b - 1) / b; // 可能溢出安全写法:
cpp复制int ceil = a / b + (a % b != 0);
5. 效率对比与选择建议
经过多次leetcode实测,我总结了不同方法的性能特点:
| 方法 | 适用场景 | 时间复杂度 | 备注 |
|---|---|---|---|
| if比较法 | 少量数据 | O(n) | 最易理解 |
| STL max | 代码简洁 | O(n) | 可读性好 |
| 三目运算符 | 代码量敏感 | O(n) | 可读性差 |
| 数学取整 | 无浮点环境 | O(1) | 可能溢出 |
| ceil函数 | 一般场景 | 较慢 | 需浮点转换 |
选择建议:
- 竞赛编程:优先考虑数学技巧法和三目运算符
- 工程项目:推荐STL算法和ceil/floor函数
- 嵌入式环境:使用数学技巧避免浮点运算
最后分享一个实用技巧:在VS Code中,可以安装C++插件后使用快捷键:
- Ctrl+Shift+P → "Run Code"快速测试代码
- 或者配置tasks.json实现一键编译运行
这些方法看似简单,但在实际刷题中能节省大量时间。我刚开始刷leetcode时,就因为没有掌握这些基础技巧,在简单题上浪费了很多时间。希望这篇总结能帮助大家少走弯路,高效刷题!
