日撸代码300行(21-30天,树与二叉树)

日撸代码300行(21-30天,树与二叉树)

原文:日撸代码300行(21-30天,树与二叉树)_minfanphd的博客-CSDN博客

21.二叉树的深度遍历的递归实现

package day30.tree;

/**
 * Day:21 二叉树
 *
 * @author pzf
 */
public class BinaryCharTree {
    // attributes
    public char value;
    public BinaryCharTree leftChild, rightChild;

    // contractor
    public BinaryCharTree(char paraValue) {
        this.value = paraValue;
        this.leftChild = null;
        this.rightChild = null;
    }// Of contractor

    /**
     * 先序遍历
     */
    public void preOrder() {
        System.out.print(value + " ");
        if (this.leftChild != null) {
            this.leftChild.preOrder();
        }// Of if
        if (this.rightChild != null) {
            this.rightChild.preOrder();
        }// Of if
    }// Of preOrder

    /**
     * 中序遍历
     */
    public void inOrder() {
        if (this.leftChild != null) {
            this.leftChild.inOrder();
        }// Of if
        System.out.print(value + " ");
        if (this.rightChild != null) {
            this.rightChild.inOrder();
        }// Of if
    }// Of inOrder

    /**
     * 后序遍历
     */
    public void postOrder() {
        if (this.leftChild != null) {
            this.leftChild.postOrder();
        }// Of if
        if (this.rightChild != null) {
            this.rightChild.postOrder();
        }// Of if
        System.out.print(value + " ");
    }// Of postOrder

    /**
     * 生成一个树
     *
     * @return 一个树
     */
    public static BinaryCharTree manualConstructTree() {
        // Step 1. Construct a tree with only one node.
        BinaryCharTree resultTree = new BinaryCharTree('a');

        // Step 2. Construct all nodes. The first node is the root.
        // BinaryCharTreeNode tempTreeA = resultTree.root;
        BinaryCharTree tempTreeB = new BinaryCharTree('b');
        BinaryCharTree tempTreeC = new BinaryCharTree('c');
        BinaryCharTree tempTreeD = new BinaryCharTree('d');
        BinaryCharTree tempTreeE = new BinaryCharTree('e');
        BinaryCharTree tempTreeF = new BinaryCharTree('f');
        BinaryCharTree tempTreeG = new BinaryCharTree('g');

        // Step 3. Link all nodes.
        resultTree.leftChild = tempTreeB;
        resultTree.rightChild = tempTreeC;
        tempTreeB.rightChild = tempTreeD;
        tempTreeC.leftChild = tempTreeE;
        tempTreeD.leftChild = tempTreeF;
        tempTreeD.rightChild = tempTreeG;

        return resultTree;
    }

    /**
     * 获取树深度
     *
     * @return int 树深度
     */
    public int getDepth() {
        if (this.leftChild == null && this.rightChild == null) {
            return 1;
        }// Of if
        if (this.leftChild == null) {
            return this.rightChild.getDepth() + 1;
        }// Of if
        if (this.rightChild == null) {
            return this.leftChild.getDepth() + 1;
        }// Of if
        return 1 + Math.max(this.leftChild.getDepth(), this.rightChild.getDepth());
    }// Of getDepth

    /**
     * 获取节点数
     *
     * @return int 节点总数
     */
    public int getNum() {
        // leaf
        if (this.leftChild == null && this.rightChild == null) {
            return 1;
        }// Of if

        int leftCount = 0;
        if (this.leftChild != null) {
            leftCount =  this.leftChild.getNum();
        }// Of if
        int rightCount = 0;
        if(this.rightChild != null){
            rightCount = this.rightChild.getNum();
        }// Of if
        return leftCount + rightCount + 1;
    }// Of getNum

    /**
     * main
     *
     * @param args
     */
    public static void main(String[] args) {
        BinaryCharTree btree = manualConstructTree();
        btree.preOrder();
        System.out.println("\n");
        btree.inOrder();
        System.out.println("\n");
        btree.postOrder();
        System.out.println("\n");
        System.out.println(btree.getDepth());
        System.out.println(btree.getNum());
    }// Of main
}// Of class BinaryCharTree

生成树的方法直接摘抄原文闵老师的,创建的二叉树如下:
在这里插入图片描述
测试运行结果:在这里插入图片描述

22.二叉树的存储

1.Object版循环队列

package day20;

/**
 * Object版本队列
 */
