树用的很少,一般情况下树都是作为接口被实现的。
实现二叉排序树:
首先,二叉排序树最基本的特点: 应该有初始化,即构造函数;其次应该有增删查操作。删除操作有一个简便做法,是给每个节点增加一个boolean型的是否可用标志,查询相应节点然后将标志置为false即可。
首先,用面向对象的思想建树,树和节点应该分开,如果业务上想给整颗树加什么属性也方便。
树的定义:
/** * BinarySortTree:二叉排序树 * @author xuejupo [email protected] * create in 2015-11-19 下午5:28:19 * */ public class BinarySortTree<T extends Comparable<? super T>> implements Tree<T> { TreeNode<T> root;
只是有个根节点而已,可以加上其他的属性,比如最大深度,节点个数,树是否可用等等。
构造函数:
/** * 类BinarySortTree.java的构造函数 * <p>Title: </p> * <p>Description: </p> 创建一个空的二叉搜索树 * createTime: 下午3:27:13 */ BinarySortTree(){ root = new TreeNode<T>(); } /** * 类BinarySortTree.java的构造函数 * <p>Title: </p> * <p>Description: </p> 创建一个根节点值为value的二叉搜索树 * createTime: 下午3:27:23 * @param value */ BinarySortTree(T value) { root = new TreeNode<T>(value); // TODO Auto-generated constructor stub } BinarySortTree(TreeNode<T> node) { root = node; // TODO Auto-generated constructor stub }
很简单的一些基本方法:
/** * isEmpty: 是否为空 * @return * boolean 返回类型 */ public boolean isEmpty(){ return this == null || this.root == null; } /** * getMin: 获取最小值 * @return * T 返回类型 */ public T getMin(){ while(root.leftChild != null){ root = root.leftChild; } return root.value; } /** * getMin: 获取root下的最小的节点 * @return * T 返回类型 */ public T getMin(TreeNode<T> root){ while(root.leftChild != null){ root = root.leftChild; } return root.value; } /** * getMax: 获取root子树中最大的节点 * @return * T 返回类型 */ public T getMax(TreeNode<T> root){ while(root.rightChild != null){ root = root.rightChild; } return root.value; } /** * getMax: 获取最大值 * @return * T 返回类型 */ public T getMax(){ while(root.rightChild != null){ root = root.rightChild; } return root.value; }
插入节点: 递归,还是递归。。 写过操作树代码的人应该都清楚,树这种数据结构实在太适合应递归了。。 排序树插入,如果节点小于当前节点,递归当前节点左子树,否则递归右子树。。
/** * addNode: 二叉排序树的插入 * @param root * @param value * void 返回类型 */ public void addNode(T value) { if (root == null) { return; } addNode(root,value); } public void addNode(TreeNode<T> node,T value) { if (node.value.compareTo(value) < 0) { if (node.rightChild == null) { node.addRight(value); } else { addNode(node.rightChild, value); } } else if (node.value.compareTo(value) > 0) { if (node.leftChild == null) { node.addLeft(value); } else { addNode(node.leftChild, value); } } else { // 不允许相同元素插入 } }
查询:还是递归,跟插入类似
/** * searchNode: 在二叉排序树中查询值value是否存在 * @param value 插入的值 * @return * TreeNode 返回类型 */ public boolean searchNode(T value) { return searchNode(root,value) != null; } /** * searchNode: 在二叉排序树中查询值value所在的节点 * @param root 根节点 * @param value 插入的值 * @return * TreeNode 返回类型 */ private TreeNode<T> searchNode(TreeNode<T> root, T value) { if (root == null) { return null; } if (root.value.compareTo(value) == 0) { return root; } if (root.value.compareTo(value) < 0) { return searchNode(root.rightChild, value); } return searchNode(root.leftChild, value); }
删除:可能麻烦点,因为要分几种情况: 要删除的节点是叶子节点,这中情况最简单。 要删除的节点有左子树怎么办,要删除的节点有右子树怎么办,要删除的节点既有左子树又有右子树怎么办。。 还要考虑要删除的节点正好是根节点怎么办。。。
注释写的很清楚了,有什么不明白的可以留言问我,菜鸟互助,共同进步
/** * delNode: 二叉排序树中删除节点 * @param root 根节点 * @param value 删除的值 * @return * TreeNode 返回类型 */ private BinarySortTree<T> delNode(T value) { return new BinarySortTree<T>(delNode(root,value)); } /** * delNode: 二叉排序树中删除节点 * @param root 根节点 * @param value 删除的值 * @return * TreeNode 返回类型 */ private TreeNode<T> delNode(TreeNode<T> root, T value) { TreeNode<T> delNode = searchNode(root, value); if (delNode == null) { return root; } TreeNode<T> father = getFatherNode(root, value); // 如果要删除的是叶子节点 if (delNode.leftChild == null && delNode.rightChild == null) { // 如果要删除的是根节点 if (father == null) { root = null; } if (father.value.compareTo(delNode.value) < 0) { father.rightChild = null; } else { father.leftChild = null; } delNode = null; } else if (delNode.rightChild == null) {// 如果要删除的节点只有左子树 // 如果要删除的是根节点 if (father == null) { root = root.leftChild; } else { if (father.value.compareTo(delNode.value) < 0) { father.rightChild = delNode.leftChild; } else { father.leftChild = delNode.leftChild; } delNode = null; } }else if (delNode.leftChild == null) {// 如果要删除的节点只有右子树 // 如果要删除的是根节点 if (father == null) { root = root.rightChild; } else { if (father.value.compareTo(delNode.value) < 0) { father.rightChild = delNode.rightChild; } else { father.leftChild = delNode.rightChild; } delNode = null; } }else{ //要删除的节点既有左子树,又有右子树 //在这里我们取左子树的最大节点作为删除节点的后继 TreeNode<T> leftMax = delNode.leftChild; //后继节点的父节点 TreeNode<T> leftMaxFather = delNode; while(leftMax.rightChild != null){ leftMaxFather = leftMax; leftMax = leftMax.rightChild; } //先删除后继节点,用后继节点替换要删除的节点 leftMaxFather.rightChild = leftMax.leftChild; delNode.value = leftMax.value; leftMax = null; } return root; }
测试代码:
/** * @Title: main * @Description: 这里用一句话描述这个方法的作用 * @param args * @return void 返回类型 */ public static void main(String[] args) { BinarySortTree<Integer> tree = new BinarySortTree<Integer>(2); tree.addNode(4); tree.addNode(3); //不能添加相同元素 tree.addNode(4); tree.addNode(7); System.out.println("中序遍历二叉树,得到的应该是个有序的序列:"); TreeTools.midOrderTravel(tree.getRoot()); System.out.println("\n查找最小数:"); System.out.println(tree.getMin()); System.out.println("查找2是否在树里:"); System.out.println(tree.searchNode(2)); System.out.println("删除节点2:"); tree = tree.delNode(2); System.out.println("删除后中序遍历二叉树:"); TreeTools.midOrderTravel(tree.getRoot()); System.out.println("\n查找2是否在树里:"); System.out.println(tree.searchNode(2)); }
结果:
中序遍历二叉树,得到的应该是个有序的序列: 2 3 4 7 查找最小数: 2 查找2是否在树里: true 删除节点2: 删除后中序遍历二叉树: 3 4 7 查找2是否在树里: false