哈夫曼树读取文件创建树进行解码译码之链表实现

  • 前言:

    今天呢我们来学习进阶版的哈夫曼树的构建,这也是我们数据结构上机课要求的实验报告三,老师要求不能用map和数组,并且在节点的权重相等的时候对树高进行比较,低的在前,高的在后进行排序,这样我们生成的编码就是一样的了,就可以在同学们之间进行编码和译码。

  • 题目具体如下

1、从指定文档中读取每个字符(仅限ASCII字符包含26个英文字符及逗号,句号和空格符号),统计其出现次数,计算出每个字符的权重
2、将字符构造出节点,借助链表构造Huffman树。具体过程是:每次找到链表中权值最小的两个节点,构造一个子树,再将其放回至链表,直至链表中最后两个节点。取出这两个节点构造成Huffman树。
3、遍历Huffman树,生成Huffman代码。
4、使用Huffman树对特定字符串翻译。

  • 代码如下:


import java.util.LinkedList;
import java.util.List;

public class HuffmanTree {

  private Node root;
  private List<Node> nodes;

  //总过程
  public HuffmanTree(String str) {
    nodes = countChar(str);

    root = createTree(nodes);

    generateHuffmanCode(root);
  }

  public void printTree() {
    printTreeHelper(root);//调用递归输出
  }
  
//编码:循环拼接字符串  字符串转成字符数组表示
  public String encode(String str) {
    StringBuilder sb = new StringBuilder();
    for (char c : str.toCharArray()) {
      sb.append(encode(c));
    }

    return sb.toString();//转成String 需要调用stringbuilder的toString方法
  }
  
//解码
  public String decode(String str) {
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < str.length(); ) {
      Node rt = root;
      while (i < str.length() && (rt.left != null || rt.right != null)) {
        if (str.charAt(i) == '0') {//左0
          rt = rt.left;
        } else {//右1
          rt = rt.right;
        }
        i++;//下一个
      }
      if (rt.left == null && rt.right == null) {
        sb.append(rt.ch);
      }
    }

    return sb.toString();
  }
  //解码方法
  private String encode(char ch) {
    for (Node node : nodes) {
      if (node.ch == ch) {
        return node.code;
      }
    }
    return "";
  }
  //对节点进行01编码
  private static void generateHuffmanCode(Node root) {
    if (root == null) {
      return;
    }
    if (root.left != null) {
      root.left.code = root.code + "0";
      generateHuffmanCode(root.left);
    }
    if (root.right != null) {
      root.right.code = root.code + "1";
      generateHuffmanCode(root.right);
    }
  }

  //构建哈夫曼树
  private static Node createTree(List<Node> nodes) {
    LinkedList<Node> list = new LinkedList<>(nodes);

    while (list.size() > 1) {
      Node left = getAndRemoveMinNode(list);//左
      Node right = getAndRemoveMinNode(list);//右

      Node parent = new Node(left.weight + right.weight, Math.max(left.height, right.height) + 1,
          left, right);

      list.add(parent);
    }
    return list.getFirst();
  }

  //得到链表中最小节点的方法
  private static Node getAndRemoveMinNode(List<Node> list) {
    Node node = list.stream().min(Node::compareTo).get();//比较器(stream内部迭代 处理循环,引用node中的compareTo 方法引用)
    list.remove(node);//得到后移去
    return node;
  }
  
  //生成字符结点,并统计每个字符出现的次数作为权重
  //遇到一个字符,需要找list中是否已经有了一个存放这个字符的位置
  //如果没有,生成一个新的槽(Node)放入list
  private static List<Node> countChar(String str) {
    List<Node> nodes = new LinkedList<>();
    for (char c : str.toCharArray()) {//字符串转换为字符数组
     if ('a' <= c && c <= 'z'
          || c == ' '
          || c == ','
          || c == '.') {
        Node slot = null;
        for (Node node : nodes) {
          if (node.ch == c) {
            slot = node;
            break;
          }
        }
        if (slot == null) {
          slot = new Node(c, 0);//出现次数0
          nodes.add(slot);
        }
        slot.weight++;
      }
    
    }
    return nodes;
    
  }
//递归输出
  private static void printTreeHelper(Node root) {
    if (root == null) {
      return;//当前节点如果为null就结束
    }
    if (root.left == null && root.right == null) {
      System.out.println(root);
    }
    printTreeHelper(root.left);
    printTreeHelper(root.right);
  }