public class AnyQueue {
    public final int MAX_LENGTH = 10;
    int head,tail;
    Object[] data;

    public AnyQueue(){
        data = new Object[MAX_LENGTH];
        head = 0;
        tail = 0;
    }

    public boolean enqueue(Object paraObject){
        if((tail + 1) % MAX_LENGTH == head){
            System.out.println("Full");
            return false;
        }

        data[tail % MAX_LENGTH] = paraObject;
        tail = (tail + 1) % MAX_LENGTH;
        return true;
    }

    public Object dequeue(){
        if( head == tail ){
            System.out.println("Empty");
            return null;
        }

        Object res = data[head % MAX_LENGTH];
        head = (head + 1) % MAX_LENGTH;
        return res;
    }

    public String toString(){
        String resultString = "";

        if (head == tail) {
            return "empty";
        } // Of if

        for (int i = head; i < tail; i++) {
            resultString += data[i % MAX_LENGTH] + ", ";
        } // Of for i
        resultString = resultString.substring(0,resultString.length()-2);

        return resultString;
    }


}

2.储存

	char[] valuesArray;
    int [] indicesArray;

    /**
     * 用2个数组储存二叉树
     * eg. [a, b, c, d, e, f, g]
     *     [0, 1, 2, 4, 5, 9, 10]
     */
    public void toDataArrays(){
        int tempLength = this.getNum();

        valuesArray = new char[tempLength];
        indicesArray = new int[tempLength];
        int i=0;

        AnyQueue tempQueue = new AnyQueue();
        CircleQueue tempIntQueue = new CircleQueue();

        // root
        tempQueue.enqueue(this);
        tempIntQueue.enqueue(0);

        // tempTree : root
        BinaryCharTree tempTree = (BinaryCharTree) tempQueue.dequeue(); // Object -> BinaryCharTree
        int tempIndex = tempIntQueue.dequeue(); // tempIndex = 0

        while(tempTree != null){
            valuesArray[i] = tempTree.value;
            indicesArray[i] = tempIndex;
            i++;

            // levelOrder
            if(tempTree.leftChild != null){
                tempQueue.enqueue(tempTree.leftChild);
                tempIntQueue.enqueue(2 * tempIndex + 1);
            }// Of if
            if(tempTree.rightChild != null){
                tempQueue.enqueue((tempTree.rightChild));
                tempIntQueue.enqueue(2 * tempIndex + 2);
            }// Of if

            tempTree = (BinaryCharTree) tempQueue.dequeue();
            tempIndex = tempIntQueue.dequeue();

        }// Of while
    }// Of toDataArrays
    
		/**
		* main
		*/
        public static void main(String[] args) {
        BinaryCharTree btree = manualConstructTree();
        btree.toDataArrays();
        System.out.println(Arrays.toString(btree.valuesArray));
        System.out.println(Arrays.toString(btree.indicesArray));
    }// Of main

此方法及2个全局变量在day21的类中。
在这里插入图片描述
小结:
1.第一遍看闵老师的代码都没想通,最后醒悟是因为想起来了左孩子的下标是(节点下标 * 2 - 1)
2.在Day20的总结里面,最后那个问题是用通配符解决的,为了保持一致,直接抄了那个Object版的队列(懒).
然后用通配符的那个版本试了试,没有问题,代码如下:

	char[] valuesArray;
    int [] indicesArray;

    /**
     * 用2个数组储存二叉树
     * eg. [a, b, c, d, e, f, g]
     *     [0, 1, 2, 4, 5, 9, 10]
     */
    public void toDataArrays(){
        int tempLength = this.getNum();

        valuesArray = new char[tempLength];
        indicesArray = new int[tempLength];
        int i=0;


        CircleAnyQueue<TreeVersionTest> testQueue = new CircleAnyQueue<>();
        CircleAnyQueue<Integer> testIntQueue = new CircleAnyQueue<>();

        // root
        testQueue.enqueue(this);
        testIntQueue.enqueue(0);

        // tempTree : root

        TreeVersionTest tempTree = testQueue.dequeue();
        // Integer!!
        Integer tempIndex = testIntQueue.dequeue(); // tempIndex = 0

        while(tempTree != null){
            valuesArray[i] = tempTree.value;
            indicesArray[i] = tempIndex;
            i++;

            // levelOrder
            if(tempTree.leftChild != null){
                testQueue.enqueue(tempTree.leftChild);
                testIntQueue.enqueue(2 * tempIndex + 1);
            }// Of if
            if(tempTree.rightChild != null){
                testQueue.enqueue(tempTree.rightChild);
                testIntQueue.enqueue(2 * tempIndex + 2);
            }// Of if

            tempTree = testQueue.dequeue();
            tempIndex = testIntQueue.dequeue();

        }// Of while
    }// Of toDataArrays

