数据结构中的树形结构--二分搜索树、堆、AVL树、Trie、红黑树

一、二分搜索树(Binary Search Tree)

对于树结构,我们大家所熟知的就是二分搜索树了吧。二分搜索树也是我们面试中最常被问到的树结构。
首先,二分搜索树有什么特点呢?

  1. 是动态数据的结构
  2. 是一颗二叉树
  3. 每个节点的值都大于其左子树的所有节点的值;每个节点的值都小于其右子树的所有节点的值
  4. 每一颗子树也是二分搜索树
  5. 存储的元素必须有可比较性, Java中的话就要求二分搜索树保存的数据类型要实现Comparable接口, 或者使用额外的比较器实现
  6. 二分搜索树天然的具有递归特性

下面实现了一个基础的二分搜索树:(运用递归思想)

//定义泛型并且需要继承Comparable接口,使其具有可比较性
public class BST<E extends Comparable<E>> {
   
    private class Node {
   
        private E e;
        public Node left, right;

        public Node(E e) {
   
            this.e = e ;
            left = null ;
            right = null;
        }
    }

    private Node root;
    private int size;
    public BST(){
   
        root = null ;
        size = 0;
    }
    public int size(){
   
        return size;
    }
    public boolean isEmpty(){
   
        return size == 0;
    }

    public void add(E e) {
   
//        if (root == null) {
   
//            root = new Node(e);
//            size++;
//        }
//        else
//            add(root,e);
		//使用递归的方法调用add方法
        root = add(root, e);
    }

    private Node add(Node node, E e) {
   

        if (node == null) {
   
        	//当查到叶子节点的时候,新建一个值为e的节点
            size++;
            return new Node(e);
        }
        //比较新增节点与当前节点的大小
        //大的对当前节点的左子树进行递归
        //小的对当前节点的右子树进行递归
        if (e.compareTo(node.e) < 0) {
   
            node.left = add(node.left,e);
        } else if (e.compareTo(node.e) > 0) {
   
            node.right = add(node.right,e);
        }
        return node;
    }

    //        if (e.equals(node.e))
//            return;
//        else if (e.compareTo(node.e) < 0 && node.left == null) {
   
//            node.left = new Node(e);
//            size ++;
//            return;
//        } else if (e.compareTo(node.e) > 0 && node.right == null) {
   
//            node.right = new Node(e);
//            size++;
//            return;
//        }
//        if (e.compareTo(node.e)<0)
//            add(node.left,e);
//        else
//            add(node.right,e);
//    }
	//递归查找是否包含该节点
    public boolean contains(E e) {
   
        return contains(root,e);
    }
    private boolean contains(Node node ,E e) {
   
        if (node == null) {
   
            return false;
        }
        if (e.compareTo(node.e) == 0) {
   
            return true;
        } else if (e.compareTo(node.e) < 0) {
   
            return contains(node.left,e);
        }else {
   
            return contains(node.right,e);
        }
    }

二分搜索树的遍历
遍历分类:
深度优先遍历 :
前序遍历 : 对当前节点的遍历在对左右孩子节点的遍历之前, 遍历顺序 : 当前节点->左孩子->右孩子
中序遍历 : 对当前节点的遍历在对左右孩子节点的遍历中间, 遍历顺序 : 左孩子->当前节点->右孩子
后序遍历 : 对当前节点的遍历在对左右孩子节点的遍历之后, 遍历顺序 : 左孩子->右孩子->当前节点
广度优先遍历 :
层序遍历 : 按层从左到右进行遍历
代码实现:

	//前序遍历
    public void preOrder(){
   
        preOrder(root);
    }
    private void preOrder(Node node) {
   
        if (node == null) {
   
            return;
        }
        System.out.print(node.e+"  ");
        preOrder(node.left);
        preOrder(node.right);
    }
    //前序遍历的非递归实现 
    private void preOrder1(Node node){
   
    // 使用栈辅助实现前序遍历
		Stack<Node> stack = new Stack<>();
		/*
		 * 前序遍历的过程就是先访问当前节点, 然后再访问左子树, 然后右子树
		 * 所以使用栈实现时, 可以先将当前节点入栈, 当前节点出栈时, 
		 * 分别将当前节点的右孩子, 左孩子压入栈
		 */
		// 首先将根节点压入栈
		stack.push(root);
		while (!stack.isEmpty()) {
   
			Node cur = stack.pop();
			// 前序遍历当前节点
			System.out.println(cur.e) ;
			// 由于栈是后入先出, 前序遍历是先左孩子, 再右孩子, 所以这里需要先将右孩子压入栈
			if (cur.right != null) {
   
				stack.push(cur.right);
			}
			
			if (cur.left != null) {
   
				stack.push(cur.left);
			}
			
		}//中序遍历
    public void inOrder(){
   
        inOrder(root);
    }
    private void inOrder(Node node){
   
        if (node == null) {
   
            return;
        }
        inOrder(node.left);
        System.out.print(node.e+" ");
        inOrder(node.right);
    }
    //后序遍历
    public void aftOrder(){
   
        aftOrder(root);
    }
    private void aftOrder(Node node) {
   
        if (node == null) {
   
            return;
        }
        aftOrder(node.left);
        aftOrder(node.right);
        System.out.print(node.e+" ");
    }

    //二分搜索树的层序遍历
    public void levelOrder() {
   
    	//利用队列进行层序遍历,先进先出
        Queue<Node> q = new LinkedList<>();
        q.add(root);
        //每遍历一个节点,就将该节点的左右子树(如果存在左右子树)入队
        while(!q.isEmpty()){
   
            Node cur = q.remove();
            System.out.print(cur.e+" ");
            if (cur.left!=null)
                q.add(cur.left);
            if (cur.right != null)
                q.add(cur.right);
        }
    }

二分搜索树的删除操作:
删除二分搜索树中的最大值和最小值:

	//通过递归找出最小值
	public E minMun(){
   
        if (size == 0 )
            throw new IllegalArgumentException("BST is null");
        return minMun(root).e;
    }
    private Node minMun(Node node){
   
        if (node.left == null)
            return node;
          return  minMun(node.left);
    }
	 //删除二分搜索树中最小的值的节点,并返回最小值
    public E removeMin(){
   
    	//保存最小节点
        Node e = minMun(root);
        root = removeMin(root);
        return e.e;
    }
    private Node removeMin(Node node){
   
        //判断当前节点是不是最小的节点
        if (node.left == null) {
   
        	//删除当前节点操作
            Node rightNode = node.right;
            node.right = null;
            size -- ;
            return rightNode;
        }
        //如果不是最小节点,通过递归找出最小节点
        node.left = removeMin(node.left);
        return node;
    }
    //找出最大节点
	public E maxMun(){
   
        if (size == 0 )
            throw new IllegalArgumentException("BST is null");
        return maxMun(root).e;
    }
    private Node maxMun(Node node){
   
        if (node.right == null)
            return node;
        return  maxMun(node.right);
    }

	//删除二分搜索树中最大的值的节点,并返回最大值
    public E removeMax(){
   
        Node e = maxMun(root);
        root = removeMax(root);
        return e.e;
    }
    private Node removeMax(Node node){
   
        if (node.right == null) {
   
            Node leftNode = node.left;
            node.left = null
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值