二分搜索树(Binary Search Tree)的递归与非递归实现

本文详细介绍了二分搜索树的性质、添加元素、删除元素(包括删除最小值、最大值和任意值)的递归算法,以及递归和非递归实现的遍历方法(前序、中序、后序和层序遍历)。通过代码实例展示了二分搜索树的操作,提供完整代码和相关扩展链接。
摘要由CSDN通过智能技术生成

性质

  • 本质上是一颗二叉树
  • 二分搜索树的每个节点的值大于其左子树的所有节点的值,小于其右子树的所有节点的值。(存储的元素必须要有可比性 )
  • 每一颗子树也都是二分搜索树
  • Talk is cheap. Show you the code.

添加元素

根据二分搜索树的性质,判断被添加元素需要放置的位置,递归直到可放置位置(node为null)时增加节点。

public void add(E e) {
        if (root == null) {
            root = new Node(e);
            size++;
        } else {
            add(root, e);
        }
}
    
private Node add(Node node, E e) {
    
        if (node == null) {
            size++;
            return new Node(e);
        }
        if (e.compareTo(node.e) < 0) {
            node.leftChild = add(node.leftChild, e);
        } else if (e.compareTo(node.e) > 0) {
            node.rightChild = add(node.rightChild, e);
        }
        return root;
    }

删除元素

删除最小值

算法步骤如下:
查找节点
删除节点
递归完成

public E removeMin() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty");
        }
        E e = miniElement();//多余步骤,为了方便测试正确性
        root = removeMin(root);
        return e;
    }

    private Node removeMin(Node root) {
        if (root.leftChild == null) {
            size--;
            return root.rightChild;
        }
        root.leftChild = removeMin(root.leftChild);
        return root;
    }

删除最大值

方法跟删除最小值一样,只要把left、right互换一下就行了。

删除任意值

这里用到了hibbard deletion方法,依然是使用递归方法,找到需要删除的元素,将其左子树的最大值节点或右子树最小节点找出顶替需要被删除的元素,我这里选择了右子树的最小节点。

public void remove(E e) {

	root = remove(root, e);
}

private Node remove(Node node, E e) {

	if (node == null) {
	    return null;
	}
	int cmp = e.compareTo(node.e);
	if (cmp > 0) {
	    node.rightChild = remove(node.rightChild, e);
	} else if (cmp < 0) {
	    node.leftChild = remove(node.leftChild, e);
	} else {
		if (node.rightChild == null) {
		   return node.leftChild;
		}
		if (node.leftChild == null) {
		   return node.rightChild;
		}
		Node tmp = node;
		node = mini(tmp.rightChild);
		node.rightChild = removeMin(tmp.rightChild);
		node.leftChild = tmp.leftChild;
   }

   return node;
}

遍历的递归实现

前序遍历 中序遍历 后序遍历

使用递归方式遍历,前中后的遍历代码几乎一样,只有节点数据记录位置不同,如下。

  public void preOrder() {
        preOrder(root);
    }

    private void preOrder(Node root) {
        if (root == null) {
            return;
        }
        //在这个位置接收节点数据为前序遍历,每次访问根节点的时候记录
        preOrder(root.leftChild);
        //在这个位置接收节点数据为中序遍历,每次访问先记录下左节点
        preOrder(root.rightChild);
        //在这个位置接收节点数据为后序遍历,每次访问先记录下左右节点
    }

遍历的非递归实现

NR前序遍历

前序遍历的规则:
(1)访问根节点(2)前序遍历左子树(3)前序遍历右子树
前序遍历可以说是最简单的了,利用栈结构的先进后出特性,将根节点入栈,随后进入循环开始pop出元素,将该元素的左右节点全部添加入栈,然后依次出栈最新入栈的元素去添加下一个节点

 public void preOrderNonRecursion() {
 
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        
        while (!stack.isEmpty()) {
            Node curr = stack.pop();
            System.out.print(curr.e + " ");
            
            if (curr.rightChild != null) {
                stack.push(curr.rightChild);
            }
            if (curr.leftChild != null) {
                stack.push(curr.leftChild);
            }
        }
 }

NR中序遍历

