java之二叉树搜索树(BST)

版权声明:https://me.csdn.net/qq_42079455 https://blog.csdn.net/qq_42079455/article/details/89241535

1、二叉搜索树需满足以下四个条件:

1.若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

2.若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

3.任意节点的左、右子树也分别为二叉查找树;

4.没有键值相等的节点。

如下图所示:
在这里插入图片描述
2、查找二叉树的构建
代码:

package com.wd.digui;

public class Wd_30 {
	public static void main(String[] args) {
		Wd_30 searchBTree = new Wd_30();
		int[] arr = { 10, 10, 23, 5, 1, 45, 67, 9, 100, 66, 20 };
		for (int i : arr) {
			searchBTree.put(i);
		}
		// 中序遍历查找二叉树,结果正好为升序
		searchBTree.inOrder(searchBTree.root);
	}

	private TreeNode root = null;

	// 中序遍历二叉树
	public void inOrder(TreeNode root) {
		if (root != null) {
			inOrder(root.leftChild);
			System.out.print(root.data + " ");
			inOrder(root.rightChild);
		}
	}

	public void put(int data) {
		TreeNode node;
		TreeNode parent = null;
		// 如果跟结点为空,这构造根结点
		if (root == null) {
			node = new TreeNode(data);
			root = node;
		}
		// 每次放入都要从根结点开始比较
		node = root;
		while (node != null) {
			// 将当前结点赋值为父亲结点
			parent = node;
			// 若比当前结点的值大,下个结点赋值为右儿子
			if (data > node.data) {
				node = node.rightChild;
			} else if (data < node.data) {
				node = node.leftChild;
			} else {
				return;
			}
		}
		// 当while循环结结束后,parent结点为当前插入值的父亲结点,在判断当前值应该插入的位置
		if (data > parent.data) {
			node = new TreeNode(data);
				node.parent=parent;
			parent.rightChild = node;
		} else {
			node = new TreeNode(data);
				node.parent=parent;
			parent.leftChild = node;
		}
	}

	class TreeNode {
		int data;
		TreeNode leftChild;
		TreeNode rightChild;
		TreeNode parent;

		public TreeNode(int data) {
			super();
			this.data = data;
		}

	}
}

注意:因为二叉树搜索树的特点,左儿子<双亲结点<右儿子,所以我们可以利用这个特点去检验我们构建的二叉树搜索树是否正确(就是中序遍历二叉树搜索树结果正好为升序)
运行结果:
在这里插入图片描述
接下来再来讲讲二叉树搜索树的删除操作
3、删除操作主要有以下几种情况

1.要删除的节点是一个叶子节点,直接使用她的父节点删除即可。

2.要删除的节点是只有左孩子节点,直接用当前要删除的节点的左孩子替换要删除的节点。

3.要删除的节点是只有右孩子节点,直接用当前要删除的节点的右孩子替换要删除的节点。

4.要删除的节点是既有左孩子,又有右孩子,

首先我们需要找到哪个值来覆盖当前要删除的节点,很明显,就是刚刚把她大的那个数。也就是她的直接中序后继节点,也就是当前节点的右子树中 值最小的节点,并且此中序后继节点一定不含子节点或者只含有一个右孩子(换句话说就是当前结点子树中最左边的儿子)

找到该节点以后把值赋给当前要删除的节点即可。并删除该直接中序后继节点 (如果没有子节点的话),如果有子节点 需要把他的右子节点移动到他的位置。

代码:

public void delete(TreeNode node) {
		if (!isExist(node.data)) {
			return;
		}
		// 判断删除结点类型,根据结点类型执行对应的删除方法
		if (isLeafNode(node)) {
			deleteLeafNode(node);
		} else if (isOnlyLeftNode(node)) {
			deleteOnlyOneChild(node, true);
		} else if (isOnlyRightNode(node)) {
			deleteOnlyOneChild(node, false);
		} else {
			deleteChildren(node);
		}
	}
