数据结构 Java版二叉排序树

Posted 王景迁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 Java版二叉排序树相关的知识,希望对你有一定的参考价值。

  二叉搜索树,又称为二叉查找树和二叉搜索树。它或者是一颗空树,或者具有下列性质的二叉树。

  1 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值。

  2 若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值。

  3 它的左、右子树也都为二叉搜索树。

  构造一颗二叉搜索树,目的不是为了排序,而是为了提高查找、插入和删除关键字的速度。一个有序数据集上的查找速度总是要快于无序数据集,而二叉搜索树这种非线性的结构,也有利于插入和删除的实现。

  二叉搜索树的查找性能取决于树的高度即二叉树的形态,问题在于二叉搜索树的树高是不确定的。例如{62, 88, 58, 47, 35, 73, 51, 99, 37, 93},可以构建一棵正常的二叉搜索树。但是如果数组元素的次序是从小到大有序,如{35, 37, 47, 51, 58, 62, 73, 88, 93, 99},则二叉搜索树就成了极端的单支树,依然是一棵二叉搜索树。查找结点99,左图只需要两次比较,而右图需要10次比较才能得到结果,差异很大。

  

  二叉搜索树不一定是平衡的,即其深度与完全二叉树不一定相同。

 

// 实现了Comparable接口的元素可以通过compareTo方法来比较
public class BinarySearchTree<E extends Comparable<? super E>>

  

 1 // TreeNode静态嵌套类
 2 private static class TreeNode<E> {
 3     // 元素
 4     private E element;
 5     // 左孩子
 6     private TreeNode<E> left;
 7     // 右孩子
 8     private TreeNode<E> right;
 9     
10     private TreeNode(E e) {
11         element = e;
12         left = null;
13         right = null;
14     }
15 }

 

// 根结点
private BinarySearchTree<E> root;

 

// 无参构造方法
public BinarySearchTree() {
    root = null;
}

 

// 二叉搜索树置空
public void makeEmpty() {
    root = null;
}

 

// 判断树是否为空
public boolean isEmpty() {
    return root == null;
}

 

1 // 获取最小元素
2 public E findMin() {
3     if (isEmpty()) {
4         throw new NullPointerException();
5     }
6     
7     return findMin(root).element;
8 }

 

 1 // 子树上寻找最小值
 2 private TreeNode<E> findMin(TreeNode<E> treeNode) {
 3     if (treeNode == null) {
 4         return null;
 5     }
 6     
 7     while (treeNode.left != null) {
 8         treeNode = treeNode.left;
 9     }
10     
11     return treeNode;
12 }

 

1 // 获取最大元素
2 public E findMax() {
3     if (isEmpty()) {
4         throw new NullPointerException();
5     }
6     
7     return findMax(root).element;
8 }

 

 1 // 子树上寻找最大值
 2 private TreeNode<E> findMax(TreeNode<E> treeNode) {
 3     if (treeNode == null) {
 4         return null;
 5     }
 6     
 7     while (treeNode.right != null) {
 8         treeNode = treeNode.right;
 9     }
10     
11     return treeNode;
12 }

 

  查找操作即判断是否包含指定元素:

  先查找根结点,如果根结点的元素与指定元素相等,则返回true。否则, 如果指定元素大于根结点,则查找右子树;如果小于根结点,则查找左子树。

// 判断是否包含指定元素
public boolean contains(E e) {
    return contains(root, e);
}

 

 1 // 判断是否包含指定元素
 2 private boolean contains(TreeNode<E> treeNode, E e) {
 3     if (treeNode == null) {
 4         return false;
 5     }
 6     
 7     while (treeNode != null) {
 8         int compareResult = e.compareTo(treeNode.element);
 9         if (compareResult == 0) {
10             return true;
11         } else if (compareResult < 0) {
12             treeNode = treeNode.left;
13         } else {
14             treeNode = treeNode.right;
15         }
16     }
17     
18     return false;
19 }

 

  

  插入5以前和以后的二叉搜索树

