1. 程式人生 > >java實現樹的一般操作

java實現樹的一般操作

表示 rom class ado span .get poll() tno 2層

樹是數據結構中最基本的結構,今天的博客更新一下樹的基本操作:

樹的節點結構:

package tree;  
  
  
/**   
* TreeNode: 普通的樹節點 
* @author xuejupo  [email protected]  
* create in 2015-11-19 下午5:30:31   
*     
*/  
public class TreeNode<T> {  
    T value;  
          
    TreeNode<T> leftChild;  
    TreeNode<T> rightChild;  
  
    TreeNode(T value) {  
        
this.value = value; } TreeNode() { } /** 增加左子節點 * addLeft: * @param value * void 返回類型 */ public void addLeft(T value){ TreeNode<T> leftChild = new TreeNode<T>(value); this.leftChild = leftChild; }
/** * addRight: 增加右子節點 * @param value * void 返回類型 */ public void addRight(T value){ TreeNode<T> rightChild = new TreeNode<T>(value); this.rightChild = rightChild; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) * 重載equal方法
*/ @Override public boolean equals(Object obj) { // TODO Auto-generated method stub if(!(obj instanceof TreeNode)){ return false; } return this.value.equals(((TreeNode<?>)obj).value); } /* (non-Javadoc) * @see java.lang.Object#hashCode() * 重載hashCode方法 */ @Override public int hashCode() { // TODO Auto-generated method stub return this.value.hashCode(); } @Override public String toString(){ return this.value==null?"":this.value.toString(); } }

樹的基本操作類:

package tree;  
  
import java.util.ArrayList;  
import java.util.LinkedList;  
import java.util.List;  
import java.util.Queue;  
  
/** 
 * TreeTools:樹的操作類 
 *  
 * @author xuejupo [email protected] 
 *  
 *         create in 2015-11-19 下午5:31:05 
 *  
 */  
public class TreeTools {  
  
    /** 
     * getTreeNum: 判斷樹中節點個數 
     *  
     * @param root 
     *            根節點 
     * @return int 返回類型 
     */  
    public static <T> int getTreeNum(TreeNode<T> root) {  
        if (root == null) {  
            return 0;  
        }  
        return getTreeNum(root.leftChild) + getTreeNum(root.rightChild) + 1;  
    }  
  
    /** 
     * getTreeDepth: 判斷樹的深度 
     *  
     * @param root 
     *            根節點 
     * @return int 返回類型 
     */  
    public static <T> int getTreeDepth(TreeNode<T> root) {  
        if (root == null) {  
            return 0;  
        }  
        int leftDepth = getTreeDepth(root.leftChild) + 1;  
        int rightDepth = getTreeDepth(root.rightChild) + 1;  
        return Math.max(leftDepth, rightDepth);  
    }  
  
    /** 
     * preOrderTravel: 前序遍歷 
     *  
     * @param root 
     *            void 返回類型 
     */  
    public static <T> void preOrderTravel(TreeNode<T> root) {  
        if (root == null) {  
            return;  
        }  
        visitNode(root);  
        preOrderTravel(root.leftChild);  
        preOrderTravel(root.rightChild);  
    }  
  
    /** 
     * midOrderTravel: 中序遍歷 
     *  
     * @param root 
     *            void 返回類型 
     */  
    public static <T> void midOrderTravel(TreeNode<T> root) {  
        if (root == null) {  
            return;  
        }  
        midOrderTravel(root.leftChild);  
        visitNode(root);  
        midOrderTravel(root.rightChild);  
    }  
  
    /** 
     * backOrderTravel: 後序遍歷 
     *  
     * @param root 
     *            void 返回類型 
     */  
    public static <T> void backOrderTravel(TreeNode<T> root) {  
        if (root == null) {  
            return;  
        }  
        backOrderTravel(root.leftChild);  
        backOrderTravel(root.rightChild);  
        visitNode(root);  
    }  
  
    /** 
     * visitNode: 訪問node節點 
     *  
     * @param node 
     *            void 返回類型 
     */  
    private static <T> void visitNode(TreeNode<T> node) {  
        System.out.print(node.value + "\t");  
    }  
  
    /** 
     * levelTravel: 分層遍歷 
     *  
     * @param root 
     *            void 返回類型 
     */  
    public static <T> void levelTravel(TreeNode<T> root) {  
        Queue<TreeNode<T>> q = new LinkedList<TreeNode<T>>();  
        q.offer(root);  
        while (!q.isEmpty()) {  
            TreeNode<T> temp = q.poll();  
            visitNode(temp);  
            if (temp.leftChild != null) {  
                q.offer(temp.leftChild);  
            }  
            if (temp.rightChild != null) {  
                q.offer(temp.rightChild);  
            }  
        }  
    }  
  
    /** 
     * getNumForKlevel: 求第K層節點個數 
     *  
     * @param root 
     * @param k 
     * @return int 返回類型 
     */  
    public static <T> int getNumForKlevel(TreeNode<T> root, int k) {  
        if (root == null || k < 1) {  
            return 0;  
        }  
        if (k == 1) {  
            return 1;  
        }  
        int leftNum = getNumForKlevel(root.leftChild, k - 1);  
        int rightNum = getNumForKlevel(root.rightChild, k - 1);  
        return leftNum + rightNum;  
    }  
  
    /** 
     * getLeafNum: 求二叉樹中葉子節點的個數 
     *  
     * @param root 
     * @return int 返回類型 
     */  
    public static <T> int getLeafNum(TreeNode<T> root) {  
        if (root == null) {  
            return 0;  
        }  
        if (root.leftChild == null && root.rightChild == null) {  
            return 1;  
        }  
        int leftNum = getLeafNum(root.leftChild);  
        int rightNum = getLeafNum(root.rightChild);  
        return leftNum + rightNum;  
    }  
  
    /** 
     * exchange: 交換根節點的左右子樹 
     *  
     * @param root 
     * @return TreeNode 返回類型 
     */  
    public static <T> TreeNode<T> exchange(TreeNode<T> root) {  
        if (root == null) {  
            return null;  
        }  
        TreeNode<T> left = exchange(root.leftChild);  
        TreeNode<T> right = exchange(root.rightChild);  
        root.leftChild = right;  
        root.rightChild = left;  
        return root;  
    }  
  
    /** 
     * nodeIsChild: 查看node是否是root的子節點 
     *  
     * @param root 
     * @param node 
     * @return boolean 返回類型 
     */  
    public static <T> boolean nodeIsChild(TreeNode<T> root, TreeNode<T> node) {  
        if (root == null || node == null) {  
            return false;  
        }  
        if (root == node) {  
            return true;  
        }  
        boolean isFind = nodeIsChild(root.leftChild, node);  
        if (!isFind) {  
            isFind = nodeIsChild(root.rightChild, node);  
        }  
        return isFind;  
    }  
  
    /** 
     * findAllFatherNode: 返回兩個節點lnode和rnode的以root為根節點的公共父節點 
     *  
     * @param root 
     *            根節點 
     * @param lNode 
     * @param rNode 
     * @return TreeNode 返回類型 
     */  
    public static <T> TreeNode<T> findAllFatherNode(TreeNode<T> root,  
            TreeNode<T> lNode, TreeNode<T> rNode) {  
        if (lNode == root || rNode == root) {  
            return root;  
        }  
        if (root == null || lNode == null || rNode == null) {  
            return null;  
        }  
        // 如果lNode是左子樹的節點  
        if (nodeIsChild(root.leftChild, lNode)) {  
            if (nodeIsChild(root.rightChild, rNode)) {  
                return root;  
            } else {  
                return findAllFatherNode(root.leftChild, lNode, rNode);  
            }  
        } else {  
            if (nodeIsChild(root.leftChild, rNode)) {  
                return root;  
            } else {  
                return findAllFatherNode(root.rightChild, lNode, rNode);  
            }  
        }  
    }  
  
    /** 
     * getTreeFromPreAndMid: 根據前序和中序構建二叉樹 
     *  
     * @param pre 
     *            前序序列 
     * @param mid 
     *            中序序列 
     * @return TreeNode 返回類型 
     */  
    public static <T> TreeNode<T> getTreeFromPreAndMid(List<T> pre, List<T> mid) {  
        if (pre == null || mid == null || pre.size() == 0 || mid.size() == 0) {  
            return null;  
        }  
        if (pre.size() == 1) {  
            return new TreeNode<T>(pre.get(0));  
        }  
        TreeNode<T> root = new TreeNode<T>(pre.get(0));  
        // 找出根節點在中序中的位置  
        int index = 0;  
        while (!mid.get(index++).equals(pre.get(0))) {  
        }  
        // 構建左子樹的前序  
        List<T> preLeft = new ArrayList<T>(index);  
        // 左子樹的中序  
        List<T> midLeft = new ArrayList<T>(index);  
        for (int i = 1; i < index; i++) {  
            preLeft.add(pre.get(i));  
        }  
        for (int i = 0; i < index - 1; i++) {  
            midLeft.add(mid.get(i));  
        }  
  
        // 重建左子樹  
        root.leftChild = getTreeFromPreAndMid(preLeft, midLeft);  
        // 右子樹的前序  
        List<T> preRight = new ArrayList<T>(pre.size() - index - 1);  
        // 右子樹的中序  
        List<T> midRight = new ArrayList<T>(pre.size() - index - 1);  
        for (int i = 0; i <= pre.size() - index - 1; i++) {  
            preRight.add(pre.get(index + i));  
        }  
        for (int i = 0; i <= pre.size() - index - 1; i++) {  
            midRight.add(mid.get(index + i));  
        }  
        // 重建→子樹  
        root.rightChild = getTreeFromPreAndMid(preRight, midRight);  
        return root;  
    }  
  
    /** 
     * equals: 查看node1和node2兩棵樹是否相等(兩棵樹所有節點都相等) 
     *  
     * @param node1 
     *            node2 兩個節點 
     * @return boolean 返回類型 
     */  
    public static <T> boolean equals(TreeNode<T> node1, TreeNode<T> node2) {  
        // TODO Auto-generated method stub  
        if (node1 == null && node2 == null) {  
            return true;  
        } else if (node1 == null || node2 == null) {  
            return false;  
        }  
        boolean isEqual = node1.value.equals(node2.value);  
        boolean isLeftEqual = equals(node1.leftChild, node2.leftChild);  
        boolean isRightEqual = equals(node1.rightChild, node2.rightChild);  
        return isEqual && isLeftEqual && isRightEqual;  
    }  
}  

測試類:

測試類中,先利用節點中的基本操作構建一棵樹:

public static void main(String[] args) {
        // TODO Auto-generated method stub
        TreeNode<Integer> t = new TreeNode<Integer>(1);
        t.addLeft(2);
        t.addRight(3);
        t.leftChild.addLeft(4);
        t.leftChild.addRight(5);
        System.out.println("中序遍歷測試:");
        TreeTools.midOrderTravel(t);
        System.out.println("\n前序遍歷測試:");
        TreeTools.preOrderTravel(t);
        System.out.println("\n後序遍歷測試:");
        TreeTools.backOrderTravel(t);
        System.out.println("\n層次遍歷測試:");
        TreeTools.levelTravel(t);
        System.out.println("\n樹的深度:"+TreeTools.getTreeDepth(t));
        System.out.println("樹的葉子個數:"+TreeTools.getLeafNum(t));
        System.out.println("樹的節點個數:"+TreeTools.getTreeNum(t));
        System.out.println("第2層節點個數為:"+TreeTools.getNumForKlevel(t,2));
        List<Integer> pre = new ArrayList<Integer>();
        pre.add(1);
        pre.add(2);
        pre.add(4);
        pre.add(5);
        pre.add(3);
        List<Integer> mid = new ArrayList<Integer>();
        mid.add(4);
        mid.add(2);
        mid.add(5);
        mid.add(1);
        mid.add(3);
        TreeNode<Integer> root = TreeTools.getTreeFromPreAndMid(pre, mid);
        System.out.println("\n通過前序和中序構建樹測試:");
        TreeTools.levelTravel(root);
        System.out.println("\n構建的樹比較測試:");
        System.out.println(TreeTools.equals(t,root));
    }

結果為:

中序遍歷測試:
4    2    5    1    3    
前序遍歷測試:
1    2    4    5    3    
後序遍歷測試:
4    5    2    3    1    
層次遍歷測試:
1    2    3    4    5    
樹的深度:3
樹的葉子個數:3
樹的節點個數:5
第2層節點個數為:2

通過前序和中序構建樹測試:
1    2    3    4    5    
構建的樹比較測試:
true

最後的話: 樹的基本操作都很簡單,樹是我見過的最適合用遞歸來操作的數據結構了。因為子節點和父節點是一樣的類型,而且基本具有同樣的屬性。

以上的方法裏面,可能只有利用前序和中序構建一棵樹不容易理解。 比如圖1裏面那棵樹,前序是1 2 4 5 3 中序是4 2 5 1 3 前序的1表示根是1,然後在中序序列裏,1左邊的是根節點的左子樹,1後邊的是整棵樹的右子樹,中序1前邊有3個數字,右邊有1個數字,表示根節點的左子樹有3個節點,右子樹有1個節點,所以構建根節點左子樹的前序和中序時,取根節點前序的2,4,5和根節點中序的425,構建根節點右子樹的前序和中序時取根節點的3,然後遞歸構建即可。

java實現樹的一般操作