二叉搜索树及 Java 实现
定义
由一棵二叉树构成,满足:
$$
left.key<key<=right.key
$$
进一步有:
$$
key<=right.key<parent.key
$$
大部分二叉搜索树的最坏运行时间与树的高度成正比
1 2 3 4 5
| Tree parent; Tree left; Tree right; var key;
|
遍历方法
1.中序遍历:
通过递归方法,先找出左节点,再找出当前节点,最后是右节点。
定理:若树节点为n
,则调用该遍历方法需要O(n)
的时间。
2.先序遍历
先当前节点,后左节点,最后右节点。
3.后序遍历
先左节点,后右节点,最后当前节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public void inorder() { inorder(this); } private void inorder(Tree node) { if (node.left != null) inorder(node.left); System.out.println(node.key); if (node.right != null) inorder(node.right); }
public void preorder() { preorder(this); } private void preorder(Tree node) { System.out.println(node.key); if (node.left != null) preorder(node.left); if (node.right != null) preorder(node.right); }
public void postorder() { postorder(this); } private void postorder(Tree node) { System.out.println(node.key); if (node.left != null) postorder(node.left); if (node.right != null) postorder(node.right); }
|
查找
给定一关键字key,返回第一个找到的含该关键字的树节点,没有则返回null
。
运行时间为O(h)
,h
为树的高度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public Tree search(int key) { if (key == this.key) return this; if (key < this.key) return left != null ? left.search(key) : null; return right != null ? right.search(key) : null; }
public Tree iterativeSearch(int key) { Tree root = new Tree(); root = this; while (root != null && root.key != key) { if (key < this.key) root = root.left != null ? root.left : null; else root = root.right != null ? root.right : null; } return root; }
|
最大值和最小值:仅需查找左/右节点到头即可,运行时间为O(h)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public Tree minimum(Tree node) { if (node.left == null) return node; else return minimum(node.left); }
public Tree iterativeMinimum(Tree node) { while (node.left != null) node = node.left; return node; }
public Tree maximum(Tree node) { if (node.right == null) return node; else return maximum(node.right); }
public Tree iterativeMaximum(Tree node) { while (node.right != null) node = node.right; return node; }
|
后继和前驱:关键字key的最小大于值和最大小于值(key
均不相等的情况下),运行时间为O(h)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public Tree predecessor(Tree node) { if (node.left != null) return maximum(node.left); Tree node_p = node.parent; while (node_p != null && node == node_p.left) { node = node_p; node_p = node_p.parent; } return node_p; }
public Tree successor(Tree node) { if (node.right != null) return maximum(node.right); Tree node_p = node.parent; while (node_p != null && node == node_p.right) { node = node_p; node_p = node_p.parent; } return node_p; }
|
插入和删除
插入和删除会引发树结构的动态集合的变化。插入相对简单,但删除较为复杂。
插入:从根节点向下遍历到指定位置,实际上为新叶子节点的添加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public void insert(int key) { if (key < this.key) { if (left == null) { left = new Tree(); left.key = key; left.parent = this; } else left.insert(key); } else { if (right == null) { right = new Tree(); right.key = key; right.parent = this; } else right.insert(key); } }
public void iterativeInsert(int key) { Tree parent = null; Tree x = this; while (x != null) { parent = x; if (key < x.key) x = x.left; else x = x.right; } if (key < parent.key) { parent.left = new Tree(); parent.left.key = key; parent.left.parent = parent; } else { parent.right = new Tree(); parent.right.key = key; parent.right.parent = parent; } }
|
删除:有三种情况,有一种较为棘手,设该节点为z
。
z
节点没有子节点,则仅将对应父节点连接的子节点位置修改为null
;
z
节点有一个子节点,则将该子节点与父节点相连;
z
节点有两个子节点,则应寻找其后继y
,将y
代替z
,z
的子节点和父节点改接到y
上,并将原y
的父节点连接的子节点位置修改为null
。该情况较为棘手,还涉及y
是否为z
右子节点的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public void delete(Tree node) { if (node.left == null) transPlant(node, node.right, false); else if (node.right == null) transPlant(node, node.left, true); else { Tree next = successor(node); if (next != node.right) { transPlant(next, next.right, false); next.right = node.right; } transPlant(node, next, false); } }
private void transPlant(Tree node, Tree next, boolean isLeft) { if (isLeft) next.right = node.right; else next.left = node.left; if (node == node.parent.left) node.parent.left = next; else node.parent.right = next; }
|