23.使用具有通用性的队列

和之前的代码一样,通过2种办法实现了通用性的队列。
1.使用Object类,所有类都是Object类的子类
2.泛型,通配符

今天没有什么代码,比较一下这两种方法:
1.通配符用于数组属性时,在构造方法里初始化会遇到问题。我的解决办法是把数组换成ArrayList.

public class CompareObject<T> {
        T[] name;
        ArrayList<T> title;
        Object[] voice;

        public CompareObject(){
            this.name =  new T[2]; // 报错 不可以用T创建数组
            this.title = new ArrayList<>();
            this.voice = new Object[5];
    }
public static void main(String[] args) {
            CompareObject<Integer> a = new CompareObject<>();
    }
}

2.使用Object类,需要转型,通配符不用。

	public Object getO() {
        return voice[0];
    }
    public T getT(){
        return this.title.get(0);
    }
	public static void main(String[] args) {
        CompareObject<Integer> a = new CompareObject<>();
        
        a.voice[0] = 1;
        int b = (int) a.getO();
        a.voice[0] = "String";
        String c = (String) a.getO();
        
        Integer d = a.getT();
    }

24.二叉树的建立

    /**
     * 构造方法
     *
     * @param paraDataArray 装value的数组
     * @param paraIndicesArray 装下标的数组
     */
    public BinaryCharTree(char[] paraDataArray, int[] paraIndicesArray){
        // 1.储存节点
        int tempNumNodes = paraDataArray.length;
        BinaryCharTree[] tempAllNodes = new BinaryCharTree[tempNumNodes ];
        for (int i = 0; i < tempNumNodes ; i++) {
            // 用value构造,tempAllNodes[]里放了所有节点
            tempAllNodes[i] = new BinaryCharTree(paraDataArray[i]);
        }// Of for

        // 2.连接节点
        for (int i = 1; i < tempNumNodes; i++) {
            // j是用来找父节点的
            for (int j = 0; j < i; j++) {
                // 谁的leftchild 是 i
                if(paraIndicesArray[j] * 2 + 1 == paraIndicesArray[i]){
                    tempAllNodes[j].leftChild = tempAllNodes[i];
                    break;
                }else if(paraIndicesArray[j] * 2 + 2 == paraIndicesArray[i]){
                    tempAllNodes[j].rightChild = tempAllNodes[i];
                    break;
                }// Of if
            }// Of for j
        }// Of for i

        value = tempAllNodes[0].value;
        leftChild = tempAllNodes[0].leftChild;
        rightChild = tempAllNodes[0].rightChild;

    }// Of contractor

这是一个构造方法,这个2层的for循环,是第一层从第二个节点开始,用第二层其之前遍历,看tempAllNodes[j]这个节点是tempAllNodes[i]的左还是右孩子,属于找父亲的思路。
我写的时候想的是第一层循环遍历节点,第二层循环从i+1开始,找孩子。一开始模仿闵老师代码犯了个错,在找到左孩子后就加了break,测试的时候发现只有左子树。修改后如下:

for (int i = 0; i < tempNumNodes; i++) {
            for (int j = i+1; j < tempNumNodes; j++) {
                if(paraIndicesArray[i] * 2 + 1 == paraIndicesArray[j]){
                    tempAllNodes[i].leftChild = tempAllNodes[j];
                }else if(paraIndicesArray[i] * 2 + 2 == paraIndicesArray[j]){
                    tempAllNodes[i].rightChild = tempAllNodes[j];
                    break;
                }// Of if
            }// Of for j
        }// Of for i

属于找孩子的思路。

25.二叉树深度遍历的栈实现 (中序)

1.Object 通用栈

package day30.stack;

/**
 *  Object 通用栈
 *
 * @author pzf
 */
public class ObjectStack {
    // 变量
    public static final int MAX_DEPTH = 10;
    int depth;
    Object[] data;

    // 构造方法
    public ObjectStack() {
        depth = 0;
        data = new Object[MAX_DEPTH];
    }// Of the first constructor

