二叉搜索树

     说到二叉搜索树,就让我想到了本科时期大寒假写代码的那段时期。大冷天没空调一个人拿着笔记本在楼下客厅照着书敲代码,当时敲的就是二叉搜索树,老爸让我快上楼睡觉,我非不肯,因为代码没看懂。就是那个晚上,我不幸感冒了,所以和二叉搜索树还是有很深的感觉的,哈哈哈。

   二叉搜索树和前面的堆结构一样,都有一个重要的规则。它的规则是一个节点的左子树上面的元素一定不大于自己,右子树上面的元素一定不小于自己。就是这么个简单的规则,为我们提供了很多相当有趣的性质。

  先来看一副二叉搜索树的图:













   可以看到上面的节点值完全符合我们的要求,用公式表示为:leftchild.key <= key < rightchild.key。

   这里特别强调一下,二叉搜索树不一定是完全二叉树,有可能是一条直线。

(补充:满二叉树数是二叉树已饱和,不能在固定的层上再添加元素,完全二叉树是只有最后一层为满)

   数的遍历有前序遍历,中序遍历,后序遍历。这三种的区别是输出节点的顺序不一样。

   1)前序遍历是先输出自己,再遍历左子树,最后遍历右子树

   2)中序遍历是先输出左子树,再输出自己,最后遍历右子树

   3)后序遍历是先输出左子树,再输出右子树,最后输出自己

  中序遍历是可以完全顺序输出的,即从小到大输出。

   一般我们已递归的方式完成遍历:

private void preorder(TreeNode tNode)
	{
		if (tNode != null) {
			preorder(tNode.getlChild());
			System.out.println(tNode);
			preorder(tNode.getrChild());
		}
	}</span>

   其实也可以不用递归的方式,可以借助栈结构,将自身压入栈中,知道左子树尽头,然后出栈,输出自己,再处理右子树,压栈循环。。。。
private void preorder(TreeNode rootNode)
	{
		Stack<TreeNode> nodeStack = new Stack<>();
		TreeNode tNode = rootNode;
		while (tNode != null || !nodeStack.empty()) {
			if (tNode == null) {
				tNode = nodeStack.pop();
				System.out.println(tNode);
				tNode = tNode.getrChild();
			} else {
				nodeStack.push(tNode);
				tNode = tNode.getlChild();
			}
		}
				
	}</span>

   其他的二种遍历方式都是大同小异。

   二叉搜索树可以完成很多种动作: insert, delete, search, findMin, findMax, successor(后继节点), predecessor(前继节点)。

   这些动作里面除了delete情况比较复杂,其他的都很简单。下面我们来着重说一下delete的过程:

   1)如果该tnode节点是叶子节点,那么用null取代tnode

   2)如果该tnode节点只有一个孩子,那么用这个孩子取代tnode

   3)如果该tnode节点有两个孩子,那么找tnode节点的后继节点,然后用后继节点替代它。这里有两种情况处理方式不一样,如果后继节点是tnode的右孩子,那么直接替换;如果不是,那么用后继节点的右孩子取代它(因为后继节点左孩子为null),然后再用后继节点取代tnode。

  对于第三种情况,我们也可以采用递归的方式解决,不管后继节点是不是tnode的孩子节点,我们先递归delete后继节点,然后用后继节点取代tnode就可以,这种方式代码很简洁也很好理解,值得推荐。

  先写个移植子树的函数:

private void transplant(TreeNode uNode, TreeNode vNode)
	{
		if (uNode == null)
			root = vNode;
		else {
			if (uNode.getParent() == null)
				root = vNode;
			else if (uNode == uNode.getParent().getlChild())
				uNode.getParent().setlChild(vNode);
			else {
				uNode.getParent().setrChild(vNode);
			}
			if (vNode != null)
				vNode.setParent(uNode.getParent());
		}
	}</span>

然后我们来分别实现一下这两种方法。

  老方法(非递归):

 

private void delete(TreeNode tNode)
	{
		if (tNode.getlChild() == null)
			transplant(tNode, tNode.getrChild());
		else if (tNode.getrChild() == null)
			transplant(tNode, tNode.getlChild());
		else {
			TreeNode replaceNode = successor(tNode);
			if (replaceNode != tNode.getrChild()) {
				transplant(replaceNode, replaceNode.getrChild());
				//处理replaceNode的右孩子
				replaceNode.setrChild(tNode.getrChild());
				replaceNode.getrChild().setParent(replaceNode);
			}
			transplant(tNode, replaceNode);
			//处理replaceNode的左孩子
			replaceNode.setlChild(tNode.getlChild());
			replaceNode.getlChild().setParent(replaceNode);
		}
	}</span>
 
  推荐方法(递归):