// 插入指定元素
public void insert(E e) {
    root = insert(root, e);
}

 

 1 // 子树上插入元素
 2 private TreeNode<E> insert(TreeNode<E> treeNode, E e) {
 3     if (treeNode == null) {
 4         return new TreeNode<E>(e, null, null);
 5     }
 6     
 7     int compareResult = e.compareTo(treeNode.element);
 8     if (compareResult < 0) {
 9         treeNode.left = insert(treeNode.left, e);
10     } else if (compareResult > 0) {
11         treeNode.right = insert(treeNode.right, e);
12     }
13     
14     return treeNode;
15 }

 

  删除操作即删除第一次出现指定元素的结点,有3种情况:

  1 叶子结点:直接删除即可。

  2 仅有左或右子树的结点:上移子树即可。

  

  结点4删除前后的情况

  3 有左右子树的结点:用删除结点的直接前驱或者直接后继来替换当前结点,调整直接前驱或者直接后继的位置。

  

// 刪除指定元素
public void remove(E e) {
    root = remove(root, e);
}

 

 1 // 子树上删除元素
 2 private TreeNode<E> remove(TreeNode<E> treeNode, E e) {
 3     if (treeNode == null) {
 4         return null;
 5     }
 6     
 7     int compareResult = e.compareTo(treeNode.element);
 8     TreeNode<E> l = treeNode.left, r = treeNode.right;
 9     if (compareResult < 0) {
10         treeNode.left = remove(l, e);
11     } else if (compareResult > 0) {
12         treeNode.right = remove(r, e);
13     } else if (l != null && r != null) {
14         treeNode.element = findMax(l).element;
15         treeNode.left = remove(treeNode.left, treeNode.element);
16     } else {
17         treeNode = (l != null) ? l : r;
18     }
19     
20     return treeNode;
21 }

 

