RLE解码是将“频次+值”交替数组(如[2,8,3,9])还原为重复序列(如[8,8,9,9,9])。C++需手写:先遍历求总长并reserve,再用insert批量展开,注意频次非负、长度偶数、uint8_t场景需类型适配。

什么是RLE解码,C++里怎么写最直接
RLE(Run-Length Encoding)解码就是把形如 [2, 8, 3, 9] 这样的数组还原成 [8, 8, 9, 9, 9]:偶数索引是频次,奇数索引是对应值。C++ 没有标准库函数直接做这事,得手写循环——但别套复杂模板,用 std::vector 配合两次 insert 或一次 reserve + push_back 就够了。
常见错误是边读边 push 导致反复 realloc,性能掉得明显;或者把频次和值的位置搞反,比如误以为 [8, 2, 9, 3] 是合法输入(其实按惯例是频次在前)。
- 输入数组长度必须为偶数,否则直接报错或断言失败
- 频次值应为非负整数,
0合法(跳过该值),负数需提前检查 - 如果原始数据量很大(比如百万级元素),先调用
result.reserve(total_size)能省掉至少一半时间
用 for 循环 + reserve 的安全写法
这是最可控、最容易调试的实现方式,适合嵌入已有项目或需要自定义错误处理的场景:
std::vector<int> rle_decode(const std::vector<int>& encoded) {
std::vector<int> result;
size_t total_size = 0;
for (size_t i = 0; i < encoded.size(); i += 2) {
if (i + 1 >= encoded.size()) break; // 防止越界
int freq = encoded[i];
int val = encoded[i + 1];
if (freq < 0) throw std::invalid_argument("negative frequency");
total_size += freq;
}
result.reserve(total_size); // 一次性分配
for (size_t i = 0; i < encoded.size(); i += 2) {
if (i + 1 >= encoded.size()) break;
int freq = encoded[i];
int val = encoded[i + 1];
result.insert(result.end(), freq, val);
}
return result;
}
注意 result.insert(result.end(), freq, val) 比循环 push_back 更高效,且语义清晰。别用 std::fill_n 配合 back_inserter,它不预分配空间,反而更慢。
立即学习“C++免费学习笔记(深入)”;
遇到 uint8_t 或 char 类型数组怎么办
图像或嵌入式场景中,RLE 常用 uint8_t 存储(比如 BMP 的 RLE8)。这时不能直接套用 int 版本,否则符号扩展或截断会出错:
- 输入是
std::vector<uint8_t>?把循环里的int freq改成size_t freq,避免uint8_t赋给int后比较时发生隐式转换问题 - 输出类型要匹配:若原始是灰度图字节流,结果也该是
std::vector<uint8_t>,不是int - 特别注意:
encoded[i]是uint8_t,但i是size_t,别写成for (uint8_t i = 0; ...)—— 会导致无限循环(255 + 2 = 1)
为什么 std::transform 不适合做 RLE 解码
有人想用 std::transform 配合 lambda 把每对 (freq, val) 映射成子 vector 再拼接,这在逻辑上可行,但实际会踩三个坑:
- 无法预知总长度,
reserve失效,频繁扩容 - 每个子 vector 构造+移动带来额外开销,比单次循环慢 3–5 倍
- lambda 捕获
result时容易引发生命周期问题,尤其在返回局部 vector 时
真想函数式一点,可以封装成 decode_pair 辅助函数,但主循环仍建议用传统 for —— RLE 解码本质是顺序展开,不是映射变换。
最容易被忽略的是频次为 0 的情况:它合法,但很多手写代码直接跳过或报错。实际压缩数据里常出现 [0, 127] 这种占位符,得留着兼容逻辑。


















