【数据结构】二叉搜索树

概述

本文讲解二叉搜索树的一些基本概念和性质,实现了二叉搜索树的一些常用方法,包括:添加元素、删除元素、计算给定节点node的前驱节点、计算给定节点node的后继节点。

二叉树的性质

  • 任意一个节点的值都大于其左子树所有节点的值
  • 任意一个节点的值都小于其右子树所有节点的值
  • 二叉搜索树所存储的元素必须具备可比较性,且不能为null

二叉搜索树常用方法代码实现

本文二叉搜索树的代码继承了上一节所讲的二叉树,如果对代码中的部分属性和方法有疑惑可查阅二叉树部分
注意:代码部分所讲的前驱和后继节点为中序遍历中的前驱和后继。例:中序遍历为 (a,e,d,c,f),则d的前驱为e,后继为c。

添加元素

public class BinarySearchTree<E> extends BinaryTree{

	private Comparator<E> comparator;
	public  BinarySearchTree() {
		this(null);
	}
	public  BinarySearchTree(Comparator<E> comparator) {
		this.comparator = comparator;
	}
	
	private int compare(E e1,E e2) {
		if (comparator != null) {
			return comparator.compare(e1, e2);
		}
		return ((Comparable<E>)e1).compareTo(e2);
	}
	//添加元素
	public void add(E element) {
		elementNotNullCheck(element);
		//添加第一个节点
		if (root == null) {
			root = new Node<>(element, null);
			size++;
			return;
		}
		//添加的不是第一个节点
		//找到父节点
		Node<E> parent = root;
		Node<E> node = root;
		int cmp = 0;
		while (node != null) {
			cmp = compare(element,node.element);
			parent = node;
			if (cmp > 0) {
				node = node.right;
			}
			else if (cmp < 0) {
				node = node.left;
			}
			else {
				node.element = element;
				return;
			}
		}
		//看看插入到父节点的哪个位置
		Node<E> newNode = new Node<>(element, parent);
		if (cmp > 0) {
			parent.right = newNode;
		}
		else {
			parent.left = newNode;
		}
		size ++;
	}
}

计算给定节点node的前驱节点

实现思路:

  • 分三种种情况:
    • node的左子树不为空,则前驱节点为其左子树中的最右边的节点(此节点为子树中的最大值,即比node小的所有节点中的最大值为node的前驱节点)
    • node的左子树为空,但有父节点,则从其所有父节点和祖父节点中找到的第一个比node小的节点就是node的前驱节点(即比node小的所有节点中的最大值为node的前驱节点)
    • node的左子树为空,所有父节点和祖父节点都比node大,则node没有前驱节点
	private Node<E> predecessor(Node<E> node) {
		if (node == null) return null;
		Node<E> p = node.left;
		if (p != null) {
			while (p.right != null) {
				p = p.right;
			}
			return p;
		}
		while(node.parent != null && node == node.parent.left) {
			node = node.parent;
		}
		return node.parent;
	}

计算给定节点node的后继节点

思路:与计算前驱节点类似

  • 分三种情况:
    • node的右子树不为空,则后继节点为其右子树中的最左边的节点(此节点为子树中的最小值,即比node大的所有节点中的最小值为node的后继节点)
    • node的右子树为空,但有父节点,则从其所有父节点和祖父节点中找到的第一个比node大的节点就是node的后继节点(即比node大的所有节点中的最小值为node的后继节点)
    • node的右子树为空,所有父节点和祖父节点都比node小,则node没有后继节点
	private Node<E> successor(Node<E> node) {
		if (node == null) return null;
		Node<E> p = node.right;
		if (p != null) {
			while (p.left != null) {
				p = p.left;
			}
			return p;
		}
		while(node.parent != null && node == node.parent.right) {
			node = node.parent;
		}
		return node.parent;
	}

删除元素

实现思路:

  • 设根节点为root,要删除的节点为node,其子节点为child(node.left或者node.right)。
  • 分三种情况:
    • 删除度为0的节点:
      • 若要删除的节点为其父节点的左子节点,则将其父节点的左子节点赋值为null
      • 若要删除的节点为其父节点的右子节点,则将其父节点的右子节点赋值为null
      • 若要删除的节点的父节点为null,则要删除的节点为根节点,直接将根节点赋值为null
    • 删除度为1的节点:
      • 若要删除的节点为其父节点的左子节点,则令child.parent = node.parent,node.parent.left = child
      • 若要删除的节点为其父节点的右子节点,则令child.parent = node.parent,node.parent.right = child
      • 若要删除的节点为根节点,则令root = child,child.parent = null
    • 删除度为2的节点:
      • 先用node节点的前驱或者后继节点的值覆盖node节点的值
      • 然后删除相应的前驱或者后继节点
      • 如果一个节点的度为2,则其前驱和后继节点的度一定为1或者0
	//删除元素
	public void remove(E element) {
		remove(node(element));
	}
	
	private void remove(Node<E> node) {
		if(node == null) return;
		size--;
		if(node.hasTwoChild()) { //度为2的节点
			//找到后继节点
			Node<E> s = successor(node);
			//用后继节点的值覆盖度为2的节点的值
			node.element = s.element;
			//删除后继节点
			node = s;
		}
		//删除node节点(node的度必然是1或者0)
		Node<E> replacement = node.left != null ? node.left : node.right;
		if (replacement != null) { //node是度为1的节点
			//更改parent
			replacement.parent = node.parent;
			//更改parent的left、right的指向
			if (node.parent == null) { //node是度为1的节点并且是根节点
				root = replacement;
			}else if (node == node.parent.left) {
				node.parent.left = replacement;
			}else {	//node == node.parent.right
				node.parent.right = replacement;
			}
		}else if (node.parent == null) { //node是叶子节点并且是根节点
			root = null; 
		}else {	//node是叶子节点,但不是根节点
			if(node == node.parent.left) {
				node.parent.left = null;
			}else {	//node == node.parent.right
				node.parent.right = null;
			}
		}
	}
	
	//根据element获取节点
	private Node<E> node(E element) {
		Node<E> node = root;
		while(node != null) {
			int cmp = compare(element, node.element);
			if (cmp == 0) return node;
			if (cmp > 0) {
				node = node.right;
			}else {
				node = node.left;
			}
		}
		return null;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值