1 // 遍历树
2 public void printTree() {
3     if (root == null) {
4         throw new NullPointerException();
5     }
6     
7     inorder(root);
8 }

 

 1 // 中序遍历
 2 private void inorder(TreeNode<E> treeNode) {
 3     if (treeNode == null) {
 4         return;
 5     }
 6     
 7     TreeNode<E> l = treeNode.left, r = treeNode.right;
 8     if (l != null) {
 9         inorder(l);
10     }
11     
12     System.out.print(" " + treeNode.element);
13     
14     if (r != null) {
15         inorder(r);
16     }
17 }

 

  完整代码:

  1 // 实现了Comparable接口的元素可以通过compareTo方法来比较
  2 public class BinarySearchTree<E extends Comparable<? super E>> {
  3     // TreeNode静态嵌套类
  4     private static class TreeNode<E> {
  5         // 元素
  6         private E element;
  7         // 左孩子
  8         private TreeNode<E> left;
  9         // 右孩子
 10         private TreeNode<E> right;
 11         
 12         private TreeNode(E e) {
 13             element = e;
 14             left = null;
 15             right = null;
 16         }
 17     }
 18     
 19     // 根结点
 20     private TreeNode<E> root;
 21     
 22     // 无参构造方法
 23     public BinarySearchTree() {
 24         root = null;
 25     }
 26     
 27     // 二叉搜索树置空
 28     public void makeEmpty() {
 29         root = null;
 30     }
 31     
 32     // 判断树是否为空
 33     public boolean isEmpty() {
 34         return root == null;
 35     }
 36     
 37     // 判断是否包含指定元素
 38     public boolean contains(E e) {
 39         return contains(root, e);
 40     }
 41     
 42     // 获取最小元素
 43     public E findMin() {
 44         if (isEmpty()) {
 45             throw new NullPointerException();
 46         }
 47         
 48         return findMin(root).element;
 49     }
 50     
 51     // 获取最大元素
 52     public E findMax() {
 53         if (isEmpty()) {
 54             throw new NullPointerException();
 55         }
 56         
 57         return findMax(root).element;
 58     }
 59         
 60     // 插入指定元素
 61     public void insert(E e) {
 62         root = insert(root, e);
 63     }
 64     
 65     // 刪除指定元素
 66     public void remove(E e) {
 67         root = remove(root, e);
 68     }
 69         
 70     // 遍历树
 71     public void printTree() {
 72         if (root == null) {
 73             throw new NullPointerException();
 74         }
 75         
 76         inorder(root);
 77     }
 78     
 79     // 判断子树上是否包含指定元素
 80     private boolean contains(TreeNode<E> treeNode, E e) {
 81         if (treeNode == null) {
 82             return false;
 83         }
 84         
 85         while (treeNode != null) {
 86             int compareResult = e.compareTo(treeNode.element);
 87             if (compareResult == 0) {
 88                 return true;
 89             } else if (compareResult < 0) {
 90                 treeNode = treeNode.left;
 91             } else {
 92                 treeNode = treeNode.right;
 93             }
 94         }
 95         
 96         return false;
 97     }
 98     
 99     // 子树上寻找最小值
100     private TreeNode<E> findMin(TreeNode<E> treeNode) {
101         if (treeNode == null) {
102             return null;
103         }
104         
105         while (treeNode.left != null) {
106             treeNode = treeNode.left;
107         }
108         
109         return treeNode;
110     }
111     
112     // 子树上寻找最大值
113     private TreeNode<E> findMax(TreeNode<E> treeNode) {
114         if (treeNode == null) {
115             return null;
116         }
117         
118         while (treeNode.right != null) {
119             treeNode = treeNode.right;
120         }
121         
122         return treeNode;
123     }
124         
125     // 子树上插入元素
126     private TreeNode<E> insert(TreeNode<E> treeNode, E e) {
127         if (treeNode == null) {
128             return new TreeNode<E>(e);
129         }
130         
131         int compareResult = e.compareTo(treeNode.element);
132         if (compareResult < 0) {
133             treeNode.left = insert(treeNode.left, e);
134         } else if (compareResult > 0) {
135             treeNode.right = insert(treeNode.right, e);
136         }
137         
138         return treeNode;
139     }
140     
141     // 子树上删除元素
142     private TreeNode<E> remove(TreeNode<E> treeNode, E e) {
143         if (treeNode == null) {
144             return null;
145         }
146         
147         int compareResult = e.compareTo(treeNode.element);
148         TreeNode<E> l = treeNode.left, r = treeNode.right;
149         if (compareResult < 0) {
150             treeNode.left = remove(l, e);
151         } else if (compareResult > 0) {
152             treeNode.right = remove(r, e);
153         } else if (l != null && r != null) {
154             treeNode.element = findMax(l).element;
155             treeNode.left = remove(treeNode.left, treeNode.element);
156         } else {
157             treeNode = (l != null) ? l : r;
158         }
159         
160         return treeNode;
161     }
162     
163     // 中序遍历
164     private void inorder(TreeNode<E> treeNode) {
165         if (treeNode == null) {
166             return;
167         }
168         
169         TreeNode<E> l = treeNode.left, r = treeNode.right;
170         if (l != null) {
171             inorder(l);
172         }
173         
174         System.out.print(" " + treeNode.element);
175         
176         if (r != null) {
177             inorder(r);
178         }
179     }
180     
181     public static void main(String[] args) {
182         BinarySearchTree<String> binarySearchTree = new BinarySearchTree<String>();
183         String[] key = {"62", "88", "58", "47", "35", "73", "51", "99", "37", "93"};
184         for (int i = 0; i < 10; i++) {
185             binarySearchTree.insert(key[i]);
186         }
187         
188         System.out.println("中序遍历结果:");
189         binarySearchTree.printTree();
190         System.out.println();
191         
192         binarySearchTree.remove("58");
193         System.out.println("删除58后中序遍历结果:");
194         binarySearchTree.printTree();
195         System.out.println();
196     }
197 }

  输出结果:

中序遍历结果:
 35 37 47 51 58 62 73 88 93 99
删除58后中序遍历结果:
 35 37 47 51 62 73 88 93 99

 

  参考资料

  二叉排序树(查询、插入、删除)

  《数据结构与算法分析Java语言描述 原书第3版》 P78-86

  《2017年数据结构联考复习指导》 P153

以上是关于数据结构 Java版二叉排序树的主要内容,如果未能解决你的问题,请参考以下文章

数据结构 Java 版二叉树的实现(超多图超详解)

数据结构C语言版二叉树的结构和遍历的实现

数据结构与算法:树 二叉排序树(BST)

数据结构中二叉树的顺序存储结构代码怎么编写?

数据结构与算法:树 二叉排序树(BST)

Java 大话数据结构(11) 查找算法(二叉排序树/二叉搜索树)