数字反转是编程中常见的需求,特别是在处理数值计算和算法问题时。在C++中,我们可以利用标准库提供的工具高效地实现这一功能。
最直观的实现方式是先将数字转换为字符串,然后反转字符串:
cpp复制#include <string>
#include <algorithm>
int reverseNumber(int num) {
std::string s = std::to_string(abs(num));
std::reverse(s.begin(), s.end());
return (num < 0 ? -1 : 1) * std::stoi(s);
}
注意:这种方法在处理大数时可能会溢出,需要额外检查边界条件。
更高效的方式是直接通过数学运算实现反转:
cpp复制int reverseNumberMath(int num) {
int reversed = 0;
while (num != 0) {
if (reversed > INT_MAX/10 || reversed < INT_MIN/10)
return 0; // 处理溢出
reversed = reversed * 10 + num % 10;
num /= 10;
}
return reversed;
}
实际应用中需要考虑多种边界情况:
cpp复制int reverseNumberSafe(int num) {
long long reversed = 0; // 使用更大范围类型防止中间结果溢出
while (num != 0) {
reversed = reversed * 10 + num % 10;
num /= 10;
}
return (reversed < INT_MIN || reversed > INT_MAX) ? 0 : reversed;
}
当需要累加除数直到满足某个条件时,常规做法是:
cpp复制int sum = 0;
int count = 0;
while (sum < threshold) {
sum += divisor;
count++;
}
如果累加量固定,可以直接计算需要累加的次数:
cpp复制int count = (threshold + divisor - 1) / divisor; // 向上取整
int sum = divisor * count;
这种方法将O(n)的时间复杂度降为O(1),特别适合大数计算。
当累加数呈规律性变化时:
cpp复制// 等差数列求和
int arithmeticSum(int a1, int d, int n) {
return n * (2 * a1 + (n - 1) * d) / 2;
}
// 等比数列求和
int geometricSum(int a1, int r, int n) {
return a1 * (1 - pow(r, n)) / (1 - r);
}
差分数组是处理范围更新问题的利器,特别适合大规模数据的情况。
差分数组是原数组相邻元素的差:
code复制原数组: A = [a1, a2, a3, ..., an]
差分数组: D = [a1, a2-a1, a3-a2, ..., an-a(n-1)]
假设需要对数组A的区间[l,r]统一加val:
cpp复制// 传统方法 O(n)
for (int i = l; i <= r; i++) {
A[i] += val;
}
// 差分方法 O(1)
D[l] += val;
D[r+1] -= val;
cpp复制class Difference {
private:
vector<int> diff;
public:
Difference(vector<int>& nums) {
diff.resize(nums.size());
diff[0] = nums[0];
for (int i = 1; i < nums.size(); i++) {
diff[i] = nums[i] - nums[i-1];
}
}
void increment(int i, int j, int val) {
diff[i] += val;
if (j + 1 < diff.size()) {
diff[j+1] -= val;
}
}
vector<int> result() {
vector<int> res(diff.size());
res[0] = diff[0];
for (int i = 1; i < diff.size(); i++) {
res[i] = res[i-1] + diff[i];
}
return res;
}
};
| 方法 | 特点 | 适用场景 |
|---|---|---|
| push_back | 尾部插入,O(1) | 大多数追加场景 |
| insert | 任意位置插入,O(n) | 需要中间插入时 |
| emplace_back | 尾部构造,避免拷贝 | 对象较大时 |
cpp复制vector<int> v = {1,2,3};
v.insert(v.begin()+1, 4); // v = [1,4,2,3]
v.emplace_back(5); // v = [1,4,2,3,5]
提示:在debug阶段可以使用at()帮助发现越界问题,发布时改用[]提高性能。
cpp复制vector<int> largeVec;
largeVec.reserve(1000000); // 预分配空间避免多次扩容
// 高效批量插入
vector<int> source = {...};
largeVec.insert(largeVec.end(), source.begin(), source.end());
C++中几种实现方式对比:
cpp复制double d = 3.14159;
// 方法1:传统+0.5法
int round1 = (int)(d + 0.5);
// 方法2:使用cmath函数
#include <cmath>
int round2 = std::round(d);
// 方法3:C++11的lround
int round3 = std::lround(d);
cpp复制int a = 10, b = 3;
// 传统方法
int ceil1 = (a + b - 1) / b;
// 使用标准库
#include <cmath>
int ceil2 = std::ceil(static_cast<double>(a)/b);
金融等需要高精度计算的场景:
cpp复制#include <iomanip>
#include <sstream>
std::string preciseRound(double value, int precision) {
std::ostringstream oss;
oss << std::fixed << std::setprecision(precision);
oss << value;
return oss.str();
}
最简单的方阵转置:
cpp复制const int N = 3;
int matrix[N][N] = {...};
for (int i = 0; i < N; i++) {
for (int j = i+1; j < N; j++) {
std::swap(matrix[i][j], matrix[j][i]);
}
}
需要创建新矩阵:
cpp复制vector<vector<int>> transpose(const vector<vector<int>>& mat) {
if (mat.empty()) return {};
vector<vector<int>> res(mat[0].size(), vector<int>(mat.size()));
for (int i = 0; i < mat.size(); i++) {
for (int j = 0; j < mat[0].size(); j++) {
res[j][i] = mat[i][j];
}
}
return res;
}
对于特殊场景的优化算法:
cpp复制void transposeInPlace(vector<vector<int>>& mat) {
const int m = mat.size(), n = mat[0].size();
for (int i = 0; i < m; i++) {
for (int j = i+1; j < n; j++) {
swap(mat[i][j], mat[j][i]);
}
}
}
| 函数 | 参数 | 返回值 | 注意事项 |
|---|---|---|---|
| open | 路径,标志位,权限 | 文件描述符/-1 | 需检查返回值 |
| close | 文件描述符 | 0/-1 | 必须显式调用 |
| read | fd,缓冲区,字节数 | 读取字节数/-1 | 可能读取不足 |
| write | fd,数据,字节数 | 写入字节数/-1 | 需检查实际写入量 |
| lseek | fd,偏移量,基准 | 新位置/-1 | 可创建空洞文件 |
cpp复制#include <fcntl.h>
#include <unistd.h>
void fileCopy(const char* src, const char* dst) {
int in = open(src, O_RDONLY);
if (in == -1) { /* 错误处理 */ }
int out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (out == -1) { /* 错误处理 */ }
char buf[4096];
ssize_t nread;
while ((nread = read(in, buf, sizeof buf)) > 0) {
if (write(out, buf, nread) != nread) {
/* 写入错误处理 */
}
}
close(in);
close(out);
}
cpp复制int fd = open("file.txt", O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
exit(EXIT_FAILURE);
}
Linux文件系统关键概念:
文件描述符与内核数据结构关系:
code复制进程PCB → 文件描述符表 → 文件表项 → inode → 磁盘块
三种常见共享场景:
解决竞争的方法:
cpp复制// 使用O_APPEND保证原子写
int fd = open("file.txt", O_WRONLY | O_APPEND);
// 或者使用pread/pwrite
ssize_t pwrite(int fd, const void* buf, size_t count, off_t offset);
cpp复制truncate("file.txt", 1024); // 截断到1KB
ftruncate(fd, 2048); // 通过描述符操作
cpp复制#include <fcntl.h>
// 获取文件状态标志
int flags = fcntl(fd, F_GETFL);
// 设置追加模式
fcntl(fd, F_SETFL, flags | O_APPEND);
cpp复制#include <sys/ioctl.h>
// 获取终端大小
struct winsize ws;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
在实际开发中,我发现文件IO的性能很大程度上取决于缓冲策略的选择。对于频繁的小文件操作,可以考虑使用内存映射文件(mmap)来提高性能,特别是在处理大文件时,mmap可以避免频繁的read/write系统调用开销。