首页 > 代码库 > 二叉树

二叉树

背景:
  • 有序数组使用二分查找法,需要的时间为 O(log n),同时可以迅速遍历,但是插入和删除操作太慢(找到后,把大的全部后移,对应删除则是左移)
  • 链表的插入和删除操作都很快,时间复杂度是最小的 O(1),但是查找需要从头开始,费时 O(N),即使使用有序链表,在访问任意项时也不能加快速度

“树”是一种有着“链表那样快速的插入和删除”和“有序数组那样快速的查找”的能力的数据结构。

下面是个人写的“二叉查找树(Binary Search Tree)”的实现(不允许重复关键值的元素):
  1. /**
  2. * 节点类
  3. *
  4. * @author Frank
  5. *
  6. */
  7. public class TreeNode {
  8. int keyData;// 关键值(可能还有别的非关键数据,这里省略)
  9. TreeNode leftChild;// 左子节点
  10. TreeNode rightChild;// 右子节点
  11. public TreeNode(int keyData) {
  12. super();
  13. this.keyData = keyData;
  14. }
  15. /**
  16. * 显示该节点信息
  17. */
  18. public void display() {
  19. System.out.println(
  20. "TreeNode [keyData="http://www.mamicode.com/ + keyData + ", leftChild=" + leftChild + ", rightChild=" + rightChild + "]");
  21. }
  22. @Override
  23. public String toString() {
  24. return "TreeNode [keyData="http://www.mamicode.com/ + keyData + "]";
  25. }
  26. }

  1. import java.util.HashMap;
  2. import java.util.Iterator;
  3. import java.util.Set;
  4. /**
  5. * 二叉查找树
  6. *
  7. * @author Frank
  8. *
  9. */
  10. public class Tree {
  11. TreeNode root;// 根节点
  12. private TreeNode parent;// 父节点
  13. public Tree(TreeNode root) {
  14. super();
  15. this.root = root;
  16. }
  17. /**
  18. * 以关键值查找节点是否存在
  19. *
  20. * @param keyData
  21. * 节点关键值
  22. * @return 若该树包含关键值对应的 Node,返回 true
  23. */
  24. public boolean findNode(int keyData) {
  25. TreeNode current = root;// 当前节点
  26. while (current != null) {
  27. if (current.keyData == keyData)
  28. return true;// 找到了
  29. if (current.keyData > keyData) // 往左遍历
  30. current = current.leftChild;
  31. if (current.keyData < keyData) // 往右遍历
  32. current = current.rightChild;
  33. }
  34. return false;// 未找到
  35. }
  36. /**
  37. * 将节点插入当前树
  38. *
  39. * @param node
  40. * 新插入的节点
  41. */
  42. public void insert(TreeNode node) {
  43. if (!this.isEmpty()) {
  44. boolean isLeftChild = true;
  45. int keyData = node.keyData;
  46. TreeNode current = root;// 当前节点
  47. parent = null;
  48. while (current != null) {
  49. parent = current;
  50. if (keyData == current.keyData)
  51. return;// 关键值一样则不插入
  52. current = (keyData < current.keyData) ? (current.leftChild) : (current.rightChild);
  53. isLeftChild = (keyData < parent.keyData) ? true : false;
  54. }
  55. if (isLeftChild) {
  56. parent.leftChild = node;
  57. } else {
  58. parent.rightChild = node;
  59. }
  60. }
  61. }
  62. /**
  63. * 将集合中的所有节点插入当前树
  64. *
  65. * @param node
  66. * 新插入的节点
  67. */
  68. public void insert(Set<TreeNode> nodeSet) {
  69. Iterator<TreeNode> iterator = nodeSet.iterator();
  70. while (iterator.hasNext()) {
  71. insert(iterator.next());
  72. }
  73. }
  74. /**
  75. * 判断当前树是否为空
  76. *
  77. * @return 若为空,返回 true
  78. */
  79. public boolean isEmpty() {
  80. return root == null;
  81. }
  82. // 通过递归实现中序遍历输出
  83. private void getOrder(TreeNode localRoot) {
  84. if (localRoot != null) {
  85. getOrder(localRoot.leftChild);
  86. System.out.print(localRoot.keyData + " ");
  87. getOrder(localRoot.rightChild);
  88. }
  89. }
  90. /**
  91. * 中序遍历指定的子树,若指定的节点并不是本树的节点(以关键值为标准,可能出现非关键值不一样的正常遍历),则不输出任何值
  92. *
  93. * @param localRoot
  94. * 指定节点对象,若为 root 节点则遍历当前树
  95. */
  96. public void inOrder(TreeNode localRoot) {
  97. if (localRoot != null && findNode(localRoot.keyData)) {
  98. TreeNode current = getNode(localRoot.keyData);
  99. getOrder(current);
  100. System.out.println();
  101. }
  102. }
  103. /**
  104. * 获取指定关键值对应的节点,该节点需存在于本树中
  105. *
  106. * @param keyData
  107. * 节点关键值
  108. * @return 若存在这样的节点则返回此节点,否则返回 null
  109. */
  110. public TreeNode getNode(int keyData) {
  111. TreeNode current = root;// 当前节点
  112. while (current != null) {
  113. if (current.keyData == keyData)
  114. return current;// 找到了
  115. if (current.keyData > keyData) // 往左遍历
  116. current = current.leftChild;
  117. if (current.keyData < keyData) // 往右遍历
  118. current = current.rightChild;
  119. }
  120. return null;// 未找到
  121. }
  122. /**
  123. * 获取当前树的最小关键值节点
  124. *
  125. * @return 最小关键值对应的节点,若树为空则返回 null
  126. */
  127. public TreeNode minimum() {
  128. if (!this.isEmpty()) {
  129. parent = null;
  130. TreeNode minmum = root;
  131. while ((minmum = minmum.leftChild) != null) {
  132. parent = minmum;
  133. }
  134. return parent;
  135. }
  136. return null;
  137. }
  138. /**
  139. * 获取当前树的最大关键值节点
  140. *
  141. * @return 最大关键值对应的节点,若树为空则返回 null
  142. */
  143. public TreeNode maximum() {
  144. if (!this.isEmpty()) {
  145. parent = null;
  146. TreeNode maximum = root;
  147. while ((maximum = maximum.rightChild) != null) {
  148. parent = maximum;
  149. }
  150. return parent;
  151. }
  152. return null;
  153. }
  154. /**
  155. * 获取指定节点的子节点个数
  156. *
  157. * @param keyData
  158. * 节点关键值
  159. * @return 返回拥有 keyData 关键值的节点的子节点个数,若节点不存在与本树,返回-1
  160. */
  161. public HashMap<Integer, Boolean> numOfChildren(int keyData) {
  162. HashMap<Integer, Boolean> returnMap = new HashMap<Integer, Boolean>();
  163. if (this.isEmpty() || !findNode(keyData)) {
  164. returnMap.put(-1, false);
  165. return returnMap;
  166. }
  167. TreeNode current = root;
  168. parent = null;
  169. boolean isLeftChild = true;
  170. while (current != null) {
  171. if (keyData == current.keyData) {
  172. break;
  173. }
  174. parent = current;
  175. current = (keyData < current.keyData) ? (current.leftChild) : (current.rightChild);
  176. isLeftChild = (keyData < parent.keyData) ? true : false;
  177. }
  178. if (current.leftChild == null && current.rightChild == null) {
  179. returnMap.put(0, isLeftChild);
  180. } else if (current.leftChild != null && current.rightChild != null) {
  181. returnMap.put(2, isLeftChild);
  182. } else {
  183. returnMap.put(1, isLeftChild);
  184. }
  185. return returnMap;
  186. }
  187. /**
  188. * 删除指定关键值对应的节点,若为非叶子节点,当直接子节点仅有一个时,由该子节点继承,若直接子节点为两个,则由后继节点继承,各子节点保持规律不变,
  189. * 即左子节点小于父节点,父节点小于右子节点
  190. *
  191. * @param keyData
  192. * 叶子节点的关键值
  193. * @return 删除成功返回 true,否则返回 false
  194. */
  195. public boolean delete(int keyData) {
  196. HashMap<Integer, Boolean> map = numOfChildren(keyData);
  197. boolean isLeftChild = map.values().iterator().next();
  198. switch (map.keySet().iterator().next()) {
  199. case -1:
  200. return false;
  201. case 0:
  202. if (isLeftChild) {
  203. parent.leftChild = null;
  204. } else {
  205. parent.rightChild = null;
  206. }
  207. return true;
  208. case 1:
  209. if (isLeftChild) {
  210. TreeNode current = parent.leftChild;
  211. parent.leftChild = (current.leftChild == null) ? current.rightChild : parent.leftChild.leftChild;
  212. } else {
  213. TreeNode current = parent.rightChild;
  214. parent.rightChild = (current.leftChild == null) ? current.rightChild : parent.leftChild.leftChild;
  215. }
  216. return true;
  217. case 2:
  218. TreeNode successorNode = getSuccessor(new TreeNode(keyData));// 该节点的后继节点,即将替换被删除节点
  219. TreeNode delNode = null;// 将要删除的节点
  220. if (isLeftChild) {// 将要删除的节点是其父节点的左子节点
  221. if (parent == null) {// 删除整个树
  222. root = null;
  223. return true;
  224. }
  225. delNode = parent.leftChild;
  226. if (delNode.rightChild == null) {
  227. break;
  228. } else if (successorNode.keyData == delNode.rightChild.keyData) {// 后继节点是delNode的右子节点
  229. parent.leftChild = successorNode;// 1.将要删除的节点替换为后继节点
  230. successorNode.leftChild = delNode.leftChild;// 2.将后继节点的左子节点替换为将要删除的节点的左子节点
  231. } else {// 后继节点是delNode的右子节点的左后代
  232. TreeNode successorParent = getParent(successorNode);// 获取后继节点的父节点,后继节点一定是这个父节点的左子节点
  233. parent = getParent(delNode);
  234. successorParent.leftChild = successorNode.rightChild;// 1.将后继节点的父节点的左子节点替换为后继节点的右子节点
  235. successorNode.rightChild = delNode.rightChild;// 2.将后继节点的右子节点换为将要删除的节点的右子节点
  236. parent.leftChild = successorNode;// 3.将要删除的节点替换为后继节点
  237. successorNode.leftChild = delNode.leftChild;// 4.将后继节点的左子节点替换为将要删除的节点的左子节点
  238. }
  239. } else {// 将要删除的节点是其父节点的右子节点
  240. delNode = parent.rightChild;
  241. if (successorNode.keyData == delNode.rightChild.keyData) {// 后继节点是delNode的右子节点
  242. parent.rightChild = successorNode;// 1.将要删除的节点替换为后继节点
  243. successorNode.leftChild = delNode.leftChild;// 2.将后继节点的左子节点替换为将要删除的节点的左子节点
  244. } else {// 后继节点是delNode的右子节点的左后代
  245. TreeNode successorParent = getParent(successorNode);// 获取后继节点的父节点,后继节点一定是这个父节点的左子节点
  246. parent = getParent(delNode);
  247. successorParent.leftChild = successorNode.rightChild;// 1.将后继节点的父节点的左子节点替换为后继节点的右子节点
  248. successorNode.rightChild = delNode.rightChild;// 2.将后继节点的右子节点换为将要删除的节点的右子节点
  249. parent.rightChild = successorNode;// 3.将要删除的节点替换为后继节点
  250. successorNode.leftChild = delNode.leftChild;// 4.将后继节点的左子节点替换为将要删除的节点的左子节点
  251. }
  252. }
  253. return true;
  254. }
  255. return false;
  256. }
  257. // 获取指定节点的父节点
  258. private TreeNode getParent(TreeNode node) {
  259. int keyData = node.keyData;
  260. if (this.isEmpty() || !findNode(keyData)) {
  261. return null;
  262. }
  263. TreeNode current = root;
  264. parent = null;
  265. while (current != null) {
  266. if (keyData == current.keyData) {
  267. break;
  268. }
  269. parent = current;
  270. current = (keyData < current.keyData) ? (current.leftChild) : (current.rightChild);
  271. }
  272. return parent;
  273. }
  274. /**
  275. * 获取指定节点的后继节点,后继节点应为:在该节点的子节点中,关键值大于该节点关键值且最接近的那个,该方法为删除方法提供支持
  276. *
  277. * @param del
  278. * 将要删除的节点
  279. * @return
  280. */
  281. private TreeNode getSuccessor(TreeNode del) {
  282. if (this.isEmpty() || !findNode(del.keyData))
  283. return null;
  284. int keyData = del.keyData;
  285. TreeNode current = root;
  286. while (current != null) {
  287. if (keyData == current.keyData) {
  288. break;
  289. }
  290. current = (keyData < current.keyData) ? (current.leftChild) : (current.rightChild);
  291. }
  292. if (current.rightChild == null) {
  293. return null;// 无后继节点
  294. }
  295. TreeNode successorNode = current.rightChild;
  296. while (successorNode.leftChild != null) {
  297. successorNode = successorNode.leftChild;
  298. }
  299. return successorNode;
  300. }
  301. }

二叉树