算法导论示例-Huffman

本文介绍了一种基于哈夫曼编码的数据压缩算法实现方法。通过构建哈夫曼树,并使用最小优先队列来优化编码过程,实现了高效的数据压缩。文章详细展示了哈夫曼树的构建过程及编码流程。
/**
 * Introduction to Algorithms, Second Edition 
 * 16.3 Huffman codes 
 * 
 * @author 土豆爸爸
 * 
 */
import java.util.List;

public class Huffman {
    static class Node implements IPriorityQueueElement<Integer>{
        Node left; //左子节点
        Node right; //右子节点
        Integer f; //频率
        char c; //字符
        
        public Node() {}
        
        public Node(char c, Integer f) {
            this.c = c;
            this.f = f;
        }
        public Integer getKey() {
            return f;
        }
    }
    
    public static Node encode(List<Node> nodes) {
        int n = nodes.size();
        
        //根据频率生成最小优先队列
        MinPriorityQueue<Integer, Node> queue = new MinPriorityQueue<Integer, Node>(n);
        for(Node node : nodes) {
            queue.insert(node);
        }
        
        for(int i = 0; i < n - 1; i++) {
            Node node = new Node();
            Node x = queue.extractMin(); //取出队列的前两个元素
            Node y = queue.extractMin();
            node.left = x; //将取出两个元素作为新节点的子节点
            node.right = y;
            node.f = x.f + y.f; //新节点的频率是子节点频率之和
            queue.insert(node); //插入到队列中
        }
        
        return queue.extractMin();
    }
}

/**
 * Introduction to Algorithms, Second Edition 
 * 6.5 min-priority queue 
 * 
 * @author 土豆爸爸
 * 
 */
import java.util.EmptyStackException;

public class MinPriorityQueue<KeyType extends Comparable<KeyType>, T extends IPriorityQueueElement<KeyType>> {
    T[] array;

    int heapSize;

    /**
     * 构造函数
     * @param size 初始数组大小
     */
    @SuppressWarnings("unchecked")
    public MinPriorityQueue(int size) {
        array = (T[]) new IPriorityQueueElement[size];
    }

    /**
     * 获取当前heap中的最小值
     * 
     * @return 最小值
     */
    public T minimum() {
        return array[0];
    }

    /**
     * 获取当前heap中的最小值,并从heap中删除最小值
     * @return 最小值
     */
    public T extractMin() {
        if (heapSize < 1) {
            throw new EmptyStackException();
        }
        T min = array[0];
        array[0] = array[heapSize - 1];
        heapSize--;
        minHeapify(0);
        return min;
    }

    /**
     * 插入一个元素
     * @param e
     */
    @SuppressWarnings("unchecked")
    public void insert(T e) {
        if (heapSize == array.length) {
            T[] newArray = (T[]) new IPriorityQueueElement[array.length * 2];
            System.arraycopy(array, 0, newArray, 0, array.length);
            array = newArray;
        }
        int i = heapSize++;
        array[i] = e;
        int p = parent(i); // 父结点索引
        while (i > 0 && array[p].getKey().compareTo(array[i].getKey()) > 0) {
            T temp = array[i];
            array[i] = array[p];
            array[p] = temp;
            i = p;
            p = parent(i);
        }
    }

    /**
     * 使数组的第i个元素按max heap规则重排
     * 
     * @param i
     *            元素索引
     */
    private void minHeapify(int i) {
        int l = left(i);
        int r = right(i);
        int smallest; // 当前结点/左子结点/右子结点中最大值的索引
        if (l < heapSize && array[l].getKey().compareTo(array[i].getKey()) < 0) {
            smallest = l;
        } else {
            smallest = i;
        }

        if (r < heapSize && array[r].getKey().compareTo(array[smallest].getKey()) < 0) {
            smallest = r;
        }

        if (smallest != i) {
            // 如果最大值不是当前结点,进行交换
            T temp = array[i];
            array[i] = array[smallest];
            array[smallest] = temp;
            // 递归调用,直到当前结点比其子结点大
            minHeapify(smallest);
        }

    }

