二叉查找树
来源:互联网 发布:长春市盘古网络 法人 编辑:程序博客网 时间:2024/04/27 18:28
使二叉树成为尔叉查找树的性质是:
对于树中的每个节点X,它的左子树中所有项的值小于X中的项,而它的右子树中所有的项的值大于X中的项。
由于树的递归定义,通常是递归地编写有关树的操作例程。因为二叉查找树的平均深度是O(log N),所以一般不必担心栈空间被用尽。
1 contains方法
//contains的包装方法 public boolean contains(int val) { return contains(val, root); } private boolean contains(int val, TreeNode r) { if(r == null){ return false; } if(val - r.val < 0){ return contains(val, r.left); } else if (val - r.val > 0) { return contains(val, r.right); } else { return true; } }
2 findMin 和 findMax 方法
//findMin包装方法 public TreeNode findMin() { return findMin(root); } private TreeNode findMin(TreeNode t) { if(t == null){ return null; } else if (t.left == null) { return t; } else { return findMin(t.left); } }
//findMax的包装方法 public TreeNode findMax() { return findMax(root); } private TreeNode findMax(TreeNode t) { if(t == null){ return null; } else if (t.right == null) { return t; } else { return findMax(t.right); } }
3 insert操作
跟contains类似,就是通过比较,二分查找到要插入的位置;如果发现已经存在,则什么也不做。
// insert的包装方法 public void insert(int val) { root = insert(root, val); } private TreeNode insert(TreeNode t, int val) { // 由于是递归编写,所以每次找准了要插入的位置才新建节点;不然没调用该方法一次就新建一个节点是一种浪费 if (t == null) { TreeNode node = new TreeNode(val); return node; } // 小于0,则插在在左子树 if (val - t.val < 0) { // 如果左子树为空,则新建节点,插入为左节点即可 if (t.left == null) { TreeNode node = new TreeNode(val); t.left = node; } // 如果左子树不为空,则接着判断一次 else { insert(t.left, val); } } // 如果大于0,则插在右子树 else if (val - t.val > 0) { if (t.right == null) { TreeNode node = new TreeNode(val); t.right = node; } else { insert(t.right, val); } } // 发现二叉搜索树中已经存在这个值了 else { // do nothing } return t; }
4 remove操作
remove操作比较复杂。
(1)如果要删除的节点是一个叶子,那么直接去掉就可以了;
(2)如果要删除的节点A有一个儿子,那么调整一下A的父节点的指针,使它直接指向A的儿子即可;
(3)如果要删除的节点A有两个儿子,那么可以用A的右子树中的最小值,代替A的值,然后再去删除那么最小节点即可。这样的好处是,最小节点是一定没有左子树的,所以,删除它,就是用情况2中的步骤。
// remove的包装方法 public void remove(int val) { root = remove(root, val); } private TreeNode remove(TreeNode t, int val) { if (t == null) { return null; } // 第一步是,找到要删除的节点 // 小于0,说明该节点在左子树中 if (val - t.val < 0) { t.left = remove(t.left, val); } // 大于0,说明该节点在右子树中 else if (val - t.val > 0) { t.right = remove(t.right, val); } // 等于0,说明找到了 else { // 如果这是一个叶子,则直接删除这个节点 if (t.left == null && t.right == null) { t = null; } // 如果该节点有一个儿子 else if (t.left == null ^ t.right == null) { return t.left != null ? t.left : t.right; } // 如果有两个儿子 else if (t.left != null && t.right != null) { TreeNode minNode = findMin(t.right); t.val = minNode.val; t.right = remove(t.right, minNode.val); } } return t; }
5 完整代码即测试结果
BinarySearchTree.java
package Tree;import java.util.LinkedList;public class BinarySearchTree { private TreeNode root; public BinarySearchTree() { root = null; } public void clear() { root = null; } public boolean isEmpty() { return root == null; } // remove的包装方法 public void remove(int val) { root = remove(root, val); } private TreeNode remove(TreeNode t, int val) { if (t == null) { return null; } // 第一步是,找到要删除的节点 // 小于0,说明该节点在左子树中 if (val - t.val < 0) { t.left = remove(t.left, val); } // 大于0,说明该节点在右子树中 else if (val - t.val > 0) { t.right = remove(t.right, val); } // 等于0,说明找到了 else { // 如果这是一个叶子,则直接删除这个节点 if (t.left == null && t.right == null) { t = null; } // 如果该节点有一个儿子 else if (t.left == null ^ t.right == null) { return t.left != null ? t.left : t.right; } // 如果有两个儿子 else if (t.left != null && t.right != null) { TreeNode minNode = findMin(t.right); t.val = minNode.val; t.right = remove(t.right, minNode.val); } } return t; } // insert的包装方法 public void insert(int val) { root = insert(root, val); } private TreeNode insert(TreeNode t, int val) { // 由于是递归编写,所以每次找准了要插入的位置才新建节点;不然没调用该方法一次就新建一个节点是一种浪费 if (t == null) { TreeNode node = new TreeNode(val); return node; } // 小于0,则插在在左子树 if (val - t.val < 0) { // 如果左子树为空,则新建节点,插入为左节点即可 if (t.left == null) { TreeNode node = new TreeNode(val); t.left = node; } // 如果左子树不为空,则接着判断一次 else { insert(t.left, val); } } // 如果大于0,则插在右子树 else if (val - t.val > 0) { if (t.right == null) { TreeNode node = new TreeNode(val); t.right = node; } else { insert(t.right, val); } } // 发现二叉搜索树中已经存在这个值了 else { // do nothing } return t; } // contains的包装方法 public boolean contains(int val) { return contains(val, root); } private boolean contains(int val, TreeNode r) { if (r == null) { return false; } if (val - r.val < 0) { return contains(val, r.left); } else if (val - r.val > 0) { return contains(val, r.right); } else { return true; } } // findMin包装方法 public TreeNode findMin() { return findMin(root); } private TreeNode findMin(TreeNode t) { if (t == null) { return null; } else if (t.left == null) { return t; } else { return findMin(t.left); } } // findMax的包装方法 public TreeNode findMax() { return findMax(root); } private TreeNode findMax(TreeNode t) { if (t == null) { return null; } else if (t.right == null) { return t; } else { return findMax(t.right); } } public void buildTree() { /** * 6 * / \ * 2 8 * / \ * 1 4 * / * 3 * **/ root = new TreeNode(6); TreeNode l = new TreeNode(2); TreeNode r = new TreeNode(8); TreeNode ll = new TreeNode(1); TreeNode lr = new TreeNode(4); TreeNode lrl = new TreeNode(3); root.left = l; root.right = r; l.left = ll; l.right = lr; lr.left = lrl; } public void display() { if (root == null) { return; } System.out.println("\n*******************************"); LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); TreeNode p; queue.addLast(root); int parentCount = 1; int childrenCount = 0; while (!queue.isEmpty()) { p = queue.removeFirst(); System.out.print(p.val + " "); parentCount--; if (p.left != null) { queue.addLast(p.left); childrenCount++; } if (p.right != null) { queue.addLast(p.right); childrenCount++; } if (parentCount == 0) { System.out.println(); parentCount = childrenCount; childrenCount = 0; } } System.out.println("*******************************\n"); }}
Main.java
package Tree;public class Main { public static void main(String[] args) { BinarySearchTree bTree = new BinarySearchTree(); /**新建一个用于测试的二叉搜索树 * 6 * / \ * 2 8 * / \ * 1 4 * / * 3 * **/ bTree.buildTree(); // 按层次打印这棵树 bTree.display(); // 是否包含 3 这个值? System.out.println(bTree.contains(3) + "\n"); // 最小、最大值是多少? System.out.println("Min is: " + bTree.findMin().val); System.out.println("Max is: " + bTree.findMax().val); // 插入 5 bTree.insert(5); System.out.print("插入 5"); bTree.display(); // 插入12 bTree.insert(12); System.out.print("插入12:"); bTree.display(); // 删除 8 // 目前 8 所在的节点有一个儿子 12 bTree.remove(8); System.out.print("删除 8:"); bTree.display(); // 删除 2 // 目前2所在节点有两个儿子 1和 4 bTree.remove(2); System.out.print("删除 2:"); bTree.display(); }}
output:
*******************************6 2 8 1 4 3 *******************************trueMin is: 1Max is: 8插入 5*******************************6 2 8 1 4 3 5 *******************************插入12:*******************************6 2 8 1 4 12 3 5 *******************************删除 8:*******************************6 2 12 1 4 3 5 *******************************删除 2:*******************************6 3 12 1 4 5 *******************************
6 二叉搜索树的问题
执行大多数BST基本操作的时间显然依赖于树的形状。如果树是平衡的,即每个节点的左子树的节点数与右子树的节点数大致相等,则当查找指针下移一层的时候,需要查找的节点数降低为原来的一半。当BST变得不平衡的时候,比如极端的,一个BST退化为一个链表,使得查找的效率变成了O(N)。
所以问题有两个:
(1)上述的remove方法有助于使右子树的深度比左子树的浅,因为我们总是用右子树的最小节点来代替要删除的节点。
(2)如果是已经排序好的数据,那么可以想象一种极端情况是所有的节点都没有左儿子,或是,所有的节点都没有右儿子。
上面两个问题又可以总结为一类问题,那就是树的不平衡。为什么不平衡不好呢?因为这样的二叉搜索树,其查找等操作的平均时间将超过 O(log N)。比如,向一棵树输入预先排好的数据,那么这一连串的insert 操作将花费二次时间。为了解决这一类问题,有了平衡二叉树的概念。
平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。构造与调整方法 平衡二叉树的常用算法有红黑树、AVL、Treap等。系统的,将在下一篇博文里介绍。
- 查找--二叉查找树
- 二叉树、二叉查找树
- 二叉树 & 二叉查找树
- 【查找结构】二叉查找树
- 查找之二叉树查找
- 查找之二叉树查找
- 查找:二叉查找树总结
- 二叉树查找树...
- 二叉树查找树
- 查找--遍历二叉树
- 二叉查找树
- 二叉查找树实现
- 二叉查找树
- 动态二叉查找树
- 最优二叉查找树
- 二叉查找树
- 二叉查找树
- 平衡二叉查找树
- TCP之心跳包实现思路
- SAX解析
- iOS开发企业版ipa分发(in-house)
- Pinterest主页的机器学习
- MySQL cp xtrabackup mysqldump备份过程详解
- 二叉查找树
- 蓝桥杯--算法提高 01背包
- UNP Chapter 21 - 带外数据
- BZOJ 2463:谁能赢呢?【水】
- 蓝桥杯--历届试题 分糖果(模拟水题)
- 退出App的几种方法
- shell学习
- JDK7中的排序算法详解--Collections.sort和Arrays.sort
- Android PopupWindow的简单显示