BST树(二叉搜索树、二叉排序树)
BST树概念
-
什么是BST树
一棵空树或者是具有下列性质的二叉树:
每个结点都有一个作为搜索依据的关键码( key ) ,所有结点的关键码互不相同
左子树(如果存在)上所有结点的关健码都小于根结点的关键码
右子树(如果存在)上所有结点的关键码都大于根结点的关键码
左子树和右子树也是二叉搜索树
示例:
-
特点:中序遍历树可以得到一个递增的排序序列
-
注意:二叉排序树与大小根堆不同
- 大根堆特点:根节点大于左右孩子节点,左右子树以此类推
- 小根堆特点:根节点大于左右孩子节点,左右子树以此类推
- 二叉排序树:根节点大于左孩子节点小于右孩子节点,左右子树以此类推
BST树节点结构
static class BSTNode{
int key;
BSTNode leftchild;
BSTNode parent;
BSTNode rightchild;
public BSTNode() {
}
public BSTNode(int key) {
this.key = key;
leftchild = null;
parent = null;
rightchild = null;
}
public BSTNode(int key, BSTNode leftchild, BSTNode parent, BSTNode rightchild) {
this.key = key;
this.leftchild = leftchild;
this.parent = parent;
this.rightchild = rightchild;
}
}
BST树实现
-
查找节点
-
非递归查找
public boolean findValue(int val){ cur = root; boolean flag = false; while (cur != null && cur.key != val){ cur = cur.key > val ? cur.leftchild : cur.rightchild; } if(cur != null && cur.key == val){ flag = true; } return flag; }
-
递归查找
public boolean curFindValue(int val,BSTNode ptr){ if (ptr == null){ return false; } if(ptr.key == val){ return true; }else if(ptr.key > val){ return curFindValue(val,ptr.leftchild); }else { return curFindValue(val,ptr.rightchild); } }
-
-
插入节点
-
插入思路:插入分为两种情况,第一种非常简单就是当树为空的时候,new根节点就可以完成,第二种就是树不为空的时候,就需要先找到插入位置,在找的时候还需要去判断要插入的节点在树中是否存在,如果存在则插入失败,找到位置后进行插入操作。要注意的就是插入是挂到树叶子节点上,那么在插入时要保存要插入位置的双亲节点,示例如图:要将6插入就需要将6挂在5的右孩子节点
-
代码实现
public boolean insert(int val){ if(isEmpty()){ //树为空的插入 root = new BSTNode(val); root.parent = null; return true; }else { cur = root; BSTNode temp = cur; //找到插入位置 while (cur != null){ temp = cur; //在树中有此节点时则插入失败 if(cur.key == val){ return false; } cur = cur.key > val ? cur.leftchild : cur.rightchild; } //进行插入 if(val < temp.key){ temp.leftchild = new BSTNode(val); temp.leftchild.parent = temp; }else { temp.rightchild = new BSTNode(val); temp.rightchild.parent = temp; } } return true; }
-
-
树的中序遍历(非递归实现)
public boolean isBSTree(){ int temp = Integer.MIN_VALUE; if(isEmpty()){ return true; } Stack<BSTNode> stack = new Stack<>(); cur = root; while (cur != null || !stack.isEmpty()){ while (cur != null){ stack.push(cur); cur = cur.leftchild; } cur = stack.pop(); if(cur.key < temp){ return false; }else{ temp = cur.key; } cur = cur.rightchild; } return true; }
-
判断树是否是BST树
-
思路:利用BST树的中序遍历特点,如果遍历时有两个相邻的值不递增说明当前树不是BST树
-
代码实现:
public boolean isBSTree(){ //记录上一个遍历的节点,和当前节点比较 int temp = Integer.MIN_VALUE; if(isEmpty()){ return true; } Stack<BSTNode> stack = new Stack<>(); cur = root; while (cur != null || !stack.isEmpty()){ while (cur != null){ stack.push(cur); cur = cur.leftchild; } cur = stack.pop(); if(cur.key < temp){ return false; }else{ temp = cur.key; } cur = cur.rightchild; } return true; }
-
-
删除节点
-
思路:
-
树为空时不删除
-
找不到删除节点时不删除
-
存在删除节点分为两种情况
-
删除的节点为双分支节点
找到删除节点的直接后继节点,交换节点,转换为第二种情况(删除单分支节点或叶子节点)
-
删除的节点为单分支节点或叶子节点
- 删除节点为普通节点:找到要删除节点的孩子节点,将孩子节点直接连接在删除节点的双亲节点上
- 删除节点为根节点:找到要删除节点的孩子节点,将孩子节点修改为根节点
-
-
-
代码:
public boolean removeNode(int val){ //树为空 || 树中无此节点 if(isEmpty() || !findValue(val)){ return false; } //找到要删除的节点 BSTNode temp = getNode(val); if(temp == null){ return false; } BSTNode parent = temp.parent; //删除节点为双分支节点,将删除双分支节点转为删除叶子节点 if(temp.leftchild != null && temp.rightchild != null){ BSTNode next = Next(temp.rightchild); temp.key = next.key; parent = next.parent; temp = next; } //删除的节点为单分支节点 BSTNode child = temp.leftchild != null ? temp.leftchild : temp.rightchild; if(child != null){ child.parent = parent; } if(parent == null){ //删除的节点为根节点 root = child; }else{ if(temp.key < parent.key){ parent.leftchild = child; }else{ parent.rightchild = child; } } return true; } private BSTNode Next(BSTNode ptr) { while (ptr != null && ptr.leftchild != null){ ptr = ptr.leftchild; } return ptr; }
-
-
使用parent域对树中序遍历(从小到大)
private BSTNode first(BSTNode ptr){ while (ptr != null && ptr.leftchild != null){ ptr = ptr.leftchild; } return ptr; } private BSTNode next(BSTNode ptr){ if(ptr == null){ return ptr; } if(ptr.rightchild != null){ return first(ptr.rightchild); }else{ BSTNode pa = ptr.parent; while (pa != null && pa.leftchild != ptr){ ptr = pa; pa = ptr.parent; } return pa; } } public void NiceInOrder(){ if(isEmpty()){ return; } for (BSTNode ptr = first(root);ptr != null;ptr = next(ptr)){ System.out.print(ptr.key + " "); } System.out.println(); }
-
使用parent域对树中序遍历(从大到小)
private BSTNode last(BSTNode ptr){ while (ptr != null && ptr.rightchild != null){ ptr = ptr.rightchild; } return ptr; } private BSTNode prev(BSTNode ptr){ if(ptr == null){ return ptr; } if(ptr.leftchild != null){ return last(ptr.leftchild); }else{ BSTNode pa = ptr.parent; while (pa != null && pa.rightchild != ptr){ ptr = pa; pa = ptr.parent; } return pa; } } public void resNiceInOrder(){ if(isEmpty()){ return; } for (BSTNode ptr = last(root);ptr != null;ptr = prev(ptr)){ System.out.print(ptr.key + " "); } System.out.println(); }
-
二叉搜索树转双向链表(非递归)
public void bstreeToLinkedList(){ if(root == null){ return; } Stack<BSTNode> stack = new Stack<>(); BSTNode cur = root; BSTNode pre = null; while (cur != null || !stack.isEmpty()){ while (cur != null){ stack.push(cur); cur = cur.leftchild; } cur = stack.pop(); if(pre == null){ root = cur; }else{ pre.rightchild = cur; cur.leftchild = pre; } pre = cur; cur = cur.rightchild; } }