// 删除叶子结点
	private void deleteLeafNode(TreeNode node) {
		TreeNode parent = node.parent;
		if (node.data > parent.data) {
			parent.rightChild = null;
		} else {
			parent.leftChild = null;
		}
	}
// 删除只有一个儿子的结点
	private void deleteOnlyOneChild(TreeNode node, boolean isLeftChild) {
		TreeNode parent = node.parent;
		if (isLeftChild) {
			parent.data = node.data;
			parent.leftChild = null;
		} else {
			parent.data = node.data;
			parent.rightChild = null;
		}
	}
// 删除有两个儿子的结点
	private void deleteChildren(TreeNode node) {
		TreeNode leftestNode = getLeftest(node);
		node.data = leftestNode.data;
		if (leftestNode.rightChild == null) {
			deleteLeafNode(leftestNode);
		} else {
			TreeNode parent = leftestNode.parent;
			parent.leftChild = leftestNode.rightChild;
		}

	}
  //判断结点是否存在
	public boolean isExist(int data) {
		if (root == null) {
			return false;
		}
		TreeNode curNode = root;
		while (curNode.data != data) {
			if (data > curNode.data) {
				curNode = curNode.rightChild;
			}
			if (data < curNode.data) {
				curNode = curNode.leftChild;
			}
			if (isLeafNode(curNode) && curNode.data != data) {
				return false;
			}
		}
		return true;
	}
    //判断是否为叶子结点
	private boolean isLeafNode(TreeNode node) {
		return node.leftChild == null && node.rightChild == null;
	}
	 //判断当前结点是否只有左儿子
	private boolean isOnlyLeftNode(TreeNode node) {
		return node.leftChild != null && node.rightChild == null;
	}
	 //判断当前结点是否只有右儿子
	private boolean isOnlyRightNode(TreeNode node) {
		return node.leftChild == null && node.rightChild != null;
	}

	// 获取当前结点,子树中最左边的儿子结点
	private TreeNode getLeftest(TreeNode node) {
		while (node.leftChild != null) {
			node = node.leftChild;
		}
		return node;
	}
	// 根据值获取对应结点
	public TreeNode getTreeNode(int data) {
		if (root == null) {
			return null;
		}
		TreeNode curNode = root;
		while (curNode.data != data) {
			if (data > curNode.data) {
				curNode = curNode.rightChild;
			}
			if (data < curNode.data) {
				curNode = curNode.leftChild;
			}
		}
		return curNode;
	}
public static void main(String[] args) {
		Wd_30 searchBTree = new Wd_30();
		int[] arr = { 10, 23, 5, 1, 45, 8, 9, 40, 66, 20 };
		for (int i : arr) {
			searchBTree.put(i);
		}
		// 中序遍历查找二叉树,结果正好为升序
		searchBTree.inOrder(searchBTree.root);

		System.out.println();
        //删除值为23的结点
		searchBTree.delete(searchBTree.getTreeNode(23));
		searchBTree.inOrder(searchBTree.root);

	}

运行结果:
在这里插入图片描述
4、总结:

BST效率 : 查找最好时间复杂度O(logN),最坏时间复杂度O(N)(BST退化成单支树结构),插入删除操作算法简单,时间复杂度与查找差不多

另外关于avl(平衡查找树)以rbt以及B-,b+tree
1、红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。
2、红黑树能够以O(log2 n) 的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。当然,还有一些更好的,但实现起来更复杂的数据结构,能够做到一步旋转之内达到平衡。但红黑树能够给我们一个比较“便宜”的解决方案。红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高。大量数据实践证明,RBT的总体统计性能要好于平衡二叉树。AVL树更平衡一些,适合查找多的应用,红黑树插入删除更快一些
3、B-,b+tree 树大多用于数据库中,可以有效地降低磁盘读取次数

猜你喜欢

转载自blog.csdn.net/qq_42079455/article/details/89241535