首先依次将当前节点所有的左节点加入栈中,直到下一个左节点为null,则当前节点为需要遍历的第一个节点,然后依次从栈中弹出已经加入的左节点,并且把该节点的右节点给设为当前节点,如果右节点不存在则继续弹出上一次添加的左节点,如果右节点存在,则重复本步骤。

    public void inOrderNotRecursion() {
        Node curr = root;
        Stack<Node> stack = new Stack<>();
        while (!stack.isEmpty() || curr != null) {

            if (curr != null) {
                stack.push(curr);
                curr = curr.leftChild;
            } else {
                curr = stack.pop();
                System.out.print(curr.e + " ");
                curr = curr.rightChild;
            }
        }
    }

NR后序遍历

后序遍历跟前序遍历正好相反,前序遍历是根左右,后序遍历是左右根。只要将左右子节点的入栈顺序更改一下就能得到逆序的后序遍历结果,因此增加一个栈用来存储结果,就能得到最终的后序遍历序列。

    public void postOrderNotRecursion() {
        Stack<Node> stack = new Stack<>();
        Stack<Node> out = new Stack<>();
        Node curr = root;
        stack.push(curr);
        while (!stack.isEmpty()) {
            curr = stack.pop();
//            System.out.print(curr.e + ",");
            out.push(curr);
            if (curr.leftChild != null) {
                stack.push(curr.leftChild);
            }
            if (curr.rightChild != null) {
                stack.push(curr.rightChild);
            }
        }
        while (!out.isEmpty()) {
            System.out.print(out.pop().e + " ");
        }
    }

层序遍历

层序遍历是一个典型的广度优先遍历。
层序遍历可以维护一个队列来进行辅助遍历,首先将根节点入队,然后出队,将出队的元素的左右子节点依次入队,重复上述步骤即可遍历成功。

    public void layerTraverse() {
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            Node curr = queue.remove();
            System.out.print(curr.e + " ");
            if (curr.leftChild != null) {
                queue.add(curr.leftChild);
            }
            if (curr.rightChild != null) {
                queue.add(curr.rightChild);
            }
        }


    }

完整代码

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

/**
*该数据结构存储内容必须具备可比性,因此使用<E extends Comparable<E>>
*/
public class BST<E extends Comparable<E>> {
 
 	/**
 	*存储节点
 	*/
    private class Node {
        public E e;
        public Node leftChild, rightChild;

        public Node(E e) {
            this.e = e;
            leftChild = null;
            rightChild = null;
        }
 
    }

	//树根
    private Node root;
    private int size;

    public BST() {
        root = null;
        size = 0;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return root == null;
    }

    public void add(E e) {
        if (root == null) {
            root = new Node(e);
            size++;
        } else {
            root = add(root, e);
        }
    }

    //以root为根的二分搜索树中插入元素E,递归算法
    //返回插入新节点后二分搜索树的根
    private Node add(Node node, E e) {
 
        if (node == null) {
            size++;
            return new Node(e);
        }
        if (e.compareTo(node.e) < 0) {
            node.leftChild = add(node.leftChild, e);
        } else if (e.compareTo(node.e) > 0) {
            node.rightChild = add(node.rightChild, e);
        }
        return node;
    }

    public boolean contains(E e) {
        return contains(root, e);
    }

    private boolean contains(Node root, E e) {
        if (root == null) {
            return false;
        }
        if (e.compareTo(root.e) > 0) {
            return contains(root.rightChild, e);
        } else if (e.compareTo(root.e) < 0) {
            return contains(root.leftChild, e);
        } else {
            return true;
        }
    }

    /**
     * 前序遍历
     * Pre-Order Traversal
     */
    public void preOrder() {
        preOrder(root);
    }

    private void preOrder(Node root) {
        if (root == null) {
            return;
        }
        System.out.print(root.e + " ");
        preOrder(root.leftChild);
        preOrder(root.rightChild);
    }