    // toString 重写
    public String toString() {
        String resultString = "";
        for (int i = 0; i < depth; i++) {
            resultString += data[i];
        } // Of for i

        return resultString;
    }// Of toString

    /**
     * push 入栈
     *
     * @param paraObject 入栈元素
     * @return true of false.
     */
    public boolean push(Object paraObject) {
        if (depth == MAX_DEPTH) {
            System.out.println("Stack full.");
            return false;
        } // Of if

        data[depth] = paraObject;
        depth++;

        return true;
    }// Of push

    /**
     * 出栈
     *
     * @return 出栈元素
     */
    public Object pop() {
        if (depth == 0) {
            System.out.println("Nothing to pop.");
            return '\0';
        } // of if

        Object resultObject = data[depth - 1];
        depth--;

        return resultObject;
    }// Of pop

    /**
     * 判空
     *
     * @return true or false.
     */
    public boolean isEmpty(){
        return depth == 0;
    }
}

  1. 非递归中序遍历
	/**
     * 非递归中序遍历
     */
    public void inOrderWithStack() {
        BinaryCharTree tempTree = this;
        ObjectStack tempStack = new ObjectStack();

        while (tempTree != null || !tempStack.isEmpty()) {
            if(tempTree != null){
                tempStack.push(tempTree);
                tempTree = tempTree.leftChild;
            }else{
                tempTree = (BinaryCharTree) tempStack.pop();
                System.out.print(tempTree.value + " "); // visit
                tempTree = tempTree.rightChild;
            }// Of if
        }// Of while
    }// Of inOrderWithStack

26.二叉树深度遍历的栈实现 (前序和后序)

/**
     * 非递归前序遍历
     */
    public void preOrderWithStack() {
        BinaryCharTree tempTree = this;
        ObjectStack tempStack = new ObjectStack();

        while (tempTree != null || !tempStack.isEmpty()) {
            if(tempTree != null){
                tempStack.push(tempTree);
                System.out.print(tempTree.value + " "); // visit
                tempTree = tempTree.leftChild;
            }else{
                tempTree = (BinaryCharTree) tempStack.pop();
                tempTree = tempTree.rightChild;
            }// Of if
        }// Of while
        System.out.println("\n");
    }// Of inOrderWithStack

    /**
     * 后序遍历,非递归
     */
    public void postOrderWithStack(){
        BinaryCharTree tempTree = this;
        ObjectStack tempStack = new ObjectStack();
        ObjectStack tempValueStack = new ObjectStack();
        //1.中左右 -> 中右左
        while(tempTree != null || !tempStack.isEmpty()){
            if (tempTree != null){
                // 2. 入栈
                tempStack.push(tempTree);
                tempValueStack.push(tempTree.value);
                tempTree = tempTree.rightChild;
            }else{
                tempTree = (BinaryCharTree) tempStack.pop();
                tempTree = tempTree.leftChild;
            }// Of if
        }// Of while
        // 3.输出栈 倒序 中右左->左右中
        while (!tempValueStack.isEmpty()){
            System.out.print(""+ tempValueStack.pop() +" ");
        }// Of while
        System.out.println("\n");
    }// Of postOrderWithStack

27.Hanoi 塔问题

Hanoi塔的问题很像那个楼梯题:一次只能上一层或者两层楼梯,问n层楼梯一共有多少种走法。就是n-1层的走法加上n-2层的走法。
Hanoi塔也一样,举个例子,1个碟只要1步,2个碟要3步(最大的碟移动一次,小的2次),3个碟可以把上面2个碟子看作一个整体(小),也就是2次整体+1,2个碟的移动是三步,也就是 2 ∗ ( 2 + 1 ) + 1 = 7 2*(2+1) + 1=7 2(2+1)+1=7;同理, 4个碟就把上面3个看成一个整体,步数是 2 ∗ 7 + 1 = 15 2*7+1=15 27+1=15
设n个碟的步数为 S ( n ) S(n) S(n) S ( n ) = S ( n − 1 ) ∗ 2 + 1 , S ( 1 ) = 1 S(n)=S(n-1) *2+1,S(1)=1 S(n)=S(n1)2+1,S(1)=1

现在来分析一下输出每一步的思路,从左至右设底座为 a , b , c , a,b,c, a,b,c,从2个碟的例子出发,分为三步
1. a → b a\rarr b ab
2. a → c a\rarr c ac
3. b → c b\rarr c bc
在这里插入图片描述
再看3个碟的情况,把上面2个看做整体,就和2个碟的情况一样,整体的移动也是一次2个碟的移动,不过是把 c c c当作 b b b,所以总移动次数是 3+1+3=7

