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 等,但理解这种基础算法有助于我们更好地掌握更高级的概念。

相关推荐
NE_STOP12 小时前
Vide Coding--AI编程工具的选择
java
摇滚侠12 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
码云数智-园园12 小时前
C++20 Modules 模块详解
java·开发语言·spring
程序员黑豆12 小时前
JDK 下载安装与配置详细教程
java·前端·ai编程
bush412 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行52012 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz12 小时前
Maven依赖冲突
java·服务器·maven
swordbob12 小时前
NIO的channel中什么是 fd(File Descriptor,文件描述符)
java·开发语言·nio
咖啡八杯13 小时前
GoF设计模式——享元模式
java·spring·设计模式·享元模式
十五喵源码网13 小时前
基于springboot2+vue2的租房管理系统
java·毕业设计·springboot·论文笔记