java實現樹的一般操作
阿新 • • 發佈:2018-05-16
表示 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實現樹的一般操作