//节点类
  private static class Node implements Comparable<Node> {

    char ch;//字符
    String code;//编码
    int weight;//权重
    int height;//高度
    Node left;//左孩子
    Node right;//右孩子

    Node(char ch, int weight) {
      this.ch = ch;
      this.weight = weight;
      this.height = 1;
      this.code = "";
    }

    Node(int weight, int height, Node left, Node right) {
      this.weight = weight;
      this.height = height;
      this.left = left;
      this.right = right;
      this.height = 1;
      this.code = "";
    }

    @Override
    public int compareTo(Node o) {
      return this.weight == o.weight ? this.height - o.height : this.weight - o.weight;//(权重,高度)权重一样。 比较高度。 权重不一样比较权重。
    }
    
    //输出
    @Override
    public String toString() {
      return "Node{" +
          "ch=" + ch +
          ", code='" + code + '\'' +
          ", weight=" + weight +
          '}';
    }
  }

}

  • 测试类:


import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

public class HMain {

  public static void main(String[] args) throws Exception {

    File file = new File("E://Demo.txt");
    BufferedReader br = new BufferedReader(new FileReader(file));
    StringBuilder sb = new StringBuilder();

    char[] buffer = new char[1024];
    int len = -1;

    while ((len = br.read(buffer)) != -1) {
      sb.append(buffer, 0, len);
    }

    br.close();

    String str = sb.toString();

    HuffmanTree huffmanTree = new HuffmanTree(str);// 创建哈弗曼对象
    huffmanTree.printTree(); // 显示字符的哈夫曼编码
    String hmCode = huffmanTree.encode(str);
    System.out.println("编码: " + hmCode);
    System.out.println("解码: " + huffmanTree.decode(hmCode));

    String text = "term is a word or expression with a specific meaning, especially one which is used in relation to a particular subject. " + 
    		"";


    hmCode = huffmanTree.encode(text);
    System.out.println("编码: " + hmCode);
    System.out.println("解码: " + huffmanTree.decode(hmCode));

  }
}

  • 这里我们给出测试的文件Demo(可以直接在E盘下新建txt文档):

a data structure models some abstract object. it implements a number of operations on this object, which usually can be classified into
creation and deletion operations, update operations, and query operations. in the case of the dictionary, we want to create or delete the set itself, update the
set by inserting or deleting elements, and query for the existence of an element in the set. once it has been created, the object is changed by the update operations.
the query operations do not change the abstract object, although they might change the representation of the object in the data structure: this is called an
adaptive data structure it adapts to the query to answer future similar queries faster.
data structures that allow updates and queries are called dynamic data structures. there are also simpler structures that are created just once for
some given object and allow queries but no updates; these are called static data structures. dynamic data structures are preferable because they are more
general, but we also need to discuss static structures because they are useful as building blocks for dynamic structures, and, for some of the more complex
objects we encounter, no dynamic structure is known. we want to find data structures that realize a given abstract object and are
fast. the size of structures is another quality measure, but it is usually of less importance. to express speed, we need a measure of comparison; this is the
size of the underlying object, not our representation of that object. notice that a long sequence of update operations can still result in a small object.

  • 运行截图

在这里插入图片描述

  • 这里我们用其他的方法实现这个题目:
import java.util.LinkedList;

class Huffman<T> {

	private String str;// 最初用于压缩的字符串
    private String result="";// 保存解码的字符串
	private String hfmCodeStr = "";// 哈夫曼编码连接成的字符串
    boolean target = false; // 解码标记
    private HNode root;// 哈夫曼二叉树的根节点
    private boolean flag;// 最新的字符是否已经存在的标签
    private LinkedList<CharData> charList;// 存储不同字符的队列 相同字符存在同一位置
    private LinkedList<HNode> NodeList;// 存储节点的队列


    private class CharData {
        int num = 1; // 字符个数
        char c; // 字符

        public CharData(char ch){
            c = ch;
        }
	}

    /**
     * 构建哈夫曼树
     *
     * @param str
     */
    public void creatHfmTree(String str) {
        this.str = str;

        NodeList = new LinkedList<HNode>();
        charList = new LinkedList<CharData>();

        // 1.统计字符串中字符以及字符的出现次数
        // 以CharData类来统计出现的字符和个数
        getCharNum(str);

        // 2.根据第一步的结构,创建节点
        creatNodes();

        // 3.对节点权值升序排序
        Sort(NodeList);

        // 4.取出权值最小的两个节点,生成一个新的父节点
        // 5.删除权值最小的两个节点,将父节点存放到列表中
        creatTree();

        // 6.重复第四五步,就是那个while循环
        // 7.将最后的一个节点赋给根节点
        root = NodeList.get(0);
    }

    /**
     * 统计出现的字符及其频率
     *
     * @param str
     */
    private void getCharNum(String str) {

        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i); // 从给定的字符串中取出字符
            flag = true;