在这里插入图片描述

综上,可以把n个碟的情况分成三步:
1.除最大的碟以外(共n-1个)移动到 b b b
2.最大的碟移动到目标区域 c c c
3.剩下的n-1个碟移动到 c c c.

所以,递归代码如下:

package day30.tree;

/**
 * Hanoi
 *
 * @author pzf
 */
public class Hanoi {
    /**
     *
     * @param a 起始点
     * @param b 中转点
     * @param c 目标点
     * @param n 数量
     */
    public static void hanoi(char a, char b, char c,
                             int n) {
        // 不接受0和负数个,且不接受太大的n,因为O(n^2),递归太多
        if(n<=0 || n>20){
            System.out.println("Invalid input");
            return;
        }
        
        // 1个碟的情况,直接a->c
        if (n == 1) {
            System.out.println(a + "->" + c);
            return;
        } // Of if

        // 第一步. 把上面n-1个当作整体,移动到b
        hanoi(a, c, b, n - 1);
        
        // 第二步. 把最大的从a移动到c
        //hanoi(a,b,c,1) -> (n==1) -> print( a->c )
        System.out.println(a + "->" + c);
        
        // 第三步. 把第一步移动到b的n-1个移动到c
        hanoi(b, a, c, n - 1);
    }// Of hanoi

    public static void main(String[] args) {
        hanoi('a', 'b', 'c', 4);
    }// Of main
    
}

28-30. Huffman

package day30.tree;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * Day27-30 Huffman
 *
 * @author pzf
 */
public class Huffman {
    /**
     * Huffman节点类
     */
    class HuffmanNode {
        // 变量
        char character; // 节点字符
        int weight;     // 权值
        HuffmanNode leftChild; // 左孩子
        HuffmanNode rightChild;// 右孩子
        HuffmanNode parent;    // 父节点

        /**
         * 构造方法
         *
         * @param character  字符
         * @param weight     权
         * @param leftChild  左孩子
         * @param rightChild 右孩子
         * @param parent     父节点
         */
        public HuffmanNode(char character, int weight, HuffmanNode leftChild, HuffmanNode rightChild,
                           HuffmanNode parent) {
            this.character = character;
            this.weight = weight;
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.parent = parent;
        }// Of contractor

        /**
         * 重写toString
         *
         * @return 字符串
         */
        public String toString() {
            String resultString = "(" + character + ", " + weight + ")";

            return resultString;
        }// Of toString
    }// Of class HuffmanNode

    // 常量 ASCII 256字符 128+128
    public static final int NUM_CHARS = 256;
    // 变量
    String inputText; // 文本内容
    int alphabetLength; // 字符表长度
    char[] alphabet; // 字符表
    int[] charCounts; // 计数表
    int[] charMapping; // 索引表
    String[] huffmanCodes;// 储存霍夫曼编码
    HuffmanNode[] nodes;// 储存霍夫曼树节点

    /**
     * 构造方法
     *
     * @param paraFilename 文件地址
     */
    public Huffman(String paraFilename) {
        charMapping = new int[NUM_CHARS];

        readText(paraFilename);
    }// Of contractor

    /**
     * 读取文件 ,并输出内容
     *
     * @param paraFilename 文件地址
     */
    public void readText(String paraFilename) {
        try {
            inputText =
                    Files.newBufferedReader(Paths.get(paraFilename), StandardCharsets.UTF_8)
                            .lines().collect(Collectors.joining("\n"));
        } catch (IOException e) {
            e.printStackTrace();
        }// Of try
        System.out.println("The text is:\r\n" + inputText);
    }// Of readText