private void delete(TreeNode tNode)
	{
		if (tNode.getlChild() == null)
			transplant(tNode, tNode.getrChild());
		else if (tNode.getrChild() == null)
			transplant(tNode, tNode.getlChild());
		else {
			TreeNode replaceNode = successor(tNode);
			delete(replaceNode);
			tNode.setKey(replaceNode.getKey());
		}
	}</span>

看看同样的时间复杂度,代码量是不是小很多,而且逻辑简单清晰很多。我们可以知道递归最多也就2层深度,因为后继节点无左子树。

下面完全的二叉搜索树代码:

 * 
 */
package binaryTree;

import java.util.Stack;

import sun.reflect.generics.tree.Tree;

/**
 * @author freestyle458
 *
 */
public class BinarySearchTree {
	private TreeNode root;
	
	public static class TreeNode {
		private int key;
		private TreeNode lChild;
		private TreeNode rChild;
		private TreeNode parent;
		
		public TreeNode()
		{
			key = 0;
			lChild = null;
			rChild = null;
			parent = null;
		}
		
		public TreeNode(int value)
		{
			key = value;
			lChild = rChild = parent = null;
		}

		public int getKey()
		{
			return key;
		}

		public void setKey(int key)
		{
			this.key = key;
		}

		public TreeNode getlChild()
		{
			return lChild;
		}

		public void setlChild(TreeNode lChild)
		{
			this.lChild = lChild;
		}

		public TreeNode getrChild()
		{
			return rChild;
		}

		public void setrChild(TreeNode rChild)
		{
			this.rChild = rChild;
		}

		public TreeNode getParent()
		{
			return parent;
		}

		public void setParent(TreeNode parent)
		{
			this.parent = parent;
		}
		
		@Override
		public String toString()
		{
			// TODO Auto-generated method stub
			String result = "";
			
			if (lChild == null)
				result += "[leftChild:null, ";
			else
				result += "[leftChild:" + lChild.getKey() + ", ";
			result += "key:" + key + ", ";
			
			if (parent != null)
				result += "parent:" + parent.getKey() + ", ";
			else 
				result += "parent:null, ";
			
			if (rChild == null)
				result += "rightChild:null]";
			else 
				result += "rightChild:" + rChild.getKey() + "]";
			
			return result;

		}
	}
	//lchild.key <= key < rchild.key
	public BinarySearchTree()
	{
		// TODO Auto-generated constructor stub
		root = null;
	}
	
	public BinarySearchTree(int key)
	{
		TreeNode node = new TreeNode(key);
		root = node;
	}
	
	public boolean isContain(int value)
	{
			return isContain(root, value);
	}
	
/*	private boolean isContain(TreeNode t, int value)
	{
		if (t == null)
			return false;
		else {
			if (t.getKey() == value)
				return true;
			else if (t.getKey() < value)
				return isContain(t.getrChild(), value);
			else {
				return isContain(t.getlChild(), value);
			}
		}
	}*/
	
	private boolean isContain(TreeNode rootNode, int value)
	{
		if (search(rootNode, value) == null)
			return false;
		else 
			return true;
	}
	
	private TreeNode search(TreeNode rootNode, int value)
	{
		TreeNode xNode = rootNode;
		while (xNode != null && xNode.getKey() != value) {
			if (xNode.getKey() < value)
				xNode = xNode.getrChild();
			else
				xNode = xNode.getlChild();
		}
		return xNode;
	}
	
	public int findMin()
	{
		TreeNode tNode = findMin(root);
		if (tNode == null)
			return -1;
		else
			return tNode.getKey();
	}
	
	private TreeNode findMin(TreeNode tNode)
	{
		while (tNode.getlChild() != null) {
			tNode = tNode.getlChild();
		}
		return tNode;
	}
	
	public int findMax()
	{
		TreeNode tNode = findMax(root);
		if (tNode == null)
			return -1;
		else 
			return tNode.getKey();
	}
	
	private TreeNode findMax(TreeNode tNode)
	{
		while (tNode.getrChild() != null)
			tNode = tNode.getrChild();
		return tNode;
	}
	
	private TreeNode successor(TreeNode tNode)
	{
		if (tNode.getrChild() != null)
			return findMin(tNode.getrChild());
		else {
			TreeNode parent = tNode.getParent();
			while (parent != null && tNode == parent.getrChild()) {
				tNode = parent;
				parent = tNode.getParent();
			}
			return parent;
		}
	}
	