            for (int j = 0; j < charList.size(); j++) {
                CharData data = charList.get(j);

                if(ch == data.c){
                    // 字符对象链表中有相同字符则将个数加1
                    data.num++;
                    flag = false;
                    break;
                }
            }

            if(flag){
                // 字符对象链表中没有相同字符则创建新对象加如链表
                charList.add(new CharData(ch));
            }
        }
    }
	private void creatNodes() {
        for (int i = 0; i < charList.size(); i++) {
            String data = charList.get(i).c + "";
            int count = charList.get(i).num;
            HNode node = new HNode(data, count); // 创建节点对象
            NodeList.add(node); // 加入到节点链表
        }
    }
    /**
     * 构建哈夫曼树
     */
    private void creatTree() {

        while (NodeList.size() > 1) {// 当节点数目大于一时
            // 4.取出权值最小的两个节点,生成一个新的父节点
            // 5.删除权值最小的两个节点,将父节点存放到列表中
            HNode left = NodeList.poll();
            HNode right = NodeList.poll();

            // 在构建哈夫曼树时设置各个结点的哈夫曼编码
            left.code = "0";
            right.code = "1";
            setCode(left);
            setCode(right);

            int parentWeight = left.count + right.count;// 父节点权值等于子节点权值之和
            HNode parent = new HNode(parentWeight, left, right);

            NodeList.addFirst(parent); // 将父节点置于首位
            Sort(NodeList); // 重新排序,避免新节点权值大于链表首个结点的权值
        }
    }
	/**
     * 升序排序
     *
     * @param nodelist
     */
    private void Sort(LinkedList<HNode> nodelist) {
        for (int i = 0; i < nodelist.size() - 1; i++) {
            for (int j = i + 1; j < nodelist.size(); j++) {
                HNode temp;
                if (nodelist.get(i).count > nodelist.get(j).count) {
                    temp = nodelist.get(i);
                    nodelist.set(i, nodelist.get(j));
                    nodelist.set(j, temp);
                }

            }
        }

    }
	/**
     * 设置结点的哈夫曼编码
     * @param root
     */
    private void setCode(HNode root) {

        if (root.lChild != null) {
            root.lChild.code = root.code + "0";
            setCode(root.lChild);
        }

        if (root.rChild != null) {
            root.rChild.code = root.code + "1";
            setCode(root.rChild);
        }
    }
	/**
     * 遍历
     *
     * @param node
     *            节点
     */
    private void output(HNode node) {

        if (node.lChild == null && node.rChild == null) {
            System.out.println(node.data + ": " + node.code);
        }
        if (node.lChild != null) {
            output(node.lChild);
        }
        if (node.rChild != null) {
            output(node.rChild);
        }
    }
	/**
     * 输出结果字符的哈夫曼编码
     */
    public void output() {
        output(root);
    }
	 /**
     * 编码
     * @param str
     * @return
     */
    public String toHufmCode(String str) {

        for (int i = 0; i < str.length(); i++) {
            String c = str.charAt(i) + "";
            search(root, c);
        }

        return hfmCodeStr;
    }

    /**
     *
     * @param root 哈夫曼树根节点
     * @param c 需要生成编码的字符
     */
    private void search(HNode root, String c) {
        if (root.lChild == null && root.rChild == null) {
            if (c.equals(root.data)) {
                hfmCodeStr += root.code; // 找到字符,将其哈夫曼编码拼接到最终返回二进制字符串的后面
            }
        }
        if (root.lChild != null) {
            search(root.lChild, c);
        }
        if (root.rChild != null) {
            search(root.rChild, c);
        }
    }
	/**
     * 解码
     * @param codeStr
     * @return
     */
    public String CodeToString(String codeStr) {

        int start = 0;
        int end = 1;

        while(end <= codeStr.length()){
            target = false;
            String s = codeStr.substring(start, end);
            matchCode(root, s); // 解码
            // 每解码一个字符,start向后移
            if(target){
                start = end;
            }
            end++;
        }

        return result;
    }

    /**
     * 匹配字符哈夫曼编码,找到对应的字符
     * @param root 哈夫曼树根节点
     * @param code 需要解码的二进制字符串
     */
    private void matchCode(HNode root, String code){
        if (root.lChild == null && root.rChild == null) {
            if (code.equals(root.code)) {
                result += root.data; // 找到对应的字符,拼接到解码字符穿后
                target = true; // 标志置为true
            }
        }
        if (root.lChild != null) {
            matchCode(root.lChild, code);
        }
        if (root.rChild != null) {
            matchCode(root.rChild, code);
        }

    }



}
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class HMain2 {

    public static void main(String[] args) throws IOException {
    	File file = new File("e:\\javaProject\\normal\\Demo.txt");
    	 FileInputStream fis = new FileInputStream(file);
         StringBuilder stb = new StringBuilder();
         byte[] b = new byte[1];
         while ((fis.read(b)) != -1) {
             stb.append(new String(b));
         }
         String str = stb.toString();

        Huffman huff1 = new Huffman();// 创建哈弗曼对象
        huff1.createHfmTree(str);// 构造树
        huff1.output(); // 显示字符的哈夫曼编码

        // 将目标字符串利用生成好的哈夫曼编码生成对应的二进制编码
        String hufmCode = huff1.toHufmCode(str);
        System.out.println("编码:" + hufmCode);
        // 将上述二进制编码再翻译成字符串
        System.out.println("解码:" + huff1.CodeToString(hufmCode));


        String text1 = "today is rainy";



        Huffman huff2 = new Huffman();// 创建哈弗曼对象
        huff2.createHfmTree(text1);// 构造树
        huff2.output(); // 显示字符的哈夫曼编码
        String hufmCode2 = huff2.toHufmCode(text1);

        System.out.println("编码: " + hufmCode2);
        System.out.println("解码: " + huff2.CodeToString(hufmCode2));
    }
}
public class HNode {