    /**
     * 构建字符表
     */
    public void constructAlphabet() {
        // 初始化
        Arrays.fill(charMapping, -1);

        int[] tempCharCounts = new int[NUM_CHARS];
        int tempCharIndex;

        char tempChar;
        // 1.遍历文档,记录每个字符的次数
        for (int i = 0; i < inputText.length(); i++) {
            tempChar = inputText.charAt(i);
            tempCharIndex = (int) tempChar;

            //System.out.print("" + tempCharIndex + " ");

            tempCharCounts[tempCharIndex]++;
        }// Of for

        // 2.决定长度
        alphabetLength = 0;
        for (int i = 0; i < 255; i++) {
            if (tempCharCounts[i] > 0) {
                alphabetLength++;
            }// Of if
        }// Of for

        // 3.生成alphabet
        alphabet = new char[alphabetLength];
        charCounts = new int[2 * alphabetLength - 1];

        int tempCounter = 0;
        for (int i = 0; i < NUM_CHARS; i++) {
            if (tempCharCounts[i] > 0) {
                //alphabet 按ASCII正序
                alphabet[tempCounter] = (char) i;
                // alphabet对应的字符出现的次数
                charCounts[tempCounter] = tempCharCounts[i];
                // ASCII在alphabet中的索引
                charMapping[i] = tempCounter;
                tempCounter++;
            }// Of if
        }// Of for


        System.out.println("The alphabet is: " + Arrays.toString(alphabet));
        System.out.println("Their counts are: " + Arrays.toString(charCounts));
        System.out.println("The char mappings are: " + Arrays.toString(charMapping));
    }// Of constructAlphabet

    /**
     * 建树
     */
    public void contrastTree() {
        // 1.开辟空间
        nodes = new HuffmanNode[alphabetLength * 2 - 1];
        boolean[] tempProcessed = new boolean[alphabetLength * 2 - 1];

        // 2.生成节点们
        for (int i = 0; i < alphabetLength; i++) {
            nodes[i] = new HuffmanNode(alphabet[i], charCounts[i], null, null, null);
        }// Of for

        // 3.建树
        int tempLeft, tempRight, tempMinimal;
        for (int i = alphabetLength; i < 2 * alphabetLength - 1; i++) {
            // 3.1 找最小的作为左子树
            tempLeft = this.getMinPos(tempProcessed, i);
            // 3.2 找第二小的作为右子树
            tempRight = this.getMinPos(tempProcessed, i);

            System.out.println("Selecting " + tempLeft + " and " + tempRight);

            // 3.3 生成新的父节点
            charCounts[i] = charCounts[tempLeft] + charCounts[tempRight];
            nodes[i] = new HuffmanNode('*', charCounts[i], nodes[tempLeft], nodes[tempRight], null);

            // 3.4 链接
            nodes[tempLeft].parent = nodes[i];
            nodes[tempRight].parent = nodes[i];
            System.out.println("The children of " + i + " are " + tempLeft + " and " + tempRight);
        }// Of for
    }// Of contrastTree

    /**
     * 找权最小的节点
     *
     * @param paraProcessed 标记数组,标记已经被选过的
     * @param paraLimit     查找范围的最大值
     * @return 权最小节点在charCounts[]中的下标
     */
    public int getMinPos(boolean[] paraProcessed, int paraLimit) {
        int tempRes = -1;
        int tempMinimal = Integer.MAX_VALUE;
        for (int j = 0; j < paraLimit; j++) {
            if (paraProcessed[j]) {
                continue;
            }// Of if
            if (tempMinimal > charCounts[j]) {
                tempMinimal = charCounts[j];
                tempRes = j;
            }// Of if
        }// Of for j
        paraProcessed[tempRes] = true;
        return tempRes;
    }// Of getMinPos

    /**
     * 获取根节点
     *
     * @return 根节点
     */
    public HuffmanNode getRoot() {
        return nodes[nodes.length - 1];
    }

    /**
     * 先序遍历霍夫曼树
     *
     * @param paraNode 起始节点
     */
    public void preOrderVisit(HuffmanNode paraNode) {
        System.out.println("(" + paraNode.character + " , " + paraNode.weight + ")");
        if (paraNode.leftChild != null) {
            preOrderVisit(paraNode.leftChild);
        }
        if (paraNode.rightChild != null) {
            preOrderVisit(paraNode.rightChild);
        }
    }

    /**
     * 生成编码 生成如:T 01000
     */
    public void generateCodes() {
        huffmanCodes = new String[alphabetLength];
        // 单节点
        if (alphabetLength == 1) {
            System.out.println("The code of " + alphabet[0] + " is " + "1");
        } else {
            HuffmanNode tempNode;
            for (int i = 0; i < alphabetLength; i++) {
                tempNode = nodes[i];
                String tempCharCode = "";
                // 自下往上
                while (tempNode.parent != null) {
                    if (tempNode == tempNode.parent.leftChild) {
                        tempCharCode = "0" + tempCharCode;
                    } else {
                        tempCharCode = "1" + tempCharCode;
                    }// Of if

                    tempNode = tempNode.parent;
                }// Of while
                // tempCharCode : 00110
                huffmanCodes[i] = tempCharCode;
                System.out.println("The code of " + alphabet[i] + " is " + tempCharCode);
            }// Of for
        }// Of if
    }// Of generateCodes