	private TreeNode predecessor(TreeNode tNode)
	{
		if (tNode.getlChild() != null)
			return tNode.getlChild();
		else {
			TreeNode parent = tNode.getParent();
			while (parent != null && tNode == parent.getlChild()) {
				tNode = parent;
				parent = tNode.getParent();
			}
			return parent;
		}
	}
	
	public void insert(int value) {
		TreeNode tNode = new TreeNode(value);
		insert(root, tNode);
	}
	
	private void insert(TreeNode xNode, TreeNode tNode)
	{
		TreeNode yNode = null;
		while (xNode != null) {
			yNode = xNode;
			if (xNode.getKey() < tNode.getKey())
				xNode = xNode.getrChild();
			else 
				xNode = xNode.getlChild();
		}
		tNode.setParent(yNode);
		if (yNode == null)
			root = tNode;
		else if (yNode.getKey() < tNode.getKey())
			yNode.setrChild(tNode);
		else {
			yNode.setlChild(tNode);
		}
	}
	
	private void transplant(TreeNode uNode, TreeNode vNode)
	{
		if (uNode == null)
			root = vNode;
		else {
			if (uNode.getParent() == null)
				root = vNode;
			else if (uNode == uNode.getParent().getlChild())
				uNode.getParent().setlChild(vNode);
			else {
				uNode.getParent().setrChild(vNode);
			}
			if (vNode != null)
				vNode.setParent(uNode.getParent());
		}
	}
	
	public boolean delete(int value)
	{
		if (!isContain(value))
			return false;
		else {
			TreeNode tNode = search(root, value);
			delete(tNode);
			return true;
		}
	}
	
	private void delete(TreeNode tNode)
	{
		if (tNode.getlChild() == null)
			transplant(tNode, tNode.getrChild());
		else if (tNode.getrChild() == null)
			transplant(tNode, tNode.getlChild());
		else {
			TreeNode replaceNode = successor(tNode);
			delete(replaceNode);
			tNode.setKey(replaceNode.getKey());
		}
	}
	
/*	private void delete(TreeNode tNode)
	{
		if (tNode.getlChild() == null)
			transplant(tNode, tNode.getrChild());
		else if (tNode.getrChild() == null)
			transplant(tNode, tNode.getlChild());
		else {
			TreeNode replaceNode = successor(tNode);
			if (replaceNode != tNode.getrChild()) {
				transplant(replaceNode, replaceNode.getrChild());
				//处理replaceNode的右孩子
				replaceNode.setrChild(tNode.getrChild());
				replaceNode.getrChild().setParent(replaceNode);
			}
			transplant(tNode, replaceNode);
			//处理replaceNode的左孩子
			replaceNode.setlChild(tNode.getlChild());
			replaceNode.getlChild().setParent(replaceNode);
		}
	}*/
	
	public boolean isEmpty()
	{
		return root == null ? true: false;
	}
	
	public void preorder()
	{
		preorder(root);
	}
	
/*	private void preorder(TreeNode tNode)
	{
		if (tNode != null) {
			preorder(tNode.getlChild());
			System.out.println(tNode);
			preorder(tNode.getrChild());
		}
	}*/
	
	private void preorder(TreeNode rootNode)
	{
		Stack<TreeNode> nodeStack = new Stack<>();
		TreeNode tNode = rootNode;
		while (tNode != null || !nodeStack.empty()) {
			if (tNode == null) {
				tNode = nodeStack.pop();
				System.out.println(tNode);
				tNode = tNode.getrChild();
			} else {
				nodeStack.push(tNode);
				tNode = tNode.getlChild();
			}
		}
				
	}
	
	
	public static void main(String[] args)
	{
		BinarySearchTree bsTree = new BinarySearchTree();
		System.out.println("bsTree isEmpty : " + bsTree.isEmpty());
		bsTree.insert(3);
		bsTree.insert(1);
		bsTree.insert(2);
		bsTree.insert(4);
		bsTree.insert(0);
		bsTree.insert(10);
		bsTree.insert(6);
		bsTree.insert(1);
		
		bsTree.preorder();
		System.out.println("bsTree isContain(3) : " + bsTree.isContain(3));
		System.out.println("bsTree minValue is : " + bsTree.findMin());
		System.out.println("-----------------------------------");
		
		bsTree.delete(3);
		System.out.println("hava deleted 3");
		System.out.println("bsTree isContain(3) : " + bsTree.isContain(3));
		bsTree.preorder();
		System.out.println("bsTree isEmpty : " + bsTree.isEmpty());
	}
	
	
	
	
}
</span>

运行结果:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值