    public String code = "";// 节点的哈夫曼编码
    public String data = "";// 节点的数据
    public int count;// 节点的权值
    public HNode lChild;
    public HNode rChild;

    public HNode() {
    }

    public HNode(String data, int count) {
        this.data = data;
        this.count = count;
    }

    public HNode(int count, HNode lChild, HNode rChild) {
        this.count = count;
        this.lChild = lChild;
        this.rChild = rChild;
    }

    public HNode(String data, int count, HNode lChild, HNode rChild) {
        this.data = data;
        this.count = count;
        this.lChild = lChild;
        this.rChild = rChild;
    }

}

  • 再用其他方法去实现这个代码:
package 链表哈夫曼树二;


public class Haffmantree {
	//创建哈夫曼树的方法
	public Node createHaffmanTree(SingleList singlelist) {
		Node parent=null;

		while(singlelist.changdu()>1) {
//			//取出最小的结点
//			Node leftNode =singlelist.zuixiaoNode(singlelist);
//			//System.out.println("c"+leftNode.toString());

//
//			//取出第二小的结点
//			Node rightNode=singlelist.dierxiaoNode(singlelist);
			
			//取出最小的结点
			Node leftNode =singlelist.getfirstnode(singlelist);
			//删除已经找到的最小结点
//			System.out.println("1=> "+leftNode.weight);
			singlelist.del(leftNode);
			
			//取出第二小的结点
			Node rightNode=singlelist.getfirstnode(singlelist);
//			System.out.println("2=> "+rightNode.weight);
			//继续删除第二小的节点
			singlelist.del(rightNode);
			
//			Node temp;
//			//比较两个节点的高度
//			if(leftNode.getWeight()==rightNode.getWeight()) {
//				if(leftNode.gaodu(leftNode)>=rightNode.gaodu(rightNode)) {
//					temp=rightNode;
//					rightNode=leftNode;
//					leftNode=temp;			
//				}
//			}
			
			//比较两个节点的高度
			
			leftNode.BiJiaoGaoDu(leftNode,rightNode);
			
//			//创建一颗新的二叉树,它的根节点没有data,只有权值
//			parent=new Node ('\u0000',leftNode.getWeight()+rightNode.getWeight());
//			parent.setLeft(leftNode);
//			parent.setRight(rightNode);
			
			//创建一颗新的二叉树,它的根节点没有data,只有权值	
			//创建父节点
			parent=leftNode.fuNode(leftNode, rightNode);
			
			//将parent加入到链表
			singlelist.addByweight(parent);
		}
		return parent;
	}

	
}

package 链表哈夫曼树二;

public class Node implements Comparable <Node>{
	private char data;//存放数据(字符)本身
	private int weight;//结点权值,表示字符出现的次数
	private Node left;//指向左子结点
	private Node right;//指向右子结点
	private Node next;//指向下一个结点
	String code="";//用来存放0和1的
	
	
	public Node () {
		this('\0',0);
	}
	public Node (char data,int weight) {
		this.data=data;
		this.weight=weight;
	}
	
	
	
