本文目录
前两篇文章我们学习了一些树的基本概念以及常用操作, 本篇我们了解一下二叉树的一种特殊形式: 二叉排序树 (Binary Sort Tree), 又称二叉查找树 (Binary Search Tree), 亦称二叉搜索树.
一, 二叉排序树定义
二叉排序树或者是一颗空树, 或者是具有下列性质的二叉树:
若它的左子树不为空, 则左子树上所有结点的值均小于它的根结点的值
若它的右子树不为空, 则右子树上所有结点的值均大于它的根结点的值
它的左, 右子树也分别为二叉排序树
也就是说二叉排序树中左子树结点值均小于根结点值, 右子树节点值均大于跟节点值, 左右子树同样满足上述约定.
如下图, 即为一颗二叉排序树:
由二叉树定义知道, 我们通过中序遍历二叉树就可以按照从小到大顺序排列二叉树中所有元素.
如上图中序遍历结果为: 35, 40, 42, 45, 50, 67
二, java 代码实现二叉排序树核心方法
下面我们通过 java 代码实现二叉排序树中的几个核心方法
我们先看下每个结点类定义如下:
- class TreeNode{
- private int data;
- private TreeNode leftChild;
- private TreeNode rightChild;
- private TreeNode parent;
- public TreeNode(int data) {
- this.data = data;
- this.leftChild = null;
- this.rightChild = null;
- this.parent = null;
- }
- }
很简单, 每个结点记录自己以及左右孩子, 父类的信息.
二叉排序树的创建 (增加元素方法)
创建一颗二叉排序树就是不断往里面添加元素.
整体思路为:
判断整棵树根结点是否创建过, 如果没有创建那么第一个加入进来的元素指定为根结点, 方法返回.
如果二叉排序树已经创建过, 那么再往里面加入元素需要先找出其父节点, 然后将要插入的元素挂载到父节点下即可.
经过上面过程找出其父结点, 这里只需创建节点, 挂载到父节点下即可, 指定为父节点左孩子还是右孩子只需比较一下元素大小即可.
源码:
- public TreeNode put(int data){
- TreeNode node = root;
- TreeNode parent = null;
- // 判断二叉排序树根结点是否存在, 不存在则创建
- if (root == null){
- root = new TreeNode(data);
- return root;
- }
- // 查找其父类
- while (node != null){
- parent = node;// 记录其父亲节点
- if (data> node.data){
- node = node.rightChild;
- }else if (data <node.data){
- node = node.leftChild;
- }else {
- // 已经存在则直接返回
- return node;
- }
- }
- // 创建新节点并插入原有树中
- node = new TreeNode(data);
- if (data < parent.data){
- parent.leftChild = node;
- }else {
- parent.rightChild = node;
- }
- node.parent = parent;
- return node;
- }
二叉排序树的查找
二叉排序树中查找比较简单, 思路为:
当前结点与查找的数据比较, 相等则返回
若小于当前结点则从左子树查找即可
若大于当前结点则从右子树查找即可
重复上述过程, 这里就看出二分查找思想了
源码:
- public TreeNode searchNode(int data) {
- TreeNode node = root;
- if (node == null){
- return null;
- }else {
- while (node != null && data != node.data){
- if (data < node.data){
- node = node.leftChild;
- }else {
- node = node.rightChild;
- }
- }
- }
- return node;
- }
二叉排序树的删除
二叉排序树的删除操作分 4 中情况:
若要删除的结点无左右孩子也就是叶子结点, 那么直接删除即可, 将其父节点左或者右孩子置 null 即可
若要删除的结点有左孩子无右孩子, 则只需要将删除结点的左孩子与其父节点建立关系即可
若要删除的结点有右孩子无左孩子, 则只需要将删除结点的右孩子与其父节点建立关系即可
若要删除的结点左右孩子均有, 就需要选一个结点将其替换, 这里需要保证选取的结点保证比左子树都大, 右子树都小, 可以选取左子树中最大的结点, 或者右子树中最小的结点, 并且需要将选取的结点从二叉排序树中删除.
源码: 这里我们选取右子树最小的结点
- public void deleteNode(int data){
- TreeNode node = searchNode(data);
- if (node == null){
- throw new RuntimeException("未找到要删除的节点");
- }else {
- delete(node);
- }
- }
- private void delete(TreeNode node) {
- if (node == null){
- throw new RuntimeException("未找到要删除的节点");
- }else {
- TreeNode parent = node.parent;
- // 删除的节点无左右孩子
- if (node.leftChild == null && node.rightChild == null){
- if (parent.leftChild == node){
- parent.leftChild = null;
- }else {
- parent.rightChild = null;
- }
- return;
- }
- // 删除的节点有左无右
- if (node.leftChild != null
- && node.rightChild == null){
- if (parent.leftChild == node){
- parent.leftChild = node.leftChild;
- }else {
- parent.rightChild = node.leftChild;
- }
- return;
- }
- // 删除的节点有右无左
- if (node.leftChild == null
- && node.rightChild != null){
- if (parent.leftChild == node){
- parent.leftChild = node.rightChild;
- }else {
- parent.rightChild = node.rightChild;
- }
- return;
- }
- // 删除的结点左右都有
- TreeNode rightMinNode = getRightMinNode(node.rightChild);
- delete(rightMinNode);
- node.data = rightMinNode.data;
- }
- }
- // 获取右子树最小的结点
- private TreeNode getRightMinNode(TreeNode node) {
- TreeNode minNode = node;
- while (minNode != null && minNode.leftChild != null){
- minNode = minNode.leftChild;
- }
- System.out.println("minNode" + minNode.data);
- return minNode;
- }
接下来我们测试一下
测试代码:
- SearchBinaryTree ss = new SearchBinaryTree();
- int[] array = {77,88,34,55,66,2,34,67,78};
- for (int data : array) {
- ss.put(data);
- }
- ss.midIter(ss.getRoot());
- System.out.println();
- SearchBinaryTree.TreeNode node = ss.searchNode(66);
- System.out.println("find node:"+node.getData());
- ss.deleteNode(66);
- SearchBinaryTree.TreeNode dnode = ss.searchNode(66);
- if (dnode != null){
- System.out.println("find node:"+node.getData());
- }else {
- System.out.println("not find node");
- }
- ss.midIter(ss.getRoot());
打印信息如下:
- 2 34 55 66 67 77 78 88
- find node:66
- not find node
- 2 34 55 67 77 78 88
三, 二叉排序树性能问题
二叉排序树最好的情况下其查找性能是很高的, 接近二分查找法.
但是在有些情况下构建出的二叉排序树类似一个链表, 其查找性能为 O(n), 如下图:
构建出这样的树肯定不是我们希望的, 需要调整此树达到平衡的效果, 这里就需要二叉平衡树了 (AVL 树), 关于 AVL 树会在后续篇章介绍, 这里知道二叉平衡树有这个问题就可以了.
四, 总结
本篇主要介绍了二叉平衡树以及 Java 代码实现其核心方法, 希望你能掌握其与普通二叉树的区别, 以及其存在的问题, 好了, 本片到此为止, 希望对你有用.
声明: 文章将会陆续搬迁到个人公众号, 以后也会第一时间发布到个人公众号, 及时获取文章内容请关注公众号
最后附上整个类的全部源码, 拷贝过去就可以用了:
- public class SearchBinaryTree {
- private TreeNode root;// 二叉树根结点
- public TreeNode getRoot() {
- return root;
- }
- // 中序遍历二叉排序树: 按照从小到大排序
- public void midIter(TreeNode node){
- if (node == null){
- return;
- }
- midIter(node.leftChild);
- System.out.print(" "+node.data);
- midIter(node.rightChild);
- }
- public TreeNode put(int data){
- TreeNode node = root;
- TreeNode parent = null;
- // 判断二叉排序树根结点是否存在, 不存在则创建
- if (root == null){
- root = new TreeNode(data);
- return root;
- }
- // 查找其父类
- while (node != null){
- parent = node;// 记录其父亲节点
- if (data> node.data){
- node = node.rightChild;
- }else if (data < node.data){
- node = node.leftChild;
- }else {
- // 已经存在则直接返回
- return node;
- }
- }
- // 创建新节点并插入原有树中
- node = new TreeNode(data);
- if (data < parent.data){
- parent.leftChild = node;
- }else {
- parent.rightChild = node;
- }
- node.parent = parent;
- return node;
- }
- public void deleteNode(int data){
- TreeNode node = searchNode(data);
- if (node == null){
- throw new RuntimeException("未找到要删除的节点");
- }else {
- delete(node);
- }
- }
- private void delete(TreeNode node) {
- if (node == null){
- throw new RuntimeException("未找到要删除的节点");
- }else {
- TreeNode parent = node.parent;
- // 删除的节点无左右孩子
- if (node.leftChild == null && node.rightChild == null){
- if (parent.leftChild == node){
- parent.leftChild = null;
- }else {
- parent.rightChild = null;
- }
- return;
- }
- // 删除的节点有左无右
- if (node.leftChild != null
- && node.rightChild == null){
- if (parent.leftChild == node){
- parent.leftChild = node.leftChild;
- }else {
- parent.rightChild = node.leftChild;
- }
- return;
- }
- // 删除的节点有右无左
- if (node.leftChild == null
- && node.rightChild != null){
- if (parent.leftChild == node){
- parent.leftChild = node.rightChild;
- }else {
- parent.rightChild = node.rightChild;
- }
- return;
- }
- // 删除的结点左右都有
- TreeNode rightMinNode = getRightMinNode(node.rightChild);
- delete(rightMinNode);
- node.data = rightMinNode.data;
- }
- }
- private TreeNode getRightMinNode(TreeNode node) {
- TreeNode minNode = node;
- while (minNode != null && minNode.leftChild != null){
- minNode = minNode.leftChild;
- }
- System.out.println("minNode" + minNode.data);
- return minNode;
- }
- public TreeNode searchNode(int data) {
- TreeNode node = root;
- if (node == null){
- return null;
- }else {
- while (node != null && data != node.data){
- if (data < node.data){
- node = node.leftChild;
- }else {
- node = node.rightChild;
- }
- }
- }
- return node;
- }
- public class TreeNode{
- private int data;
- private TreeNode leftChild;
- private TreeNode rightChild;
- private TreeNode parent;
- public TreeNode(int data) {
- this.data = data;
- this.leftChild = null;
- this.rightChild = null;
- this.parent = null;
- }
- public int getData() {
- return data;
- }
- public void setData(int data) {
- this.data = data;
- }
- public TreeNode getLeftChild() {
- return leftChild;
- }
- public void setLeftChild(TreeNode leftChild) {
- this.leftChild = leftChild;
- }
- public TreeNode getRightChild() {
- return rightChild;
- }
- public void setRightChild(TreeNode rightChild) {
- this.rightChild = rightChild;
- }
- public TreeNode getParent() {
- return parent;
- }
- public void setParent(TreeNode parent) {
- this.parent = parent;
- }
- }
- }
来源: https://www.cnblogs.com/leipDao/p/10058144.html