    /**
     * 前序遍历  非递归实现
     */
    public void preOrderNonRecursion() {
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            Node curr = stack.pop();
            System.out.print(curr.e + " ");

            if (curr.rightChild != null) {
                stack.push(curr.rightChild);
            }
            if (curr.leftChild != null) {
                stack.push(curr.leftChild);
            }
        }
    }

    /**
     * 中序遍历
     * 根据二叉树性质中序遍历结果为排序好的顺序
     * In-Order Traversal
     */
    public void inOrder() {
        inOrder(root);
    }

    private void inOrder(Node node) {
        if (node == null) {
            return;
        }
        inOrder(node.leftChild);
        System.out.print(node.e + " ");
        inOrder(node.rightChild);
    }
	 /**
     * 中序遍历
     * 非递归
     */
    public void inOrderNotRecursion() {
        Node curr = root;
        Stack<Node> stack = new Stack<>();
        while (!stack.isEmpty() || curr != null) {

            if (curr != null) {
                stack.push(curr);
                curr = curr.leftChild;
            } else {
                curr = stack.pop();
                System.out.print(curr.e + " ");
                curr = curr.rightChild;
            }
        }
    }

    /**
     * 后续遍历
     * Post-Order Traversal
     */
    public void postOrder() {
        postOrder(root);
    }

    private void postOrder(Node node) {
        if (node == null) {
            return;
        }
        postOrder(node.leftChild);
        postOrder(node.rightChild);
        System.out.print(node.e + " ");
    }

    /**
     * 后序遍历 非递归
     */
    public void postOrderNotRecursion() {
        Stack<Node> stack = new Stack<>();
        Stack<Node> out = new Stack<>();
        Node curr = null;
        stack.push(root);
        while (!stack.isEmpty()) {
            curr = stack.pop();
//            System.out.print(curr.e + ",");
            out.push(curr);
            if (curr.leftChild != null) {
                stack.push(curr.leftChild);
            }
            if (curr.rightChild != null) {
                stack.push(curr.rightChild);
            }
        }
        while (!out.isEmpty()) {
            System.out.print(out.pop().e + " ");
        }
    }

    /**
     * 层序遍历
     */
    public void layerTraverse() {
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            Node curr = queue.remove();
            System.out.print(curr.e + " ");
            if (curr.leftChild != null) {
                queue.add(curr.leftChild);
            }
            if (curr.rightChild != null) {
                queue.add(curr.rightChild);
            }
        }
    }

    /**
     * 查找最小值
     */
    public E miniElement() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty.");
        }
        return mini(root).e;
    }

    private Node mini(Node node) {
        if (node.leftChild == null) {
            return node;
        }
        return mini(node.leftChild);
    }

    /**
     * 查找最大值
     */
    public E maxElement() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty.");
        }
        return max(root).e;
    }

    private Node max(Node node) {
        if (node.rightChild == null) {
            return node;
        }
        return max(node.rightChild);
    }

    /**
     * 删除最小值
     */
    public E removeMin() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty");
        }
        E e = miniElement();
        root = removeMin(root);
        return e;
    }
   /**
     * 删除最小值  
     * @param root 需要删除的树根
     * @return 被删除树树根
     */
    private Node removeMin(Node root) {
        if (root.leftChild == null) {
            size--;
            return root.rightChild;
        }
        root.leftChild = removeMin(root.leftChild);
        return root;
    }

    public E removeMax() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty");
        }
        E e = maxElement();
        root = removeMax(root);
        return e;
    }

    //删除以root为根节点的树种最小的元素,返回新的树根
    private Node removeMax(Node root) {
        if (root.rightChild == null) {
            size--;
            return root.leftChild;
        }
        root.rightChild = removeMax(root.rightChild);

        return root;
    }

    /**
     * 删除任意一个节点
     */
    public void remove(E e) {

        root = remove(root, e);

    }

    private Node remove(Node node, E e) {

        if (node == null) {
            return null;
        }
        int cmp = e.compareTo(node.e);
        if (cmp > 0) {
            node.rightChild = remove(node.rightChild, e);
        } else if (cmp < 0) {
            node.leftChild = remove(node.leftChild, e);
        } else {
            if (node.rightChild == null) {
                return node.leftChild;
            }
            if (node.leftChild == null) {
                return node.rightChild;
            }
            Node tmp = node;
            node = mini(tmp.rightChild);
            node.rightChild = removeMin(tmp.rightChild);
            node.leftChild = tmp.leftChild;

        }

        return node;
    }

扩展链接

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值