	@Override
	public String toString() {
		return "Node [data=" + data + ", weight=" + weight +  ", code=" + code + "]";
	}
	public char getData() {
		return data;
	}
	public void setData(char data) {
		this.data = data;
	}
	public int getWeight() {
		return weight;
	}
	public void setWeight(int weight) {
		this.weight = weight;
	}
	public Node getLeft() {
		return left;
	}
	public void setLeft(Node left) {
		this.left = left;
	}
	public Node getRight() {
		return right;
	}
	public void setRight(Node right) {
		this.right = right;
	}
	public Node getNext() {
		return next;
	}
	public void setNext(Node next) {
		this.next = next;
	}

	@Override
	public int compareTo(Node o) {
		//从小到大排序
		return this.weight-o.weight;
	}
	
	//前序遍历的方法
	public void preOrder() {
		if(this.data!='\u0000') {
			System.out.println(this);//输出这个结点
		}
		
		if(this.left!=null) {
			
			this.left.code =this.code+"0";
			this.left.preOrder();
			//去到左结点,code加0
		}
		if(this.right!=null) {
			
			this.right.code =this.code+"1";
			this.right.preOrder();
			//去到右结点,code加1
		}
	}
	
	//根据前序遍历查找特定字符的方法
	static String AAA="";
	public void preOrderChazhao(char zifu,Node root){
		//System.out.println(" "+root.code);
		if(root.data==zifu) {
			//System.out.println("aa");
			//每一次读到相应的字符,都用一个AAA变量来存储它
			AAA+=root.code;
			System.out.print(root.code);
		}
		if(root.left!=null) {	
			root.left.preOrderChazhao(zifu,root.left);
		}
		if(root.right!=null) {
			root.right.preOrderChazhao(zifu,root.right);
		}			
	}
	
	

//	//只前序遍历,不输出
//	public void preOrder1() {
//		if(this.left!=null) {
//			this.left.preOrder1();
//		}
//		if(this.right!=null) {
//			this.right.preOrder1();
//		}
//	}
	
	//只要有数据的结点,把它的左右孩子都变为根结点
	public void lrchildroot(Node root) {
		if(this.data!='\u0000') {
			this.left=root;
			this.right=root;
			return;
		}
		if(this.left!=null) {
			this.left.lrchildroot(root);
		}
		if(this.right!=null) {
			this.right.lrchildroot(root);
		}
	}
	 
	//孩子接受到0往左走,接受到1往右走的方法
	public Node xingzou(char i,Node root) {
		if(i=='0') root=root.left;
		else root=root.right;
		return root;	
	}
	
	//翻译的方法
	public void fanyi(String c,Node root) {
		Node root1=root;
		for(int j=0;j<c.length();j++) {
			root1=root1.xingzou(c.charAt(j), root1);
			if(root1.left==null) {System.out.print(root1.data);root1=root;}
		}
	}
	
	//高度的方法
	public int gaodu(Node node) {
		int count=1;
		Node temp1=node.left;
		Node temp2=node.right;
		while(temp1 !=null||temp2 !=null) {
			temp1=temp1.left;
			temp2=temp2.right;
			count++;
		}
		return count;
	}
	
	//高度的比较方法
	public void BiJiaoGaoDu (Node node1,Node node2) {
		Node temp;
		if(node1.getWeight()==node2.getWeight()) {
			if(node1.gaodu(node1)>=node2.gaodu(node2)) {
				temp=node2;
				node2=node1;
				node1=temp;			
			}
		}
	}
	
	//创建父节点的方法
	public Node fuNode(Node node1,Node node2) {
		Node fu=new Node ('\u0000',node1.getWeight()+node2.getWeight());
		fu.setLeft(node1);
		fu.setRight(node2);
		return fu;
	}
}

package 链表哈夫曼树二;

public class SingleList {
	
	public static Node head=new Node();
	
		//显示链表
	public void showlist() {
		if(head.getNext()==null) {
			System.out.println("链表为空");
			return;
		}
		Node temp=head.getNext();
		while(true) {		
			//判断是否到链表最后
			if(temp==null) {
				break;
			}
			System.out.println(temp);
			temp=temp.getNext();
		}
	}
	
	//在链表后面直接加入
	public void add(Node node) {
		//因为头结点不能动,因此我们需要一个辅助遍历temp
		Node temp=head;
		//遍历链表,找到最后
		while (true) {
			//找到链表的最后
			if(temp.getNext()==null) {
				break;
			}
			//如果没有找到最后,将temp后移
			//temp=temp.getNext();
			temp=temp.getNext();
		}
		//temp.next=node;
		temp.setNext(node);
	}
	
