RLE算法java实现(非递归)

RLE算法

RLE(Run LengthEncoding行程编码)算法是一个简单高效的无损数据压缩算法,其基本思路是把数据看成一个线性序列,而这些数据序列组织方式分成两种情况:一种是连续的重复数据块,另一种是连续的不重复数据块。对于连续的重复数据快采用的压缩策略是用一个字节(我们称之为数据重数属性)表示数据块重复的次数,然后在这个数据重数属性字节后面存储对应的数据字节本身。

示例

简单来说,RLE算法就是用一个字节来记录重复/非重复数量字节数量,N,D1,D2…DN。
一个字节是8位,高位1表示非重复,0表示重复。低7位表示后面字节数量,最大表示127个字符。

如下三组数据示例:

  • 1 原始数据
    AAAAAAAAAABBBBB (15个字节)

  • 1 RLE
    [10]A [5]B (4个字节)

  • 2 原始数据
    AAAAAAAAAABCDEF (15个字节)

  • 2 RLE
    [10]A [5]BCDEF(8个字节)

  • 3 原始数据
    ABCDEFG (7个字节)

  • 3 RLE
    [7]ABCDEFG(8个字节)

不难发现,当数据中重复的数据较多时,通过RLE算法可以有效的压缩数据;当数据中非重复的数据较多时,通过RLE算法压缩数据有限,甚至有可能增大数据量。

应用场景

通过上面的分析,当已知重复的数据可能较多,且有压缩数据需求的场景时,RLE算法比较适用。
那生活中熟悉的场景有哪些呢?

  1. 视频压缩:我们知道视频中常常有些静态的背景,很多帧才变化一下,(尤其是监控视频) , 那么RLE算法很适用。
  2. 黑白图像打印:黑白图像打印场景下,颜色非黑即白,也很符合适用场景。
java压缩和解压缩算法(非递归)

网上这个算法很成熟,但非递归的java算法很少,这里把我写的算法和详细注释分享给大家,希望有用。

    /**
     * 判断从index开始连续3个字符是否一致,如果剩余字符数<3返回false
     *
     * @param index
     * @param data
     * @param len
     * @return
     */
    private static boolean isRepetitionStart(int index, byte[] data, int len) {
        byte b1, b2, b3;
        if (index + 2 >= len) {
            return false;
        }
        b1 = data[index];
        b2 = data[index + 1];
        b3 = data[index + 2];
        if (b1 == b2 && b2 == b3) {
            return true;
        }
        return false;
    }

    
    /**
     * RLE压缩算法,返回压缩后的byte数组
     *
     * @param src
     * @return
     */
    public static byte[] rleCompress(byte[] src) {
        ///缓存压缩数据
        ArrayList<Byte> dest = new ArrayList<>();
        ///记录用于比较的字符
        byte newChar, oldChar = 0;
        ///记录当前数据块儿起始位置和长度
        int start, count;
        int srcLen = src.length;
        int i = 0;
        while (i < srcLen) {
            count = 0;
            ///发现重复块儿,AAA
            if (isRepetitionStart(i, src, srcLen)) {
                ///通过while循环找出重复块所有重复的字符,重复块长度>=3
                while (i < srcLen) {
                    ///重复块的起始位置进行初始化
                    if (count == 0) {
                        oldChar = src[i];
                        count = 1;
                        i++;
                        continue;
                    }
                    newChar = src[i];
                    if (newChar != oldChar) {
                        ///AAAB发现非重复字符,跳出重复块判断
                        break;
                    }
                    count++;
                    i++;
                    if (count == 127) {
                        //重复字符数达到重复块的最大长度,跳出
                        //最高位用来表示数据块儿相同和不同,数据块长度由低7位表示,最大表示127个数据
                        break;
                    }
                }
                ///记录重复块的数据值
                /// 第一个byte表示重复块0x00|count,最高位0表示重复块,后7位表示重复块长度
                dest.add((byte) count);
                ///第二个byte表示重复块的char
                dest.add(oldChar);
            } else {
                ///非重复块儿,先记录起始位置
                start = i;
                while (i < srcLen) {
                    ///非重复块儿其实位置进行初始化
                    if (count == 0) {
                        if ((i + 2) >= srcLen) {
                            ///剩余2个字符,不可能重复了,则将最后几个字符算作一个不重复块儿,并break结束
                            count = srcLen - i;
                            i = srcLen;
                            break;
                        } else {
                            ///默认连续3个不连续,把指针移动到第三个char
                            oldChar = src[i + 2];
                            count = 3;
                            i += 3;
                            continue;
                        }
                    }
                    newChar = src[i];
                    ///ABCC遇到相同的char,跳出不连续块判断,指针后退到上一个字符处
                    ///ABBB场景可能损失一个字符,但影响不大
                    if (newChar == oldChar) {
                        i--;
                        count--;
                        break;
                    }
                    oldChar = newChar;
                    count++;
                    i++;
                    if (count == 127) {
                        ///非重复字符数达到非重复块的最大长度,跳出
                        ///最高位用来表示数据块儿相同和不同,数据块长度由低7位表示,最大表示127个数据
                        break;
                    }
                }
                ///记录非重复块的数据值
                /// 第一个byte表示非重复块0x80|count,最高位1表示非重复块,后7位表示非重复块长度
                dest.add((byte) (count | 0x80));
                ///将非重复块数据拷贝到dest中
                for (int j = 0; j < count; j++) {
                    dest.add(src[start + j]);
                }
            }
        }
        byte[] destArray = new byte[dest.size()];
        for (int j = 0; j < dest.size(); j++) {
            destArray[j] = dest.get(j);
        }
        ///返回压缩后的byte数组
        return destArray;
    }

    public static byte[] rleUncompress(byte[] src) {
        ArrayList<Byte> dest = new ArrayList<>();
        int index = 0;
        while (index < src.length) {
            byte flag = src[index++];
            boolean isRepeat = ((flag >> 7) & 0xFF) == 0;
            int count = flag & 0x7F;
            if (isRepeat) {
                byte v = src[index++];
                for (int i = 0; i < count; i++) {
                    dest.add(v);
                }
            } else {
                for (int i = 0; i < count; i++) {
                    dest.add(src[index++]);
                }
            }
        }
        byte[] destArray = new byte[dest.size()];
        for (int i = 0; i < dest.size(); i++) {
            destArray[i] = dest.get(i);
        }
        ///返回压缩后的byte数组
        return destArray;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值