    /**
     * 计算结点索引为i的元素的父结点的索引
     * 
     * @param i
     *            当前索引
     * @return 父结点的索引
     */
    private int parent(int i) {
        return (i + 1) / 2 - 1;
    }

    /**
     * 计算结点索引为i的元素的左子结点的索引
     * 
     * @param i
     *            当前索引
     * @return 左子结点的索引
     */
    private int left(int i) {
        return 2 * i + 1;
    }

    /**
     * 计算结点索引为i的元素的右子结点的索引
     * 
     * @param i
     *            当前索引
     * @return 右子结点的索引
     */
    private int right(int i) {
        return 2 * i + 2;
    }
}

public interface IPriorityQueueElement<KeyType extends Comparable<KeyType>>{
    KeyType getKey();
}

import java.util.ArrayList;
import java.util.List;

import junit.framework.TestCase;

public class HuffmanTest extends TestCase {
    public void testEncode() {
        List<Huffman.Node> c = new ArrayList<Huffman.Node>();
        c.add(new Huffman.Node('a', 45));
        c.add(new Huffman.Node('b', 13));
        c.add(new Huffman.Node('c', 12));
        c.add(new Huffman.Node('d', 16));
        c.add(new Huffman.Node('e', 9));
        c.add(new Huffman.Node('f', 5));
        
        Huffman.Node root = Huffman.encode(c);
        assertEquals(100, root.f.intValue());
        assertEquals(45, root.left.f.intValue());
        assertEquals('a', root.left.c);
        assertEquals(55, root.right.f.intValue());
        assertEquals('c', root.right.left.left.c);
        assertEquals('b', root.right.left.right.c);
        assertEquals('f', root.right.right.left.left.c);
        assertEquals('e', root.right.right.left.right.c);
        assertEquals('d', root.right.right.right.c);
    }
}

自考《数据结构导论》中的算法设计题目通常会考察对基本数据结构的理解以及如何运用这些知识解决实际问题的能力。这类试题一般包括但不限于以下几个方面: ### 一、线性表的应用 例如通过数组或链表实现队列、栈等特殊形式的数据容器,并基于此完成特定任务如逆波兰表达式求值、迷宫寻路等问题。 #### 示例: 编写程序模拟停车场管理系统,车辆按到达顺序停放于空位上;离场时需计算停车费用并释放对应位置空间供其他车占用。 - 数据结构选择:循环单向链表适合处理动态变化的位置分配情况; - 算法思路概述:插入新节点表示有新车进入(入栈操作),删除指定元素结点意味着某辆车离开(出栈动作); ### 二、树形结构的操作 涵盖二叉查找树(Binary Search Tree)及其变种AVL平衡树、红黑树(Red-Black Tree),还有哈夫曼编码(Huffman Coding)构建最优前缀码方案等内容。 #### 案例分析: 给定一组字符频率统计信息构造最小带权路径长度的霍夫曼树,并据此生成压缩后的电文字符串。 - 关键步骤解析:先将所有叶子节点视为独立的小型森林集合体;接着每次从未排序序列里挑取两个权重最低者合并成新的内部节点直至剩下唯一根部为止;最后依据各分支方向01赋值规则确定最终结果串流。 ### 三、图遍历及最短路径寻找 Dijkstra迪杰斯特拉、Floyd弗洛伊德全源最短距离矩阵更新法则都是常见考点所在之处。此外有关连通分量探寻也常出现在试卷当中。 #### 具体实例: 已知城市间道路网络布局状况(无向有权边加顶点构成),请求出行人从起点到终点所经过路段总里程数最少的一条完整路线规划案。 - 技巧分享提示:采用优先级队列优化版本dijkstra可以有效降低时间复杂度,在保证效率的前提下快速获取全局范围内任意两点间的最佳行程安排建议。 以上只是部分关于“自考数据结构导论”课程内可能涉及到的一些典型练习示例,希望对你有所帮助! --
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值