	//显示链表总共有多少个节点的方法,不算头结点
	public int changdu() {
		Node temp=head;
		int length=0;
		//遍历链表
		while(true) {
			//找到链表的最后
			if(temp.getNext()==null) {
				break;
			}
			//将temp后移,每一次后移加一
			temp=temp.getNext();
			length++;
		}
		return length; 
	}
	
	//找到权值最小的节点方法
//	public Node zuixiaoNode(SingleList singlelist) {
//		if(head.next!=null) {
//			//用来遍历的节点
//			Node temp1=head.next;
//			//用来存储的节点
//			Node temp2=head.next;
//			for(int i=1;i<=singlelist.changdu();i++) {
//				if(temp1.next!=null) {
//				//找到节点的权值大于等于目前的节点的权值,则继续遍历
//				if(temp1.weight>temp1.next.weight) {
//					temp1=temp1.next;
//					continue;
//					}
//				}else {
//					
//					//找到的节点的权值小于目前节点的权值,则将找到的权值小的节点赋值给存储节点
//					temp2=temp1.next;
//					//遍历节点继续往下一个结点走
//					temp1=temp1.next;				
//					
//				}
//			}
//			//返回权值最小的节点
//			return temp2;
//		}else {
//			System.out.println("链表为空,没有节点");
//			return null;
//		}	
		
		
		
		
		
//		if(head.next!=null) {
//			//设置一个存储权值最小的结点
//			Node minnode=head;
//			for(Node temp=head;temp.next!=null;temp=temp.getNext()) {
//				if(temp.weight<minnode.weight) {
//					minnode=temp;
//				}
//			}
//			//执行完这个循环过后,temp指向的是最后一个结点,但是没有跟minnode比较过
//			return minnode;
//		}else {
//			System.out.println("链表为空,没有结点");
//			return null;
//		}
//		//System.out.println("aaa"+head.toString());
//
//		if(head.next!=null) {
//			//设置一个存储权值最小的结点
//			Node minnode=head.next;
//			//设置一个辅助结点来遍历
//			Node temp=head;
//
//			while(temp.next!=null) {
//				if(temp.next.weight<minnode.weight) {
//					minnode=temp.next;
//				}
//				temp=temp.getNext();
//			}
//			//执行完while循环后,temp指向了最后一个结点,而且最后一个结点并没有跟minnode进行比较
//			//这里是temp跟minnode比较
//			if(temp.weight<minnode.weight) {
//				minnode=temp;
//			}
//
//			return minnode;
//		}else {
//			System.out.println("链表为空,没有结点");
//			return null;
//		}
//	}
	
