二叉搜索树——《算法导论》学习心得(十二)

本文详细介绍了二叉搜索树(BST)的基本概念、Java代码实现及其优化策略,包括插入、删除、搜索等操作,以及如何通过旋转保持树的平衡,避免退化为链表,提高查找效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、算法 

     二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树。 它或者是一棵空树;或者是具有下列性质的二叉树: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值; (2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值; (3)左、右子树也分别为二叉排序树;

 不得不说二叉查找树的插入和删除非常麻烦,可谓是费了九牛二虎之力才把代码写完。支持Insert,Delete,Search,Min,Max,Successor,Predecessor等操作。BST在数据结构中占有很重要的地位,一些高级树结构都是其的变种,例如AVL树、红黑树等,因此理解BST对于后续树结构的学习有很好的作用。

二、java代码

<span style="font-size:14px;">package com.tangbo;

public class BinarySearchTree<T extends Comparable<T>> {
	Node<T> root;
	public BinarySearchTree() {
		super();
	}
	/*
	 * 插入一个元素T t
	 */
	public void insert(T t) {
		Node<T> node = new Node<T>(t);
		if(node.getKey()==null)
		{
			throw new IllegalArgumentException("插入的元素不能为空!");
		}
		if(root == null)
		{
			root = node;
		}else
		{
			Node<T> temp = root;
			while(true)
			{
				if(node.getKey().compareTo(temp.getKey())>0)
				{
					if(temp.getRightNode()==null)
					{
						node.setParentNode(temp);
						temp.setRightNode(node);
						break;
					}else
					{
						temp = temp.getRightNode();
					}
				}else
				{
					if(temp.getLeftNode()==null)
					{
						node.setParentNode(temp);
						temp.setLeftNode(node);
						break;
					}else
					{
						temp = temp.getLeftNode();
					}
				}
			}
		}
	}
	//计算该节点的有几个孩子节点
	private int childCount(Node<T> node) {
		if (node == null) {
			throw new IllegalArgumentException("节点不能为空");
		}
		int count = 0;
		if (node.getLeftNode() != null) {
			count++;
		}

		if (node.getRightNode() != null) {
			count++;
		}
		return count;
	}
	//删除一个节点
	public void delete(Node<T> node) {
		if (node == null) {
			throw new IllegalArgumentException("删除节点不能为空!");
		}
		int childCount = childCount(node);
		Node<T> parentNode = node.getParentNode();
		if (childCount == 0) {
			if (parentNode == null) {
				root = null;
			} else {
				if (node == parentNode.getLeftNode()) {
					parentNode.setLeftNode(null);
				} else {
					parentNode.setRightNode(null);
				}
			}
		} else if (childCount == 1) {
			if (parentNode == null) {
				if (node.getLeftNode() != null) {
					root = node.getLeftNode();
					node.getLeftNode().setParentNode(null);
				} else {
					root = node.getRightNode();
					node.getRightNode().setParentNode(null);
				}
			} else {
				if (node == parentNode.getLeftNode()) {
					if (node.getLeftNode() != null) {
						parentNode.setLeftNode(node.getLeftNode());
						node.getLeftNode().setParentNode(parentNode);
					} else {
						parentNode.setLeftNode(node.getRightNode());
						node.getRightNode().setParentNode(parentNode);
					}
				} else {
					if (node.getLeftNode() != null) {
						parentNode.setRightNode(node.getLeftNode());
						node.getLeftNode().setParentNode(parentNode);
					} else {
						parentNode.setRightNode(node.getRightNode());
						node.getRightNode().setParentNode(parentNode);
					}
				}
			}
		} else {
			//后继没有左孩子
			Node<T> successor = min(node);

			if (successor != node.getRightNode()) {
				transplant(successor, successor.getRightNode());

				successor.setRightNode(node.getRightNode());
				node.getRightNode().setParentNode(successor);
			}

			transplant(node, successor);

			successor.setLeftNode(node.getLeftNode());
			node.getLeftNode().setParentNode(successor);
		}
	}
	private void transplant(Node<T> u, Node<T> v) {
		if (u == null) {
			throw new IllegalArgumentException("节点不能为空");
		}

		if (u.getParentNode() == null) {
			root = v;
		} else if (u == u.getParentNode().getLeftNode()) {
			u.getParentNode().setLeftNode(v);
		} else {
			u.getParentNode().setRightNode(v);
		}

		if (v != null) {
			v.setParentNode(u.getParentNode());
		}
	}
	//查找
	public Node<T> search(T key) {
		Node<T> temp = root;
		int result;
		while(root!=null)
		{
			result = key.compareTo(temp.getKey());

			switch (result) {
			case 0:
				return temp;
			case 1:
				temp = temp.getRightNode();
				break;
			case -1:
				temp = temp.getLeftNode();
				break;
			}
		}
		return null;
	}
	//求节点rootNode下的最小节点
	public Node<T> min(Node<T> rootNode) {
		if(rootNode == null)
		{
			throw new IllegalArgumentException("参考节点不能为空!");
		}else
		{
			while(rootNode.getLeftNode()!=null)
			{
				rootNode = rootNode.getLeftNode();
			}
		}
		return rootNode;
	}
	//返回rootNode下最大的节点
	public Node<T> max(Node<T> rootNode) {
		if(rootNode == null)
		{
			throw new IllegalArgumentException("参考节点不能为空!");
		}else
		{
			while(rootNode.getRightNode()!=null)
			{
				rootNode = rootNode.getRightNode();
			}
		}
		return rootNode;
	}
	//返回一个节点的后继
	public Node<T> successor(Node<T> rootNode) {
		if(rootNode==null)
		{
			throw new IllegalArgumentException("参考节点不能为空!");
		}
		if(rootNode.getRightNode()!=null)
		{
			return min(rootNode.getRightNode());
		}
		Node<T> processNode = rootNode;
		Node<T> parent = processNode.getParentNode();  //向上迭代
		while (parent != null && processNode == parent.getRightNode()) {  
			processNode = parent;  
			parent = processNode.getParentNode();  
		}
		return parent;
	}
	//返回一个节点的前驱
	public Node<T> predecessor(Node<T> rootNode) {
		if(rootNode==null)
		{
			throw new IllegalArgumentException("参考节点不能为空!");
		}
		if(rootNode.getLeftNode()!=null)//有左孩子的情况
		{
			return max(rootNode.getLeftNode());
		}
		Node<T> processNode = rootNode;
		Node<T> parent = processNode.getParentNode();//向上迭代
		while (parent != null && processNode == parent.getLeftNode()) {
			processNode = parent;
			parent = processNode.getParentNode();
		}
		return parent;
	}
	//根据数组t构建二叉树
	public Node<T> buidBinarySearchTree(T[] t) 
	{
		for(int i=0;i<t.length;i++)
		{
			insert(t[i]);
		}
		return root;
	}
}
</span>

Node类:

<span style="font-size:14px;">package com.tangbo;

public class Node<T extends Comparable<T>> {
	private Node<T> leftNode;
	private T key;
	private Node<T> rightNode;
	private Node<T> parentNode;
	public Node() {
		super();
	}
	public Node(T t) {
		key = t;
		leftNode=null;
		rightNode = null;
		parentNode = null;
	}
	public Node<T> getLeftNode() {
		return leftNode;
	}
	public void setLeftNode(Node<T> leftNode) {
		this.leftNode = leftNode;
	}
	public Node<T> getRightNode() {
		return rightNode;
	}
	public void setRightNode(Node<T> rightNode) {
		this.rightNode = rightNode;
	}
	public Node<T> getParentNode() {
		return parentNode;
	}
	public void setParentNode(Node<T> parentNode) {
		this.parentNode = parentNode;
	}
	public T getKey() {
		return key;
	}
	public void setKey(T key) {
		this.key = key;
	}
	@Override
	public String toString() {
		return key+"";
	}
}
</span>

     
 如果大家认真的看代码,你会发现一个问题二叉搜索树对根节点有一个要求,最好根节点在选择的时候就能够处于数组的中间,因为如果根节点处于数组的两边(如果数组排序),就会导致这颗二叉树不平衡,就是总有一边的树枝太长,左右两边不太对等,更差的情况,如果这个数组T[] t已经被排序的话,那么这个二叉树就完全是一个链表,查找的速度是最差的,和平时的数组查找时间一样。那我们想一想,有没有什么方法,能够保持数时平衡的,就是左右子数的高度差不多呢?答案肯定有,就是“旋转”,具体有AVL树,在后续的文章中,我会把AVL树写出来!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值