Rust 练习册 97:Run-Length Encoding 压缩算法

在数据处理和存储领域,压缩是一种非常常见的技术。今天我们要探讨的是一个经典的压缩算法------Run-Length Encoding(游程编码)。这是一种简单但有效的数据压缩方法,在某些特定场景下能发挥重要作用。

什么是 Run-Length Encoding?

Run-Length Encoding 是一种无损数据压缩算法,特别适用于包含大量连续重复元素的数据集。它的基本思想是将连续出现的相同字符替换为该字符出现次数和字符本身的组合。

例如,字符串 "AAABBCC" 可以被压缩为 "3A2B2C",这样就节省了存储空间。当然,这种方法对于没有太多连续重复字符的数据效果不佳,甚至可能会增加数据大小。

实现思路

让我们先看看我们需要实现的两个核心函数:

rust 复制代码
pub fn encode(source: &str) -> String {
    unimplemented!("Return the run-length encoding of {}.", source);
}

pub fn decode(source: &str) -> String {
    unimplemented!("Return the run-length decoding of {}.", source);
}

这两个函数分别负责编码和解码操作。我们的任务是完成它们的具体实现。

编码实现

我们从编码开始。编码过程需要遍历输入字符串,统计连续相同字符的数量,并按照规则进行转换:

rust 复制代码
pub fn encode(source: &str) -> String {
    if source.is_empty() {
        return String::new();
    }
    
    let mut result = String::new();
    let chars: Vec<char> = source.chars().collect();
    let mut i = 0;
    
    while i < chars.len() {
        let current_char = chars[i];
        let mut count = 1;
        let mut j = i + 1;
        
        // 计算连续相同字符的数量
        while j < chars.len() && chars[j] == current_char {
            count += 1;
            j += 1;
        }
        
        // 根据数量决定如何表示
        if count == 1 {
            result.push(current_char);
        } else {
            result.push_str(&count.to_string());
            result.push(current_char);
        }
        
        i = j;
    }
    
    result
}

解码实现

解码过程则是编码的逆向操作,需要解析数字和字符的组合,并将其还原为原始字符串:

rust 复制代码
pub fn decode(source: &str) -> String {
    if source.is_empty() {
        return String::new();
    }
    
    let mut result = String::new();
    let chars: Vec<char> = source.chars().collect();
    let mut i = 0;
    
    while i < chars.len() {
        if chars[i].is_digit(10) {
            // 如果当前字符是数字,则需要读取完整的数字
            let mut j = i;
            while j < chars.len() && chars[j].is_digit(10) {
                j += 1;
            }
            
            // 解析数字
            let count: usize = source[i..j].parse().unwrap();
            let character = chars[j];
            
            // 添加指定数量的字符到结果中
            for _ in 0..count {
                result.push(character);
            }
            
            i = j + 1;
        } else {
            // 单个字符直接添加到结果中
            result.push(chars[i]);
            i += 1;
        }
    }
    
    result
}

测试案例分析

通过测试案例我们可以更深入地理解算法的行为:

rust 复制代码
#[test]
fn test_encode_empty_string() {
    assert_eq!("", rle::encode(""));
}

#[test]
fn test_encode_single_characters() {
    assert_eq!("XYZ", rle::encode("XYZ"));
}

#[test]
fn test_encode_string_with_no_single_characters() {
    assert_eq!("2A3B4C", rle::encode("AABBBCCCC"));
}

#[test]
fn test_encode_single_characters_mixed_with_repeated_characters() {
    assert_eq!(
        "12WB12W3B24WB",
        rle::encode("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB")
    );
}

这些测试展示了各种情况下的编码行为,包括空字符串、无重复字符、纯重复字符以及混合情况。

解码的测试案例也相应地验证了逆向过程:

rust 复制代码
#[test]
fn test_decode_empty_string() {
    assert_eq!("", rle::decode(""));
}

#[test]
fn test_decode_single_characters_only() {
    assert_eq!("XYZ", rle::decode("XYZ"));
}

#[test]
fn test_decode_string_with_no_single_characters() {
    assert_eq!("AABBBCCCC", rle::decode("2A3B4C"));
}

#[test]
fn test_decode_single_characters_with_repeated_characters() {
    assert_eq!(
        "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB",
        rle::decode("12WB12W3B24WB")
    );
}

最后还有一个一致性测试确保编码后再解码能得到原始数据:

rust 复制代码
#[test]
fn test_consistency() {
    assert_eq!(
        "zzz ZZ  zZ",
        rle::decode(rle::encode("zzz ZZ  zZ").as_str())
    );
}

应用场景

Run-Length Encoding 虽然简单,但在某些特定场景下非常有用:

  1. 图像处理:在黑白二值图像中,经常有大片连续的黑色或白色像素。
  2. 简单数据压缩:对于具有大量重复数据的简单文本文件。
  3. 教学用途:作为入门级的数据压缩算法示例。

总结

通过这个练习,我们学习了如何实现 Run-Length Encoding 算法,掌握了 Rust 中字符串处理的基本技巧。虽然这只是一个简单的压缩算法,但它很好地展示了数据压缩的基本原理。

在实际应用中,我们会遇到更多复杂的压缩算法如 Huffman 编码、LZW 等,但理解这种基础算法有助于我们更好地掌握更高级的概念。

相关推荐
h***346334 分钟前
在linux(Centos)中Mysql的端口修改保姆级教程
linux·mysql·centos
2509_9408802236 分钟前
Linux(CentOS)安装 MySQL
linux·mysql·centos
爱笑的源码基地37 分钟前
医院随访管理系统源码 患者随访管理系统源码 智能随访系统源码 出院随访系统源码,基于Java+Vue前后端分离架构
java·spring boot·源码·二次开发·源代码管理·随访系统·医院随访
Le1Yu39 分钟前
订单优化(状态机、分库分表、覆盖索引、缓存优化查询)
java·开发语言·数据库
可爱又迷人的反派角色“yang”39 分钟前
LVS+Keepalived群集
linux·运维·服务器·前端·nginx·lvs
JIngJaneIL41 分钟前
书店销售|书屋|基于SprinBoot+vue书店销售管理设计与实现(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·毕设·书店销售管理设计与实现
不光头强43 分钟前
mybatis中的延迟加载和一二级缓存
java·tomcat·mybatis
奋斗的小乌龟1 小时前
k8s测试环境开启远程调试
java·spring
IDOlaoluo1 小时前
JK2连接器使用教程:jakarta-tomcat-connectors-jk2-src-current.zip 安装配置步骤详解
java·tomcat