算法研究系列---二叉查找树

2 篇文章 0 订阅

查找树以便于查找的方式来存放数据,尤其是二叉查找树,二叉查找树的特性使其可以使用简单的递归算法进行查找,这种算法在思路上类似于数组的折半查找,且同样高效.

 

 二叉查找树是其节点含有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

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值