算法研究系列---二叉查找树
查找树以便于查找的方式来存放数据,尤其是二叉查找树,二叉查找树的特性使其可以使用简单的递归算法进行查找,这种算法在思路上类似于数组的折半查找,且同样高效.
?
?二叉查找树是其节点含有Comparable的对象,并且按如下组织的二叉树:
? ? ? ? ? ? 1:节点的数据大于节点的左子树中的数据。
? ? ? ? ? ? 2:节点的数据小于节点的右子树中的数据。
?
下面着重讨论如何基于java实现二叉查找树 并实现 诸如:插入,查找,删除,遍历等方法
?
?
package tree;public class BinaryTree {// Root node pointer. Will be null for an empty tree. private Node root; /* --Node-- The binary tree is built using this nested node class. Each node stores one data element, and has left and right sub-tree pointer which may be null. The node is a "dumb" nested class -- we just use it for storage; it does not have any methods. */ private static class Node { Node left; Node right; int data; Node(int newData) { left = null; right = null; data = newData; } } public BinaryTree() { root = null; } /** Returns true if the given target is in the binary tree. Uses a recursive helper. */ public boolean lookup(int data) { return(lookup(root, data)); } /** Recursive lookup -- given a node, recur down searching for the given data. */ private boolean lookup(Node node, int data) { if (node==null) { return(false); } if (data==node.data) { return(true); } else if (data<node.data) { return(lookup(node.left, data)); } else { return(lookup(node.right, data)); } } /** Inserts the given data into the binary tree. Uses a recursive helper. */ public void insert(int data) { root = insert(root, data); } /** Remove the given data into the binary tree Uses a recursive helper */ public void remove(int data){ remove(root, data); } private Node remove(Node node, int data) { if(node == null) { return null; } if(data < node.data) { node.left = remove(node.left,data); } else if(data > node.data) { node.right = remove(node.right,data); } else{ if(node.left != null && node.right != null) { node.data = findMax(node.right).data; node.right = removeMax(node.right); } else { node = ((node.left == null) ? node.right:node.left); } } return node; } private Node removeMax(Node node) { if(node != null) { if(node.right != null) { removeMax(node.right); } else { node = node.left; } } return node; } private Node findMax(Node node) { if(node != null) { while(node.right != null) { node = node.right; } } return node; } private Node remove2(Node node, int data) { if(node == null) { return node; } if(node.data < data) { node.right = remove2(node.right,data); } else if(node.data > data) { node.left = remove2(node.left,data); } else { if(node.left != null && node.right != null) { node.data = findMin(node.left).data; node.left = removeMin(node.left); } else { node = ((node.left == null) ? node.right:node.left); } } return node; } private Node removeMin(Node node) { if(node != null) { if(node.left != null) { removeMin(node.left); } else { node = node.right; } } return node; } private Node findMin(Node node) { if(node != null) { while(node.left != null) { node = node.left; } } return node; } private Node insert(Node node, int data) { if (node==null) { node = new Node(data); } else { if (data <= node.data) { node.left = insert(node.left, data); } else { node.right = insert(node.right, data); } } return(node); } /** * 中序历遍 */ public void inOrderTraverse() { inOrderTraverse(root); } /** * 先序历遍 */ public void preOrderTraverse(){ preOrderTraverse(root); } /** * 后序历遍 * @param node */ public void lastOrderTraverse(){ lastOrderTraverse(root); } private void lastOrderTraverse(Node node){ if(node != null) { lastOrderTraverse(node.left); lastOrderTraverse(node.right); System.out.print(node.data + " "); } } private void preOrderTraverse(Node node){ if(node != null) { System.out.print(node.data + " "); preOrderTraverse(node.left); preOrderTraverse(node.right); } } private void inOrderTraverse(Node node) { if(node == null) { return; } inOrderTraverse(node.left); System.out.print(node.data + " "); inOrderTraverse(node.right); }}
?
?
package test;
import tree.BinaryTree;public class TestBinaryTree {/** * @param args */public static void main(String[] args) { BinaryTree biTree=new BinaryTree(); int[] data={2,1,3,5,4,55,1245}; for(int i=0; i < data.length; i++) { biTree.insert(data[i]); } biTree.preOrderTraverse(); System.out.println(); biTree.insert(5); biTree.preOrderTraverse(); System.out.println(); biTree.remove(2); biTree.inOrderTraverse(); System.out.println(); }}?
?
上述代码实现了一颗二叉查找树,并提供了插入,删除,遍历,查找节点的操作,下面一一解释各操作:
?
插入: ?往二叉查找树中插入元素是一个基本操作,因为它正是建树时的基本操作。假设已有一颗二叉查找树,如要往其中插入一个新元素,首先必须要确保节点间的相互关系,即需要保证插入元素后的树仍是一颗二叉查找树,并且须保证方法lookup能找到这个元素,同时,每次的插入操作都是给这棵树加上一个新的叶子节点.
?
遍历,查找:这两个操作比较简单,采用递归方式,具体实现如代码.
?
删除: ?删除操作可以说是最复杂的一个了,为了从二叉查找树中删除元素,需要将与之相匹配的元素传递给方法remove,待删除的元素随后从树中删除并返回, 如果不存在这样的元素则返回null
? ? 但是删除元素时,不是简单的删除就可以了,这里需要看待删除的元素的左右孩子的情况:
? ? ? ? ?1:节点没有孩子,为叶子节点
?2: 该节点有一个孩子
? ? ? ? ?3:该节点有两个孩子
?
第一种情况是最简单的,同时第二种情况也不难,只需要将该节点的一个孩子取代这个节点就可以了,这两种情况的处理可以合二为一,具体实现如代码所示
?
下面考虑节点有两个孩子的情况:
?
? ? ? 假设待删除的节点为N,但现在N有两个孩子,如果试图删除它,就会留下两个没有双亲的孩子,尽管节点N可以被其中一个所取代,但总会有一个孩子没有双亲,即总有一个孩子没有被引用,所以删除节点N 的办法是不可行的.
? ? ?实际上,并不是非得删除节点N才能删除其中的元素,这里,不防寻找一个容易删除的节点,我们称为A,这个节点A只有一个孩子或者没有孩子,然后将节点N中的元素用A中的元素替代,随后删除节点A,并使树中仍然还有正确的元素,但是这里的一个问题,如何确定这个节点A,并保证树仍然为二叉查找树呢? 显然,节点A是不可以随便选择的,它必须有可以合法的替代节点N中的元素的条件。
? ? ? 假设e为N中的元素,因为节点N有两个孩子,则e不可能是树中最小的元素,也不可能是最大元素,(为了讨论方便,假设树中没有重复元素), ?并且书中没有重复的元素,若树中元素升序排列,所以有:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ...a<e<b...(a,b为节点N左右孩子的元素)
由于a为节点N的左子树中的元素,b为节点N右子树中的元素,且有上述关系,我们可以想到 a将是N左子树中最大的元素,b是右子树中最小的元素。现假定删除了还有a的节点X,并用a替代e,那么现在N的左子树中所有剩下的元素都小于a,满足所需条件,而且N的右子树中的所有的元素都大于e,从而也大于a,因此该树仍是二叉查找树.
?
? ? 那么如何寻找元素a呢? ? 假设以上论述能够找到适当的俄元素a,所以现在应寻找含有a的节点并且确认他没有两个孩子,为了找到比任一个指定节点中的元素更大的元素,需要查看这个节点的右孩子,因此,a位于这颗子树最右侧的节点R中, 节点R不可能有右孩子,因为如果他有的话,该孩子的元素就会大于a,因此节点R的孩子不可能多余一个,可以很容易的删除它。
?
下面总结上述讨论(由于节点N的右子树情况类似于左子树,不过右子树中我们只要找右子树中最左侧的节点就可以了):
?
? ? ?1: 删除位于有两个孩子的节点N中的元素e
? ? ? ? ? 找到N的左子树中最右侧的节点R
? ? ? ? ? 以节点R中的元素替代节点N的元素
? ? ? ? ? 删除节点R
?
? ? ?2:?删除位于有两个孩子的节点N中的元素e
? ? ? ? ? ?找到N的右子树中最左侧的节点L,
? ? ? ? ? ?以节点L中的元素替代节点N的元素,
? ? ? ? ? ?删除节点L
?
?
?
?
?
?
?
?
?
?
?