二叉搜索树(BST-binary search tree)
1.基本概述
顾名思义:二叉搜索树就是一颗二叉树。具有二叉树的性质
特性:左子节点的值<=根节点的值<=右子节点的值
假设x为BST里的一个节点:
- 如果x.left为x的左孩子里的一个节点,那么 x . l e f t . k e y < = x . k e y x.left.key<=x.key x.left.key<=x.key
- 如果x.right为x的右孩子里的一个节点,那么 x . r i g h t . k e y > = x . k e y x.right.key>=x.key x.right.key>=x.key
- 满足: x . l e f t . k e y < = x . k e y < = x . r i g h t . k e y x.left.key <= x.key<=x.right.key x.left.key<=x.key<=x.right.key
如图为算法导论中的描述。注意在我们用代码实现的时候,不考虑值相等的情况(较为复杂)
2.迭代代码的实现
删除操作在代码里有详细的注释
import java.util.Stack;
/**
* @D:以迭代形式实现一个二叉搜索树
*/
public class BST_Iterative{
Node root;
public BST_Iterative(){
this.root = null;
}
public static void main(String[] args) {
BST_Iterative bs = new BST_Iterative();
bs.insert(7);
bs.insert(8);
bs.insert(5);
bs.insert(3);
bs.insert(2);
bs.insert(1);
bs.inorder();
bs.remove(3);
bs.inorder();
}
/**
* @D:向二叉搜索树中插入节点,如果已经存在,直接返回——在树中不允许插入重复的数据
* @param data
*/
public void insert(int data){
Node parent = null;
Node temp = this.root;//在这里我们用一个临时节点代替root节点
int rightOrLeft = -1;//-1代表根节点,0代表左节点,1代表右节点
//寻找一个正确的位置来及逆行插入
while (temp != null){
if (temp.data>data) { //进入左节点
parent = temp;
temp = parent.left;
rightOrLeft = 0;
}else if(temp.data<data){
parent = temp;
temp = parent.right;
rightOrLeft = 1;
}else{
System.out.println(data+"已经存在");
return;
}
}
//开始进行插入操作
Node newNode = new Node(data);
//try:parent变为rightOrLeft=0;parent.left变为temp;
//this.root是一个全局变量,进行操作的时候尽量用parent这个节点。此时我们始终都没有修改root。不然会将其修改
//这样,建完一棵树,全局变量root就是根节点
if (parent==null) { //parent如果还是null,代表此时根节点是null,将数值插入根节点
this.root = newNode;
}else{
if (rightOrLeft==0) { //左子节点符合条件
parent.left = newNode;
}else{
parent.right = newNode;
}
}
}
/**
* @D:返回一个值是否存在
* @param data
* @return
*/
public boolean find(int data){
Node temp = this.root;
while (temp != null) {
if (temp.data>data) {
temp = temp.left;
}else if (temp.data<data){
temp = temp.right;
}else{
System.out.println(data+"存在");
return true;
}
}
System.out.println(data+"没有找到");
return false;
}
/**
* @D:删除BST中的某个节点
* @param data
*/
public void remove(int data){
Node parent = null;//要删除节点的父节点
Node temp = this.root;//临时节点存储节点自己(要删除的节点)
int rightOrLeft = -1;//记录左右子节点
//找到要删除的节点
while (temp != null) {
if (temp.data ==data) {
break;
}else if(temp.data>data){
parent = temp;
temp = parent.left;
rightOrLeft = 0;
}else{
parent = temp;
temp = parent.right;
rightOrLeft = 1;
}
}
//开始执行删除
/**
* 1. 待删除如果是叶子节点,那么就直接删除
* 2. 待删除节点的左节点或者右节点为空,那么就直接删除,左节点或者右节点上浮(左子树或者右子树上浮)
* 3. 待删除节点的左右孩子(左右子树)都存在——寻找其左子树的最大值所在的节点(寻找其右子树的最小值所在的节点)将这个节点删除并放置到待删除的节点处以保持BST的性质
* 1.待删除节点的右孩子没有左孩子,那么待删除节点的右孩子就是最小值
* 2.沿着左子树下沉,寻找最小节点
*
* 注意:在这里所使用的方法是先用一个replacement节点保存信息——用来代替待删除的节点
*/
//如果temp为空,那么给定的值并不在BST中
if (temp!=null) {
Node replacement; //为被删除的节点存储新的值,同时如果我们上浮某些值的会删除上浮的值
if (temp.right == null && temp.left==null) { //叶子节点
replacement = null;
} else if (temp.right == null) { //右子节点为空,那么左节点直接上浮。注意:这里只是临时存储
replacement = temp.left; //replacement指向temp.left;(注意:temp.left是待删除结点的左子节点,其后面可能还有许多左子节点,这里更改地址)
temp.left = null;
}else if(temp.left == null){
replacement = temp.right;
temp.right = null;
}else{ //左右节点都存在。寻找待删除节点右子树中的最小值(或者左子树中的最大值)
if (temp.right.left==null) { //如果待删除节点的右节点的左节点不存在。那么待删除节点的右节点就是左子树最右边的节点(待删除节点的右子节点就是其右子树的最小值)
temp.data = temp.right.data;
replacement = temp;
temp.right = temp.right.right;//删除temp.right
}else{ //继续寻找待删除节点右子树的最小值
Node parent2 = temp.right;
Node child = temp.right.left; //左节点肯定是最小的。因此不断遍历子节点
while (child.left != null) { //直到左节点为空
parent2 = child;
child = parent2.left;
}
temp.data = child.data;
parent2.left = child.right; //删除左节点,再连接其右子树。(注意,此时parent右子树最小左节点的父节点,child是右子树最小左节点,其右子树肯定小于parent(child再parent的左子树中))
replacement = temp;
}
}
//在执行删除后,更改其父节点的资料
if (parent==null) {
this.root = replacement;//直接代替根节点
}else{
if (rightOrLeft==0) {
parent.left = replacement;
}else{
parent.right = replacement;
}
}
}
}
/**
* 中序遍历,根据二叉搜索树的特性,这里可以按顺序输出
*/
public void inorder() {
if (this.root == null) {
System.out.println("This BST is empty.");
return;
}
System.out.println("Inorder traversal of this tree is:");
Stack<Node> st = new Stack<Node>();
Node cur = this.root;
while (cur != null || !st.empty()) {
while (cur != null) {
st.push(cur);
cur = cur.left;
}
cur = st.pop();
System.out.print(cur.data + " ");
cur = cur.right;
}
System.out.println(); // for next line
}
private class Node{
int data;
Node left;
Node right;
/**
* 构造器
*/
Node(int d) {
data = d;
left = null;
right = null;
}
}
}
3.递归的形式
/**
* 以递归的形式实现一个二叉搜索树
*/
public class BST_Recursive {
Node root;
public BST_Recursive(){
this.root = null;
}
public static void main(String[] args) {
BST_Recursive bs = new BST_Recursive();
bs.add(14);
bs.add(9);
bs.add(16);
bs.add(5);
bs.add(10);
bs.add(15);
bs.add(18);
bs.add(1);
bs.add(6);bs.add(17);bs.add(20);
bs.inorder();
bs.remove(16);
}
/**
* @D:用递归方法删除某个节点
* @param node 当前节点,用来寻找数据(一般从根节点开始寻找)
* @param data 要删除的数据
* @return Node 这个node是根节点,注意在我们运行完程序后,程序是要回溯到最初的那个节点的
*/
private Node delete(Node node,int data){
if (node==null) { //跳出循环
System.out.println("BST里没有这个数据了");
}else if(node.data >data){ //w往左子树进行寻找
node.left = delete(node.left,data);
}else if(node.data<data){ //往右子树进行寻找
node.right = delete(node.right,data);
}else{
if (node.right==null && node.left==null) { //叶子节点,直接标记为null
node = null;
}else if(node.left ==null){ //待删除节点左子树为空,右子树上浮
Node temp = node.right; //保存右子树的地址信息
node.right = null; //删除
node = temp; //更新
}else if(node.right == null){
Node temp = node.left;
node.left = null;
node = temp;
}else{
Node temp = node.right;
while (temp.left != null) { //寻找右子树中的最小值
temp = temp.left;
}
node.data = temp.data; //待删除节点的值更新
node.right = delete(node.right, node.data); //这是什么意思呢?首先确定一点就是node的右子树的所有值肯定要大于node.data的。那么这行代码的意思应该就是要开始回溯了
}
}
return node;
}
public void remove(int data){
this.root = delete(this.root,data);
}
/**
* @D:递归的插入数据
* @param node 一般从根节点开始向下搜寻
* @param data
* @return node 根节点,在运行完程序之后是要回溯到最初的那个节点
*/
private Node insert(Node node,int data){
if (node==null) { //结束天健
node = new Node(data);
}else if (node.data > data) {
node.left = insert(node.left,data);
}else if(node.data < data){
node.right = insert(node.right,data);
}
return node;
}
//对insert方法的调用
public void add(int data){
this.root = insert(this.root,data);
}
/**
* 寻找某个值
* @param node
* @param data
* @return
*/
private boolean search(Node node, int data) {
if (node == null) {
return false;
} else if (node.data == data) {
return true;
} else if (node.data > data) {
return search(node.left, data);
} else {
return search(node.right, data);
}
}
//对search的实现
public boolean find(int data) {
if (search(this.root, data)) {
System.out.println(data + " 在BST树中");
return true;
}
System.out.println(data + " 没有找到.");
return false;
}
/*------------------------------------------------------遍历算法----------------------------------------------------*/
private void preOrder(Node node) {
if (node == null) {
return;
}
System.out.print(node.data + " ");
if (node.left != null) {
preOrder(node.left);
}
if (node.right != null) {
preOrder(node.right);
}
}
private void postOrder(Node node) {
if (node == null) {
return;
}
if (node.left != null) {
postOrder(node.left);
}
if (node.right != null) {
postOrder(node.right);
}
System.out.print(node.data + " ");
}
private void inOrder(Node node) {
if (node == null) {
return;
}
if (node.left != null) {
inOrder(node.left);
}
System.out.print(node.data + " ");
if (node.right != null) {
inOrder(node.right);
}
}
public void inorder() {
System.out.println("Inorder traversal of this tree is:");
inOrder(this.root);
System.out.println(); // for next line
}
public void postorder() {
System.out.println("Postorder traversal of this tree is:");
postOrder(this.root);
System.out.println(); // for next li
}
public void preorder() {
System.out.println("Preorder traversal of this tree is:");
preOrder(this.root);
System.out.println(); // for next li
}
private class Node{
int data;
Node left;
Node right;
public Node(int data) {
this.data = data;
this.left = null;
this.right = null;
}
}
}