二叉树BST的实现
(01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(03) 任意节点的左、右子树也分别为二叉查找树。
(04) 没有键值相等的节点(no duplicate nodes)。
1.遍历
- 2.查找 get
- 3.插入 put
- 4.最大最小 max,min
- 5.向上取整 floor()向下取整 ceiling()
- 6.删除
public class BSTree<Key extends Comparable<Key>,Value>{//有键有值的版本 {
public Node root;
public class Node{
private Key key;//键
private Value val;//值
private Node left;//该树的左结点
private Node right;//该树的右结点
private int N=0;//以该结点为根的子树中的结点数
public Node(Key key,Value value,int N) {
this.key=key;
this.val=value;
this.N=N;
}
}
public int size() {
return size(root);//传根结点就是该树的总结点数
}
public int size(Node x) {
if(x==null) return 0;
return x.N;
}
一、遍历
* 遍历
* (1)若二叉树非空,访问根结点
* (2)遍历左子树
* (3)遍历右子树
* 前序遍历的特点
* 第一个数是根结点
* 第一个比第一个数小的数是根结点的左结点,那一堆比第一个数小的是左子树
* 第一个比第一个数大的数是根结点的右结点,那一堆比第一个树大的是右子树
* 以此递归可以画出原二叉树
public void preOrder(Node x) {
if(x!=null) {
System.out.print(x.key+" ");
preOrder(x.left);
preOrder(x.right);
}
public void preOrder() {
preOrder(root);
System.out.println();
}
中序遍历
-
1.先访问左结点
- 2.访问根结点
- 3.访问右结点
- 中序特点
- 根所在的左边位置都比根小,根所在右边都比根大
*/
public void inOrder() {
inOrder(root);
System.out.println();
}
public void inOrder(Node x) {
if(x!=null) {
inOrder(x.left);
System.out.print(x.val+" ");
inOrder(x.right);
}
}
后序遍历
- 1.先遍历左子树
- 2.遍历右子树
- 3.遍历根结点
- 后续遍历后的特点:
- 最后一个数是根结点
- 前面比最后一个数小的一堆是左子树结点
- 比最后一个树大的一堆是右子树结点
- 以此递归可以画出原二叉树
public void postOrder() {
postOrder(root);
System.out.println();
}
public void postOrder(Node x) {
if(x!=null) {
postOrder(x.left);
postOrder(x.right);
System.out.print(x.val+" ");
}
}
二、查
递归的思路
* 1.当x为null 的时候就是没有,找不到
* 2.缩小问题规模
* 当只有一个根结点和一个左子结点一个右子结点三个结点的时候
* 如果根结点就是要找到那个cmp==0,返回根结点的值
* 如果cmp>0,要找到比根结点大,那么return (get一遍的返回),判断右结点存在与否是否等于根节点
* 如果cmp<0,要找到比根结点小,返回return (get一遍的返回),判断左结点存在与否是否等于根节点
public Value get(Key key) {
return get(root,key);
}
public Value get(Node x,Key key) {
if(x==null)
return null;
int cmp=key.compareTo(x.key);//比较的是键
if(cmp>0) //要找的比父结点大
return get(x.right,key);//从右子结点开始找
else if(cmp<0)//要找的比父结点小
return get(x.right,key);//从左子结点开始找
else return x.val;
}
三、插
关于这里调用的理解
* 1.调用时 一步一步往下搜索,从树根到树底
* 2.调用完了后,一个一个关闭时,从那个插入的树底到树顶,计算每个结点的子结点个数,以此类推
*
* 这里的递归
* 1.当x==null的时候return 一个新节点就是这个插入的结点
* 2.缩小问题的规模
* 假如这个数只有根结点一个结点
* 大于根结点,则让右结点为新插入的结点,小于根结点则让左结点为新插入的结点
* x.N就等于左结点的结点数+右结点的结点数+1;
* return x;
public void put(Key key,Value value) {
root=put(root,key,value);
}
public Node put(Node x,Key key,Value val) {
if(x==null)
return new Node(key,val,1);//创建新的结点
int cmp=key.compareTo(x.key);
if(cmp>0)
x.right=put(x.right,key,val);
else if(cmp<0)
x.left=put(x.left,key,val);
else x.val=val;//如果结点已经存在,更替值即可
x.N=size(x.left)+size(x.right)+1;//左子结点+右子结点+本身
return x;
}
四、最大最小值
/
1.最小值
递归思路
缩小问题规模+何时return。
假设只有根结点和一个左孩子,一个右孩子,共三个结点,易知,左结点为空则return 根节点的值
左结点不为空则return 左结点的值
2.最大值
递归思路
缩小问题规模+何时return。
假设只有根结点和一个左孩子,一个右孩子,共三个结点,易知,右结点为空则return 根节点的值
右结点不为空则return 右结点的值
public Key min() {
return min(root).key;
}
public Node min(Node x) {
if(x.left==null)
return x;
return min(x.left);
}
public Key max() {
return max(root).key;
}
public Node max(Node x) {
if(x.right==null)
return x;
return max(x.right
五、向上取整和向下取整
floor()找出小于等于key的最大的键
* 递归思路
* 缩小问题规模+何时return
*假如只有一个根结点一个左孩子一个右孩子3个结点,如果key<根结点,那么那个值一定return 左孩子
*如果key>根节点,那个值有可能在右孩子,如果右孩子比key大,直接返回根,否则return 右孩子
*
ceiling()找出大于等于key的最大的键
递归思路
* 缩小问题规模+何时return
*假如只有一个根结点一个左孩子一个右孩子3个结点,如果key>根结点,那么那个值一定return 右孩子
*如果key<根节点,那个值有可能在左孩子,如果左孩子比key大,直接返回根结点,否则return 左孩子
*
public Key floor(Key key) {
Node x= floor(root,key);
if(x==null)
return null;
return x.key;
}
public Node floor(Node x,Key key) {
if(x==null)
return null;
int cmp=key.compareTo(x.key);
if(cmp<0) {
return floor(x.left,key);
}
if(cmp==0)
return x;
Node t=floor(x.right,key);
if(t!=null)
return t;
return x;
}
public Key celling(Key key) {
Node x= celling(root,key);
if(x==null)
return null;
return x.key;
}
public Node celling(Node x,Key key) {
if(x==null)
return null;
int cmp=key.compareTo(x.key);
if(cmp>0) {
return celling(x.right,key);
}
if(cmp==0)
return x;
Node t=celling(x.left,key);
if(t!=null)
return t;
return x
六、删除
-
1.删除最小值
-
递归
思路
* 何时return+缩小问题规模
* 如果有一个根结点,一个左孩子,一个右孩子。一共三个结点。
* 最小值一定在左孩子这边,一直查左孩子,直到左孩子为空的时候,为了让左孩子与根结点断开,那么让根结点的右孩子变成左孩子
* 即如果x.left==null,return x.right;
* 左结点不为空,继续查x.left=deletemin(x.left); 最后return x;
* 这里还涉及到,调用一层层关闭的时候是在往上走,那么结点的个数就会重新计算。
*
* 2.删除最大值
* 同理
* 当右结点为null的时候,返回左结点
*
public void deleteMin() {
root=deleteMin(root);
}
public Node deleteMin(Node x) {
if(x.left==null)
return x.right;
x.left=deleteMin(x.left);
x.N=size(x.left)+size(x.right)+1;
return x;
}
public void deleteMax() {
root=deleteMax(root);
}
public Node deleteMax(Node x) {
if(x.right==null)
return x.left;
x.right=deleteMax(x.right);
x.N=size(x.left)+size(x.right)+1;
return x;
}
3删除任意结点
*
* 思路:
* 1.找
* 2.只有一个子结点
* 像删除最大最小值那个一样
* x.right为ull return x.left
* x.left为null return x.rigt
* 若左右连接都存在
* (1)将即将被删除的结点链接保存为t
* (2)将x指向它的后继结点min(t.right),关于后继结点就是要替代原来t结点的位置,那么那个后继结点一定在t的右链接且一定是右链接里面最小的那个结点。为什么呢?因为后继结点x要大于t.left的所有结点,小于t.right的所有结点,这是固定的插入时候的顺序。
* (3)x的右链接指向deleteMin(t.right),后继结点的右链接就是原本的右链接删掉最小值后的链接
* (4)x的左链接指向原本的左链接。x.left=t.left;
*
public void delete(Key key) {
root=delete(root,key);
}
public Node delete(Node x,Key key) {
if(x==null)
return null;
int cmp=key.compareTo(x.key);
if(cmp>0)
x.right=delete(x.right,key);
else if(cmp<0)
x.left=delete(x.left,key);
else {
if(x.left==null)
return x.right;
if(x.right==null)
return x.left;
Node t=x;
x=min(t.right);
x.right=deleteMin(t.right);
x.left=t.left;
}
x.N=size(x.left)+size(x.right)+1;
return x;
}