	//取出权值第二小的结点的方法
//	public Node dierxiaoNode(SingleList singlelist) {
//		if(head.next!=null) {
//			//用来遍历的节点
//			Node temp1=head.next;
//			//用来存储的节点
//			Node temp2=head.next;
//			for(int i=1;i<=singlelist.changdu();i++) {
//				if(temp1.next!=null) {
//					
//				
//				//找到节点的权值大于等于目前的节点的权值,则继续遍历
//				if(temp1.weight>temp1.next.weight) {
//					temp1=temp1.next;
//					continue;
//					}
//				}
//				else {
//					//找到的节点的权值小于目前节点的权值,则将找到的权值小的节点赋值给存储节点
//					temp2=temp1.next;
//					//遍历节点继续往下一个结点走
//					temp1=temp1.next;				
//				}		
//			}
//			//目前已经找到了权值最小的节点temp2.
//			//从此基础上继续寻找权值第二小的节点
//			
//			//继续设立一个用来遍历的结点
//			Node temp3=head.next;
//			//继续设立一个用来存储第二小的结点
//			Node temp4=head.next;
//			//标志是否找到第二小的结点
//			boolean flag=false;
//			//开始寻找
//			while(true) {
//				if(temp3.next==null) {//已经到了链表的最后
//					break;
//				}
//				if(temp3.next.weight>temp2.weight) {
//					//把目前找到比  最小权值的结点的权值    大的结点赋值给存储节点
//					temp4=temp3.next;
//					//然后寻找比temp4权值小的结点
//					for(int i=1;i<=singlelist.changdu()-1;i++) {
//						//找到节点的权值大于等于目前的节点的权值,则继续遍历
//						if(temp4.weight>=temp3.next.weight) {
//							temp3=temp3.next;
//							continue;
//						}else {
//							//找到的节点的权值小于目前节点的权值,则将找到的权值小的节点赋值给存储节点
//							temp4=temp3.next;
//							//遍历节点继续往下一个结点走
//							temp3=temp3.next;				
//						}		
//					}			
//				}
//				temp3=temp3.next;//temp后移,遍历
//			}
//			//返回权值第二小的结点
//			return temp4;
//		}else {
//			System.out.println("链表为空,没有节点");
//			return null;
//		}
		
//		if(head.next!=null) {
//			//设置一个存储权值最小的结点
//			Node minnode=head.next;
//			//设置一个辅助结点来遍历
//			Node temp=head;
//			while(temp.next!=null) {
//				if(temp.next.weight<minnode.weight) {
//					minnode=temp;
//				}
//				temp=temp.getNext();
//			}
//			//执行完while循环后,temp指向了最后一个结点,而且最后一个结点并没有跟minnode进行比较
//			//这里是temp跟minnode比较
//			if(temp.weight<minnode.weight) {
//				minnode=temp;
//			}
//			
//			
//			
//			
//			//这里的minnode已经是权值最小的结点
//			//再次设置一个辅助结点来遍历链表
//			Node temp2=head;
//			//再次设置一个存储权值第二小的结点
//			Node secondnode=head.next;
//			
//			while(temp2.next!=null) {
//				if(temp2.next.weight<secondnode.weight&&secondnode.next.weight>minnode.weight) {
//					secondnode=temp2;
//				}
//				temp2=temp2.next;
//			}
//			//执行完while循环后,temp指向了最后一个结点,而且最后一个结点并没有跟minnode进行比较
//			//这里是temp跟minnode比较
//			if(temp2.weight<secondnode.weight&&secondnode.weight>minnode.weight) {
//				secondnode=temp2;
//			}
//			
//			
//			
//			
//			return secondnode;
//		}else {
//			System.out.println("链表为空,没有结点");
//			return null;
//		}
//	}
	
	
	//删除节点的方法
	//找到需要删除的前一个结点
	public void del(Node node) {
		//辅助结点
		Node temp=head;

		boolean flag=false;//标志是否找到待删除的结点

		while(true) {
			if(temp.getNext()==null) {//已经到了链表的最后
				//System.out.println(temp.toString());

				break;
			}
			if(temp.getNext().getData()==node.getData()) {
				//找到的待删除节点的前一个结点temp
				flag=true;
				break;
			}
			temp=temp.getNext();//temp后移,遍历
		}
		//判断flag
		if(flag) {//找到了
			//可以删除
			//temp.next=temp.next.next;
			temp.setNext(temp.getNext().getNext());
		}else {
			System.out.printf("要删除的结点不存在\n");
		}
	}
	
	//按照权值从小到大的顺序来添加结点到链表中的方法
	public void addByweight(Node node) {
		//因为头结点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到
		Node temp=head;

		while(true) {
			if(temp.getNext()==null) {
				//说明temp已经在链表的最后
				break;
			}
			if(temp.getNext().getWeight()>node.getWeight()) {//位置找到了,就在temp的后面插入,权值大于或者等于
				break;
			}
			temp=temp.getNext();//后移,遍历当前链表
		}
		//插入到链表中,temp的后面
		//node.next=temp.next;
		node.setNext(temp.getNext());
		temp.setNext(node);
	}
	
	//取出最小的结点
	public Node getfirstnode(SingleList singlelist) {
		return singlelist.head.getNext();
	}
	
}

package 链表哈夫曼树二;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class wenjian {
	public void stat(File file,SingleList singlelist){
        BufferedReader bfr = null;   						//定义字符读取(缓冲)流
        try{
            bfr = new BufferedReader(new FileReader(file));	//给该流赋值
            String value = null; 							//定义一个临时接收文件中的字符串变量
            String newValue = "";                           //接收文件中所有字符串的变量
            while((value = bfr.readLine()) != null){    	//开始读取文件中的字符
                newValue = newValue + value;    			//存入newValue变量中

            }

            newValue=newValue.replaceAll("\\;","");
            newValue=newValue.replaceAll("\\:","");
            char [] ch=newValue.toCharArray();
            TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>(); //定义一个TreeMap,默认从小到大顺序,键对应字符,值对应字符出现的次数
            for(int x = 0;x < ch.length; x++){   			//遍历ch,将ch中所有的字符存入一个Map集合中(TreeSet),键对应字符,值对应字符出现的次数
                char c = ch[x];
                if(tm.containsKey(c)){  					//如果TreeMap(tm)中有该键,则取出该键中的值,也就是出现的次数
                    int count = tm.get(c);
                    tm.put(c, count + 1);  					//把新值存入tm集合中,如果键相同的话, 新键会替换老键,值也随着变化了
                }
                else{
                    tm.put(c, 1);  							//如果没有出现该键就说明是第一次出现,存入1次
                }
            }
            //下面的是取出TreeMap(tm)中的键和值
            Set<Map.Entry<Character, Integer>> set = tm.entrySet();
            Iterator<Map.Entry<Character, Integer>> iter = set.iterator();
            while(iter.hasNext()){
                Map.Entry<Character, Integer> map = iter.next();
                char k = map.getKey();
                int v = map.getValue();

                Node node=new Node(k,v);
                singlelist.addByweight(node);
            }
            System.out.println();
        }
        catch(IOException e){
            System.out.println("文件读取错误");
        }
        finally{
            try{
                if(bfr!=null)
                    bfr.close();
            }
            catch(IOException e){
                System.out.println("文件关闭错误");
            }
        }
    }
}

