一、二叉樹
二叉樹指的是每個節點最多只能有兩個子樹的有序樹。通常左邊的子樹被稱為“左子樹”(left subtree),右邊的子樹被稱為右子樹。
二叉樹的每個節點最多只有2棵子樹,二叉樹的子樹次序不能顛倒。
二、順序存儲二叉樹的實現
1 package com.ietree.basic.datastructure.tree.binarytree; 2 3 /** 4 * Created by ietree 5 * 2017/5/1 6 */ 7 public class ArrayBinTree<T> { 8 9 // 使用數組來記錄該樹的所有節點 10 private Object[] datas; 11 private int DEFAULT_DEEP = 8; 12 // 保存該樹的深度 13 private int deep; 14 private int arraySize; 15 16 // 以默認的深度創建二叉樹 17 public ArrayBinTree() { 18 this.deep = DEFAULT_DEEP; 19 this.arraySize = (int) (Math.pow(2, deep) - 1); 20 datas = new Object[arraySize]; 21 } 22 23 // 以指定深度創建二叉樹 24 public ArrayBinTree(int deep) { 25 this.deep = deep; 26 this.arraySize = (int) Math.pow(2, deep) - 1; 27 datas = new Object[arraySize]; 28 } 29 30 // 以指定深度、指定節點創建二叉樹 31 public ArrayBinTree(int deep, T data) { 32 this.deep = deep; 33 this.arraySize = (int) Math.pow(2, deep) - 1; 34 datas = new Object[arraySize]; 35 datas[0] = data; 36 } 37 38 /** 39 * 為指定節點添加子節點 40 * 41 * @param index 需要添加子節點的父節點的索引 42 * @param data 新子節點的數據 43 * @param left 是否為左節點 44 */ 45 public void add(int index, T data, boolean left) { 46 if (datas[index] == null) { 47 throw new RuntimeException(index + "處節點為空,無法添加子節點"); 48 } 49 if (2 * index + 1 >= arraySize) { 50 throw new RuntimeException("樹底層的數組已滿,樹越界異常"); 51 } 52 // 添加左子節點 53 if (left) { 54 datas[2 * index + 1] = data; 55 } else { 56 datas[2 * index + 2] = data; 57 } 58 } 59 60 // 判斷二叉樹是否為空 61 public boolean empty() { 62 // 根據根元素判斷二叉樹是否為空 63 return datas[0] == null; 64 } 65 66 // 返回根節點 67 public T root() { 68 return (T) datas[0]; 69 } 70 71 // 返回指定節點(非根結點)的父節點 72 public T parent(int index) { 73 return (T) datas[(index - 1) / 2]; 74 } 75 76 // 返回指定節點(非葉子)的左子節點,當左子節點不存在時返回null 77 public T left(int index) { 78 if (2 * index + 1 >= arraySize) { 79 throw new RuntimeException("該節點為葉子節點,無子節點"); 80 } 81 return (T) datas[index * 2 + 1]; 82 } 83 84 // 返回指定節點(非葉子)的右子節點,當右子節點不存在時返回null 85 public T right(int index) { 86 if (2 * index + 1 >= arraySize) { 87 throw new RuntimeException("該節點為葉子節點,無子節點"); 88 } 89 return (T) datas[index * 2 + 2]; 90 } 91 92 // 返回該二叉樹的深度 93 public int deep(int index) { 94 return deep; 95 } 96 97 // 返回指定節點的位置 98 public int pos(T data) { 99 // 該循環實際上就是按廣度遍歷來搜索每個節點 100 for (int i = 0; i < arraySize; i++) { 101 if (datas[i].equals(data)) { 102 return i; 103 } 104 105 } 106 return -1; 107 } 108 109 public String toString() { 110 return java.util.Arrays.toString(datas); 111 } 112 113 }
測試類:
1 package com.ietree.basic.datastructure.tree.binarytree; 2 3 /** 4 * Created by ietree 5 * 2017/5/1 6 */ 7 public class ArrayBinTreeTest { 8 9 public static void main(String[] args) { 10 11 ArrayBinTree<String> binTree = new ArrayBinTree<String>(4, "根"); 12 binTree.add(0, "第二層右子節點", false); 13 binTree.add(2, "第三層右子節點", false); 14 binTree.add(6, "第四層右子節點", false); 15 System.out.println(binTree); 16 17 } 18 19 }
程序輸出:
[根, null, 第二層右子節點, null, null, null, 第三層右子節點, null, null, null, null, null, null, null, 第四層右子節點]
三、二叉樹的二叉鏈表存儲
二叉鏈表存儲的思想是讓每個節點都能“記住”它的左、右兩個子節點。為每個節點增加left、right兩個指針,分別引用該節點的左、右兩個子節點。
1 package com.ietree.basic.datastructure.tree.binarytree; 2 3 /** 4 * Created by ietree 5 * 2017/5/1 6 */ 7 public class TwoLinkBinTree<E> { 8 9 public static class TreeNode { 10 11 Object data; 12 TreeNode left; 13 TreeNode right; 14 15 public TreeNode() { 16 17 } 18 19 public TreeNode(Object data) { 20 this.data = data; 21 } 22 23 public TreeNode(Object data, TreeNode left, TreeNode right) { 24 this.data = data; 25 this.left = left; 26 this.right = right; 27 } 28 29 } 30 31 private TreeNode root; 32 33 // 以默認的構造器創建二叉樹 34 public TwoLinkBinTree() { 35 this.root = new TreeNode(); 36 } 37 38 // 以指定根元素創建二叉樹 39 public TwoLinkBinTree(E data) { 40 this.root = new TreeNode(data); 41 } 42 43 /** 44 * 為指定節點添加子節點 45 * 46 * @param parent 需要添加子節點的父節點的索引 47 * @param data 新子節點的數據 48 * @param isLeft 是否為左節點 49 * @return 新增的節點 50 */ 51 public TreeNode addNode(TreeNode parent, E data, boolean isLeft) { 52 53 if (parent == null) { 54 throw new RuntimeException(parent + "節點為null, 無法添加子節點"); 55 } 56 if (isLeft && parent.left != null) { 57 throw new RuntimeException(parent + "節點已有左子節點,無法添加左子節點"); 58 } 59 if (!isLeft && parent.right != null) { 60 throw new RuntimeException(parent + "節點已有右子節點,無法添加右子節點"); 61 } 62 63 TreeNode newNode = new TreeNode(data); 64 if (isLeft) { 65 // 讓父節點的left引用指向新節點 66 parent.left = newNode; 67 } else { 68 // 讓父節點的left引用指向新節點 69 parent.right = newNode; 70 } 71 return newNode; 72 } 73 74 // 判斷二叉樹是否為空 75 public boolean empty() { 76 // 根據元素判斷二叉樹是否為空 77 return root.data == null; 78 } 79 80 // 返回根節點 81 public TreeNode root() { 82 if (empty()) { 83 throw new RuntimeException("樹為空,無法訪問根節點"); 84 } 85 return root; 86 } 87 88 // 返回指定節點(非根節點)的父節點 89 public E parent(TreeNode node) { 90 // 對於二叉樹鏈表存儲法,如果要訪問指定節點的父節點必須遍歷二叉樹 91 return null; 92 } 93 94 // 返回指定節點(非葉子)的左子節點,當左子節點不存在時返回null 95 public E leftChild(TreeNode parent) { 96 if (parent == null) { 97 throw new RuntimeException(parent + "節點為null,無法添加子節點"); 98 } 99 return parent.left == null ? null : (E) parent.left.data; 100 } 101 102 // 返回指定節點(非葉子)的右子節點,當右子節點不存在時返回null 103 public E rightChild(TreeNode parent) { 104 if (parent == null) { 105 throw new RuntimeException(parent + "節點為null,無法添加子節點"); 106 } 107 return parent.right == null ? null : (E) parent.right.data; 108 } 109 110 // 返回該二叉樹的深度 111 public int deep() { 112 // 獲取該樹的深度 113 return deep(root); 114 } 115 116 // 這是一個遞歸方法:每一棵子樹的深度為其所有子樹的最大深度 + 1 117 private int deep(TreeNode node) { 118 if (node == null) { 119 return 0; 120 } 121 // 沒有子樹 122 if (node.left == null && node.right == null) { 123 return 1; 124 } else { 125 int leftDeep = deep(node.left); 126 int rightDeep = deep(node.right); 127 // 記錄其所有左、右子樹中較大的深度 128 int max = leftDeep > rightDeep ? leftDeep : rightDeep; 129 // 返回其左右子樹中較大的深度 + 1 130 return max + 1; 131 } 132 133 } 134 135 }
測試類:
1 package com.ietree.basic.datastructure.tree.binarytree; 2 3 /** 4 * Created by ietree 5 * 2017/5/1 6 */ 7 public class TwoLinkBinTreeTest { 8 9 public static void main(String[] args) { 10 11 TwoLinkBinTree<String> binTree = new TwoLinkBinTree<String>("根節點"); 12 // 依次添加節點 13 TwoLinkBinTree.TreeNode tn1 = binTree.addNode(binTree.root(), "第二層左節點", true); 14 TwoLinkBinTree.TreeNode tn2 = binTree.addNode(binTree.root(), "第二層右節點", false); 15 TwoLinkBinTree.TreeNode tn3 = binTree.addNode(tn2, "第三層左節點", true); 16 TwoLinkBinTree.TreeNode tn4 = binTree.addNode(tn2, "第三層右節點", false); 17 TwoLinkBinTree.TreeNode tn5 = binTree.addNode(tn3, "第四層左節點", true); 18 19 System.out.println("tn2的左子節點:" + binTree.leftChild(tn2)); 20 System.out.println("tn2的右子節點:" + binTree.rightChild(tn2)); 21 System.out.println(binTree.deep()); 22 23 } 24 25 }
程序輸出:
tn2的左子節點:第三層左節點
tn2的右子節點:第三層右節點
4
四、二叉樹的三叉鏈表存儲
三叉鏈表存儲方式是對二叉鏈表的一種改進,通過為樹節點增加一個parent引用,可以讓每個節點都能非常方便的訪問其父節點,三叉鏈表存儲的二叉樹即可方便地向下訪問節點,也可方便地向上訪問節點。
1 package com.ietree.basic.datastructure.tree.binarytree; 2 3 /** 4 * Created by ietree 5 * 2017/5/1 6 */ 7 public class ThreeLinkBinTree<E> { 8 9 public static class TreeNode { 10 11 Object data; 12 TreeNode left; 13 TreeNode right; 14 TreeNode parent; 15 16 public TreeNode() { 17 18 } 19 20 public TreeNode(Object data) { 21 this.data = data; 22 } 23 24 public TreeNode(Object data, TreeNode left, TreeNode right, TreeNode parent) { 25 this.data = data; 26 this.left = left; 27 this.right = right; 28 this.parent = parent; 29 } 30 31 } 32 33 private TreeNode root; 34 35 // 以默認的構造器創建二叉樹 36 public ThreeLinkBinTree() { 37 this.root = new TreeNode(); 38 } 39 40 // 以指定根元素創建二叉樹 41 public ThreeLinkBinTree(E data) { 42 this.root = new TreeNode(data); 43 } 44 45 /** 46 * 為指定節點添加子節點 47 * 48 * @param parent 需要添加子節點的父節點的索引 49 * @param data 新子節點的數據 50 * @param isLeft 是否為左節點 51 * @return 新增的節點 52 */ 53 public TreeNode addNode(TreeNode parent, E data, boolean isLeft) { 54 55 if (parent == null) { 56 throw new RuntimeException(parent + "節點為null, 無法添加子節點"); 57 } 58 if (isLeft && parent.left != null) { 59 throw new RuntimeException(parent + "節點已有左子節點,無法添加左子節點"); 60 } 61 if (!isLeft && parent.right != null) { 62 throw new RuntimeException(parent + "節點已有右子節點,無法添加右子節點"); 63 } 64 65 TreeNode newNode = new TreeNode(data); 66 if (isLeft) { 67 // 讓父節點的left引用指向新節點 68 parent.left = newNode; 69 } else { 70 // 讓父節點的left引用指向新節點 71 parent.right = newNode; 72 } 73 // 讓新節點的parent引用到parent節點 74 newNode.parent = parent; 75 return newNode; 76 } 77 78 // 判斷二叉樹是否為空 79 public boolean empty() { 80 // 根據元素判斷二叉樹是否為空 81 return root.data == null; 82 } 83 84 // 返回根節點 85 public TreeNode root() { 86 if (empty()) { 87 throw new RuntimeException("樹為空,無法訪問根節點"); 88 } 89 return root; 90 } 91 92 // 返回指定節點(非根節點)的父節點 93 public E parent(TreeNode node) { 94 if (node == null) { 95 throw new RuntimeException("節點為null,無法訪問其父節點"); 96 } 97 return (E) node.parent.data; 98 } 99 100 // 返回指定節點(非葉子)的左子節點,當左子節點不存在時返回null 101 public E leftChild(TreeNode parent) { 102 if (parent == null) { 103 throw new RuntimeException(parent + "節點為null,無法添加子節點"); 104 } 105 return parent.left == null ? null : (E) parent.left.data; 106 } 107 108 // 返回指定節點(非葉子)的右子節點,當右子節點不存在時返回null 109 public E rightChild(TreeNode parent) { 110 if (parent == null) { 111 throw new RuntimeException(parent + "節點為null,無法添加子節點"); 112 } 113 return parent.right == null ? null : (E) parent.right.data; 114 } 115 116 // 返回該二叉樹的深度 117 public int deep() { 118 // 獲取該樹的深度 119 return deep(root); 120 } 121 122 // 這是一個遞歸方法:每一棵子樹的深度為其所有子樹的最大深度 + 1 123 private int deep(TreeNode node) { 124 if (node == null) { 125 return 0; 126 } 127 // 沒有子樹 128 if (node.left == null && node.right == null) { 129 return 1; 130 } else { 131 int leftDeep = deep(node.left); 132 int rightDeep = deep(node.right); 133 // 記錄其所有左、右子樹中較大的深度 134 int max = leftDeep > rightDeep ? leftDeep : rightDeep; 135 // 返回其左右子樹中較大的深度 + 1 136 return max + 1; 137 } 138 139 } 140 141 }
測試類:
1 package com.ietree.basic.datastructure.tree.binarytree; 2 3 /** 4 * Created by ietree 5 * 2017/5/1 6 */ 7 public class ThreeLinkBinTreeTest { 8 9 public static void main(String[] args) { 10 11 ThreeLinkBinTree<String> binTree = new ThreeLinkBinTree<String>("根節點"); 12 // 依次添加節點 13 ThreeLinkBinTree.TreeNode tn1 = binTree.addNode(binTree.root(), "第二層左節點", true); 14 ThreeLinkBinTree.TreeNode tn2 = binTree.addNode(binTree.root(), "第二層右節點", false); 15 ThreeLinkBinTree.TreeNode tn3 = binTree.addNode(tn2, "第三層左節點", true); 16 ThreeLinkBinTree.TreeNode tn4 = binTree.addNode(tn2, "第三層右節點", false); 17 ThreeLinkBinTree.TreeNode tn5 = binTree.addNode(tn3, "第四層左節點", true); 18 19 System.out.println("tn2的左子節點:" + binTree.leftChild(tn2)); 20 System.out.println("tn2的右子節點:" + binTree.rightChild(tn2)); 21 System.out.println(binTree.deep()); 22 23 } 24 25 }
程序輸出:
tn2的左子節點:第三層左節點
tn2的右子節點:第三層右節點
4