哈弗曼编码与反编码的实现 java源代码下载地址:http://download.csdn.net/source/2357457
public class Huffman { public static void main(String[] args) { new HaffmanFrame(); } }
//主界面类 import java.awt.*; import java.awt.event.*; import javax.swing.*; //主界面 public class HaffmanFrame extends JFrame implements ActionListener { private static final long serialVersionUID = 1808617924849665897L; private JButton choice, ok, reset,recoding,re;//choice 文件选择,ok 确定 ,reset 重置,recoding 反编译 private JLabel choiceJL,codingJL,recodingJL;//选择 编码 反编码 标签 private JTextArea inputJTA,recodingJTA; private JScrollPane js1,js2,js3; private HaffmanJTable table ; private TotalChars tc = null; private HffmanCoding hfc = null; HaffmanFrame(){ super("哈弗曼编码与反编码"); newFrame();//界面的初始化 formeValidate(); } @Override public void actionPerformed(ActionEvent e) { // TODO 事件的处理 if(e.getSource() == choice) { open(); } else if(e.getSource() == ok) { ok(); } else if(e.getSource() == reset) { reset(); } else if(e.getSource() == recoding) { if(tc != null ) { JOptionPane.showMessageDialog(this, hfc.reCoding(recodingJTA.getText()), "反哈弗曼编码文本", JOptionPane.DEFAULT_OPTION); } } else if(e.getSource() == re) { recodingJTA.setText(null); } } //重置 private void reset() { inputJTA.setText(""); recodingJTA.setText(""); table.beginTable(); table.repaint(); } // 确定 private void ok() { table.beginTable(); table.repaint(); tc = new TotalChars(); int chars[][] = tc.getCharAddWeight(inputJTA.getText()); hfc = new HffmanCoding(chars); hfc.coding();//哈弗曼树 String s[] = hfc.CreateHCode();//哈弗曼编码 for (int i = 0; i < chars.length; i++) { if (chars[i][0]<= 32) table.setT(i, 0, "A" + String.valueOf(chars[i][0]));//以ascll码的形式在表格输出 else table.setT(i, 0, String.valueOf((char) chars[i][0]));//以字符的形式在表格输出 table.setT(i, 1, String.valueOf(chars[i][1])); table.setT(i, 2, s[i]); } recodingJTA.setText(hfc.show(inputJTA.getText())); table.repaint(); } // 打开 private void open() { inputJTA.setText(""); FileOpen fo = new FileOpen(this); fo.open(); inputJTA.setText(fo.getText()); fo = null; } //界面的初始化 private void newFrame() { table = new HaffmanJTable(); choice = new JButton("选择文件"); ok = new JButton("确 定"); reset = new JButton("重 置"); recoding = new JButton("反编码"); re = new JButton("重置反编码"); choiceJL = new JLabel("请选择文件或输入文本:"); codingJL = new JLabel("字符的哈弗曼编码:"); recodingJL = new JLabel("文本的哈弗曼编码表示:"); inputJTA = new JTextArea(5,5); recodingJTA = new JTextArea(5,5); recodingJTA.setLineWrap(true); js1 = new JScrollPane(inputJTA); js2 = new JScrollPane(table.myTable()); js3 = new JScrollPane(recodingJTA); //设置位置 choiceJL.setBounds(10, 10, 150, 30); js1.setBounds(160, 10, 320, 150); ok.setBounds(200, 170, 80, 30); reset.setBounds(350, 170, 80, 30); choice.setBounds(30,50,100,30); codingJL.setBounds(10, 210, 150, 30); js2.setBounds(160, 210, 320, 150); recodingJL.setBounds(10, 370, 150, 30); js3.setBounds(160, 370, 320, 150); recoding.setBounds(30, 410, 100, 30); re.setBounds(30, 450, 100, 30); this.setLayout(null); this.add(choiceJL); this.add(js1); this.add(ok); ok.addActionListener(this); this.add(reset); reset.addActionListener(this); this.add(choice); choice.addActionListener(this); this.add(re); re.addActionListener(this); this.add(codingJL); this.add(js2); this.add(recodingJL); this.add(js3); this.add(recoding); recoding.addActionListener(this); Font f = new Font("宋体",Font.BOLD,15);//设置字体 inputJTA.setFont(f); recodingJTA.setFont(f); } private void formeValidate() { Toolkit tk = getToolkit();//获得屏幕的宽和高 Dimension dim = tk.getScreenSize(); this.setResizable(false); this.setBounds(dim.width/2-250, dim.height/2-280,500,570); setVisible(true); validate(); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } }
//表格类 import java.awt.Component; import java.awt.Font; import javax.swing.JTable; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; public class HaffmanJTable { private JTable table; private Object t[][] = new Object[100][3];//用来存放字符 次数 和哈弗曼编码 private Object name[] = {"字符","次数","哈弗曼编码"};//用来存放表头 public Component myTable() { // TODO Auto-generated method stub table = new JTable(t,name); Font f = new Font("宋体",Font.BOLD,15);//设置字体 table.setFont(f); setColumnSize(0,40);//设置第1列的大小 setColumnSize(1,60);//设置第2列的大小 setColumnSize(2,202);//设置第3列的大小 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);//AUTO_RESIZE_OFF // 不自动调整列的宽度;使用滚动条。 //table.setAutoResizeMode()当调整表的大小时,设置表的自动调整模式。 return table; } public void setT(int i ,int j, String volue) {//设置表格的值 this.t[i][j] = volue; } public void setColumnSize(int i, int width ) { TableColumnModel cm = table.getColumnModel(); //表格的列模型 TableColumn column = cm.getColumn(i);//得到第i个列对象 column.setPreferredWidth(width);//将此列的首选宽度设置为 preferredWidth。 //如果 preferredWidth 超出最小或最大宽度,则将其调整为合适的界限值。 } public void repaint() {//重绘表格 table.repaint(); } public void beginTable()//重值表格 { for (int i = 0; i < 100; i++) { for (int j = 0; j < 3; j++) setT(i, j, ""); } } }
//文件的打开 import java.awt.FileDialog; import java.awt.event.*; import java.io.*; import java.io.File; import java.io.FileReader; public class FileOpen { private FileDialog filedialog_open; private String fileopen = null, filename = null;// 用于存放打开文件地址 和文件名 private File file1; // 文件字节流对象 private FileReader file_reader;//文件字符流对象 private BufferedReader in;//文件行读取 写入对象 private StringBuffer text = new StringBuffer(); HaffmanFrame haffman= null; FileOpen(HaffmanFrame hf) { haffman = hf; filedialog_open = new FileDialog(haffman, "打开文件对话框", FileDialog.LOAD); // 打开文件对话框适配器 filedialog_open.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { filedialog_open.setVisible(false); } }); } public void open() { String s = ""; filedialog_open.setVisible(true); fileopen = filedialog_open.getDirectory();// 返回文件对话框中显示的文件所属的目录 filename = filedialog_open.getFile();// 返回当前文件对话框中显示的文件名的字符串表示 // 如果不存在就返回NULL if (filename != null)// 判断打开的文件是否存在 { try { file1 = new File(fileopen,filename ); file_reader = new FileReader(file1); in = new BufferedReader(file_reader);//每次读取一行 while ((s = in.readLine()) != null) text.append(s + '/n'); in.close(); file_reader.close(); } catch (IOException e2) { System.out.println("不能打开文件!"); } } } //返回得到的文本字符串 public String getText() { return new String(text); } }
//统计字符类 public class TotalChars { private int charCount[] = new int[255]; private char c[];//统计字符的个数 private int charAddWeight[][];//存放字符的ascll码值和字符的频率 private int total = 0;//存放需要编码字符的个数 TotalChars(){ for(int i = 0; i<255;i++) charCount[i] = 0; } //统计每个字符的出现的个数 public void charToal(String s) { c = new char[s.length()]; c = s.toCharArray();// 将字符串转化为字符数组 for (int i = 0; i < c.length; i++) { int j = c[i]; charCount[j]++; } for(int i = 0; i < 255 ; i++) { if (charCount[i] != 0) { total++;//统计字符的个数 } } } // 返回字符的ascll码值和字符的频率 public int[][] getCharAddWeight(String s){ charToal(s);//统计每个字符的出现的个数 charAddWeight = new int[total][2]; for(int i = 0,j=0; i < 255 ; i++) { if (charCount[i] != 0) { charAddWeight[j][0] = i; charAddWeight[j++][1] = charCount[i]; } } return charAddWeight; } }
//哈弗曼编码的实现类 public class HffmanCoding { private int charsAndWeight[][];// [][0]是 字符,[][1]存放的是字符的权值(次数) private int hfmcoding[][];// 存放哈弗曼树 private int i = 0;// 循环变量 private String hcs[]; public HffmanCoding(int[][] chars) { // TODO 构造方法 charsAndWeight = new int[chars.length][2]; charsAndWeight = chars; hfmcoding = new int[2 * chars.length - 1][4];// 为哈弗曼树分配空间 } // 哈弗曼树的实现 public void coding() { int n = charsAndWeight.length; if (n == 0) return; int m = 2 * n - 1; // 初始化哈弗曼树 for (i = 0; i < n; i++) { hfmcoding[i][0] = charsAndWeight[i][1];// 初始化哈弗曼树的权值 hfmcoding[i][1] = 0;// 初始化哈弗曼树的根节点 hfmcoding[i][2] = 0;// 初始化哈弗曼树的左孩子 hfmcoding[i][3] = 0;// 初始化哈弗曼树的右孩子 } for (i = n; i < m; i++) { hfmcoding[i][0] = 0;// 初始化哈弗曼树的权值 hfmcoding[i][1] = 0;// 初始化哈弗曼树的根节点 hfmcoding[i][2] = 0;// 初始化哈弗曼树的左孩子 hfmcoding[i][3] = 0;// 初始化哈弗曼树的右孩子 } // 构建哈弗曼树 for (i = n; i < m; i++) { int s1[] = select(i);// 在哈弗曼树中查找双亲为零的 weight最小的节点 hfmcoding[s1[0]][1] = i;// 为哈弗曼树最小值付双亲 hfmcoding[s1[1]][1] = i; hfmcoding[i][2] = s1[0];// 新节点的左孩子 hfmcoding[i][3] = s1[1];// 新节点的右孩子 hfmcoding[i][0] = hfmcoding[s1[0]][0] + hfmcoding[s1[1]][0];// 新节点的权值是左右孩子的权值之和 } } // 查找双亲为零的 weight最小的节点 private int[] select(int w) { // TODO Auto-generated method stub int s[] = { -1, -1 }, j = 0;// s1 最小权值且双亲为零的节点的序号 , i 是循环变量 int min1 = 32767, min2 = 32767; for (j = 0; j < w; j++) { if (hfmcoding[j][1] == 0) {// 只在尚未构造二叉树的结点中查找(双亲为零的节点) if (hfmcoding[j][0] < min1) { min2 = min1; s[1] = s[0]; min1 = hfmcoding[j][0]; s[0] = j; } else if (hfmcoding[j][0] < min2) { min2 = hfmcoding[j][0]; s[1] = j; } } } return s; } public String[] CreateHCode() {// 根据哈夫曼树求哈夫曼编码 int n = charsAndWeight.length; int i, f, c; String hcodeString = ""; hcs = new String[n]; for (i = 0; i < n; i++) {// 根据哈夫曼树求哈夫曼编码 c = i; hcodeString = ""; f = hfmcoding[i][1]; // f 哈弗曼树的根节点 while (f != 0) {// 循序直到树根结点 if (hfmcoding[f][2] == c) {// 处理左孩子结点 hcodeString += "0"; } else { hcodeString += "1"; } c = f; f = hfmcoding[f][1]; } hcs[i] = new String(new StringBuffer(hcodeString).reverse()); } return hcs; } public String show(String s) {// 对字符串显示编码 String textString = ""; char c[]; int k = -1; c = new char[s.length()]; c = s.toCharArray();// 将字符串转化为字符数组 for (int i = 0; i < c.length; i++) { k = c[i]; for (int j = 0; j < charsAndWeight.length; j++) if (k == charsAndWeight[j][0]) textString += hcs[j]; } return textString; } // 哈弗曼编码反编译 public String reCoding(String s) { String text = "";// 存放反编译后的字符 int k = 0, m = hfmcoding.length - 1;// 从根节点开始查询 char c[]; c = new char[s.length()]; c = s.toCharArray(); k = m; for (int i = 0; i < c.length; i++) { if (c[i] == '0') { k = hfmcoding[k][2];// k的值为根节点左孩子的序号 if (hfmcoding[k][2] == 0 && hfmcoding[k][3] == 0)// 判断是不是叶子节点,条件(左右孩子都为零) { text += (char) charsAndWeight[k][0]; k = m; } } if (c[i] == '1') { k = hfmcoding[k][3];// k的值为根节点右孩子的序号 if (hfmcoding[k][2] == 0 && hfmcoding[k][3] == 0)// 判断是不是叶子节点,条件(左右孩子都为零) { text += (char) charsAndWeight[k][0]; k = m; } } } return text; } }