package 链表哈夫曼树二;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;


public class wenjianshuzu {
    public static  char[] statshuzu(File file){
        BufferedReader bfr = null;   						//定义字符读取(缓冲)流
        try{
            bfr = new BufferedReader(new FileReader(file));	//给该流赋值
            String value = null; 							//定义一个临时接收文件中的字符串变量
            String newValue = "";                           //接收文件中所有字符串的变量

            while((value = bfr.readLine()) != null){    	//开始读取文件中的字符
                newValue = newValue + value;    			//存入newValue变量中


            }

            newValue=newValue.replaceAll("\\;","");
            newValue=newValue.replaceAll("\\:","");
            char [] ch=newValue.toCharArray();
            return ch;
        }
        catch(IOException e){
            System.out.println("文件读取错误");
        }
        finally{
            try{
                if(bfr!=null)
                    bfr.close();
            }
            catch(IOException e){
                System.out.println("文件关闭错误");
            }
        }
		return null;
    }
}

  • 测试类:
package 链表哈夫曼树二;

import java.io.File;

public class Test {
	 public static SingleList singlelist=new SingleList();
	public static void main(String[] args) {
		
		wenjian file=new wenjian();
		

		file.stat(new File("E:\\Demo.txt"),singlelist);
		
		
		//singlelist.showlist();
		

		System.out.println("--------------------------------------------------");
		Haffmantree haffmantree=new Haffmantree();

		Node haffmantreeRoot=haffmantree.createHaffmanTree(singlelist);
		haffmantreeRoot.preOrder();
		System.out.println("---------------------------------------------------------");
		char zhun[]=wenjianshuzu.statshuzu(new File("E://Demo.txt"));
		//System.out.println(zhun);//输出文件
//		System.out.println(zhun);//已经接受到了文件
		//输出哈夫曼编码
		for(int i=0;i<zhun.length;i++) {
			char pei=zhun[i];
			//System.out.println(pei);//pei也已经读到了数组的固定字符
			haffmantreeRoot.preOrderChazhao(pei, haffmantreeRoot);
		}
		
		System.out.println("\n--------------------------------------------------------------------------");
		//System.out.println(Node.AAA);
		//char huang[]=wenjianshuzu.statshuzu(new File("C:\\Users\\abc61\\Desktop\\Dem0.txt"));
		//把哈夫曼树的含有数据的结点的左右孩子全部指向原本的根节点
		//haffmantreeRoot.lrchildroot(haffmantreeRoot);
		
		
		//翻译特定的哈夫曼编码
		haffmantreeRoot.fanyi("00000111000110111100011111110111010111010110111101100101100011", haffmantreeRoot);
		
		
	}
	

}

上面这个方法如果不使用map,则可以改成这段代码:



	int[] nums = new int[29];
	BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Demo.txt"), "UTF-8"));
String line,rline="";while((line=br.readLine())!=null)
	{

		for (int i = 0; i < line.length(); i++) {
			if (line.charAt(i) == ';' || line.charAt(i) == ':')
				continue;
			if (line.charAt(i) == ' ') {
			nums[26]++;
			continue;
		}
			if (line.charAt(i) == ',') {
				nums[27]++;
				continue;
			}
			if (line.charAt(i) == '.') {
				nums[28]++;
				continue;
			}
			nums[line.charAt(i) - 97]++;// 用ascll的值来放进数组里面
		}
//     for(int i=0;i<line.length();i++) {
//      if(line.charAt(i)==';'||line.charAt(i)==':')continue;
      hs.put(line.charAt(i), hs.get(line.charAt(i))+1);
    } 
		rline += (line + "\n");
	}System.out.println(rline);br.close();for(
	int i = 0;i<nums.length-3;i++)
	{//遍历数组
HuffmanNode2 temp=new HuffmanNode2((char)(i+97),nums[i]);//创建结点
insert(temp); 
}

怎么样?经过这次学习有没有学到很多?~~~希望我的博客能够帮助到你们吖!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值