    /**
     * 生成字符串的编码,如果文件中不存在该字符串,输出not exist
     *
     * @param paraString 字符串
     * @return 编码
     */
    public String coding(String paraString) {
        String resultCodeString = "";

        int tempIndex;
        for (int i = 0; i < paraString.length(); i++) {
            // 获取ASCII
            tempIndex = charMapping[(int) paraString.charAt(i)];

            // 防止越界
            if (tempIndex == -1) {
                //System.out.println("Not exist");
                return "Not exist";
            }// Of if
            // 编码
            resultCodeString += huffmanCodes[tempIndex];
        } // Of for i
        return resultCodeString;
    }// Of coding

    /**
     * 单节点的解码
     *
     * @param paraString 编码
     * @return 解码结果
     */
    public String decodingSingle(String paraString) {
        String res = "";
        char tempValue = getRoot().character;

        for (int i = 0; i < paraString.length(); i++) {
            char tempChar = paraString.charAt(i);
            if (tempChar != '0' && tempChar != '1') {
                return "Invalid input";
            } else {
                if (tempChar == '1') {
                    res += tempValue;
                } else {
                    System.out.println("extra input:" + paraString.substring(i));
                    break;
                }// Of if
            }// Of if
        }// Of for
        return res;
    }// Of decodingSingle

    /**
     * 解码
     * 
     * @param paraString 给定的编码
     * @return 解码结果字符串
     */
    public String decoding(String paraString) {
        // 单节点情况
        if (this.alphabetLength == 1) {
            return decodingSingle(paraString);
        }// Of if

        String resString = "";
        HuffmanNode tempNode = getRoot();
        int tempMark = 0;
        for (int i = 0; i < paraString.length(); i++) {
            char tempChar = paraString.charAt(i);
            // 只接受0,1
            if (tempChar != '0' && tempChar != '1') {
                return "Invalid input";
            } else {
                if (tempChar == '0') {
                    tempNode = tempNode.leftChild;
                } else {
                    tempNode = tempNode.rightChild;
                }// Of if

                // 到叶节点(是初始节点,有值)
                if (tempNode.leftChild == null) {
                    resString += tempNode.character;
                    tempMark = i; // 记录上一个完成解码的位置

                    // 防止刚好无多余编码的时候 输出多余编码时越界
                    if (i != paraString.length() - 1) {
                        tempNode = getRoot();
                    }// Of if
                }// Of if
            }// Of if
        }// Of for

        // 有多余编码
        if (tempNode.leftChild != null) {
            System.out.println("extra input:" + paraString.substring(tempMark + 1));
        }// Of if
        return resString;
    }// Of decoding

    public static void main(String[] args) {
        Huffman huffman = new Huffman("E:\\Master\\Day90\\src\\files\\singleLetter");
        huffman.constructAlphabet();
        huffman.contrastTree();
        //huffman.preOrderVisit(huffman.getRoot());
        huffman.generateCodes();
        System.out.println(huffman.coding("Hello"));
        System.out.println(huffman.decoding("111001"));

    }// Of main
}// Of class Huffman

说明:decoding()方法中,增加了一些输入合法性的判断、对多余编码的输出和对单节点的处理。
generateCodes()也增加了对单节点的处理

举例:

情况1:节点数大于1
The text is:
aaaaabbcd

The alphabet is: [a, b, c, d]
Their counts are: [5, 2, 1, 1, 0, 0, 0]

The code of a is 1
The code of b is 00
The code of c is 010
The code of d is 011

// 测试

huffman.decoding("1110010");
// 返回的字符串:aaaba (1 1 1 00 1) extra:(0)
huffman.decoding("0001");
// 返回的字符串:b (00) extra:(01)        // 不会跳过0再输出1

情况2:节点数为1
The text is:
a
The alphabet is: [a]
Their counts are: [1]

The code of a is 1

huffman.decoding("1110010");
// 返回的字符串:aaa( 1 1 1 ) extra:(0010)


原文:日撸代码300行(21-30天,树与二叉树)_minfanphd的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值