JAVA 实现 - 二叉搜索树

二叉搜索树

二叉搜索树/二叉查找树/二叉排序树

特点:

  • 树节点增加key属性,用来比较谁大谁小,key不可以重复
  • 对于任意一个树节点,它的key比左子树的key都大,同时也比右子树的key都大
/**
 * 二叉搜索树
 */
public class BSTree1 {

    TreeNode root;

    static class TreeNode {
        int key;  //节点的健
        Object value; //节点的值
        TreeNode left;
        TreeNode right;

        public TreeNode(int key) {
            this.key = key;
        }

        public TreeNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public TreeNode(int key, Object value, TreeNode left, TreeNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }
}

查找元素 - 递归实现

public class BSTree1 {
  
  ...

  public Object get(int key) {
    return doGet(root, key);
  }

  private Object doGet(TreeNode node, int key) {
      if (node == null) { //没找到结束递归
          return null;
      }

      if (key < node.key) { //向左找
          return doGet(node.left, key);
      } else if (key > node.key) { //向右找
          return doGet(node.right, key);
      } else { //找到了
          return node.value;
      }
  }
}

查找元素 - 非递归实现

public class BSTree1 {

  ...

  public Object get(int key){
      TreeNode node = root;
      while(node != null){
          if (key < node.key){ //向左
              node = node.left;
          }else if (key > node.key){ //向右
              node = node.right;
          }else{
              return node.value;
          }
      }

      return null;
  }
}

获取最小健上的值

非递归实现:

package com.datastructure.binarytree.bstree;


/**
 * 二叉搜索树
 */
public class BSTree1 {

    ...

    public Object getMin() {

        if (this.root == null) {
            return null;
        }
        TreeNode node = this.root;
        while (node.left != null) {

            node = node.left;
        }
        return node.value;
    }
}

递归实现:

package com.datastructure.binarytree.bstree;


/**
 * 二叉搜索树
 */
public class BSTree1 {
  
    ...

    /**
     * 递归实现 - 查找最小健对应的值
     */
    public Object getMin(){
        return doGetMin(root);
    }

    private  Object doGetMin(TreeNode node){
        if (node == null){
            return null;
        }
        if(node.left != null){
            return doGetMin(node.left);
        }else {
            return node.value;
        }
    }

插入节点

思路:
1.利用get的思路,找到值时更新
2.找不到节点时插入元素,关键是找到父节点。

package com.datastructure.binarytree.bstree;


/**
 * 二叉搜索树
 */
public class BSTree1 {

    ...
    
    public void put(int key, Object value) {
        TreeNode node = root;
        TreeNode parent = null;
        while (node != null) {
            parent = node;  //node 作为一个指针,记录了父节点
            if (key < node.key) {
                node = node.left;
            } else if (key > node.key) {
                node = node.right;
            } else {
                //1. 找到 key 值, 更新操作
                node.value = value;
                return;
            }
        }


        //2. 没有找到,创建一个新节点插入
        if (parent == null) {
            root = new TreeNode(key,value);
            return;
        }

        if (key < parent.key) { //作为左孩子
            parent.left = new TreeNode(key,value);
        } else { //作为右孩子
            parent.right = new TreeNode(key,value);
        }
    }
}

获取节点的前驱

package com.datastructure.binarytree.bstree;


import com.sun.source.tree.Tree;

/**
 * 二叉搜索树
 */
public class BSTree1 {

 
    ...      


    /**
     * 查找节点的前驱节点
     * 情况1: 节点有左子树,此时前任就是左子树的最大值
     * 情况2: 节点没有左子树,若离它最近的,自左而来的祖先就是前任
     *
     * @param key
     * @return
     */
    public Object predecessor(int key) {
        TreeNode p = this.root;
        TreeNode accessorFromLeft = null;  //记录自左而来的组祖先
        while (p != null) {
            if (key < p.key) {
                p = p.left;
            } else if (p.key < key) {
                accessorFromLeft = p; //记录自左而来的祖先
                p = p.right;
            } else {
                break;
            }
        }

        if (p == null) { // 没有找到 key
            return null;
        }

        if (p.left != null) {  //情况1: 节点有左子树,此时前任就是左子树的最大值
            return getMax(p);  //获取左子树的最大值
        }
        //情况2: 节点没有左子树,若离它最近的,自左而来的祖先就是前任
        return accessorFromLeft != null ? accessorFromLeft.value : null;
    }
}

查找节点的后继节点

package com.datastructure.binarytree.bstree;


import com.sun.source.tree.Tree;

/**
 * 二叉搜索树
 */
public class BSTree1 {

    ...

    /**
     * 查找节点的后继节点:
     * 情况1: 节点有右子树,右子树的最小值即为该节点的后继节点
     * 情况2: 节点没有右子树,离它最近的自右而来的节点即为该节点的后继节点
     */
    public Object sucessor(int key) {
        TreeNode p = root;
        TreeNode accessorFromRight = null; //记录自右而来的祖先节点
        while (p != null) {
            if (key < p.key) {
                accessorFromRight = p;
                p = p.left;
            } else if (p.key < key) {
                p = p.right;
            } else {
                break; // 找到
            }
        }

        if (p == null) { //没找到key
            return null;
        }

        if (p.right != null) {   //情况1: 节点有右子树,右子树的最小值即为该节点的后继节点
            return getMin(p);
        }
        //情况2: 节点没有右子树,离它最近的自右而来的节点即为该节点的后继节点
        return accessorFromRight != null ? accessorFromRight.value : null;
    }

}

删除节点

  1. 删除节点没有左孩子,将右孩子托孤给Parent
  2. 删除节点没有右孩子,将左孩子托孤给Parent
  3. 删除节点左右孩子都没有,已经被涵盖在情况1、2 当中,把 null 托孤给Parent
  4. 删除节点左右孩子都有,可以将它的后继节点(称为S)托孤给Parent,再称S的父节点为SP,又分为两种情况
    4.1 SP就是删除节点,此时D(删除节点)与S紧邻,只需将S托孤给Parent

4.2 SP不是被删除节点,此时D与S不相邻,此时需要将S的后代托孤给SP,再将S 托孤给Parent.

package com.datastructure.binarytree.bstree;


import com.sun.source.tree.Tree;

/**
 * 二叉搜索树
 */
public class BSTree1 {

    ...     

    public Object delete(int key) {
        TreeNode p = root;
        TreeNode parent = null;
        while (p != null) {
            if (key < p.key) {
                parent = p;
                p = p.left;
            } else if (p.key < key) {
                parent = p;
                p = p.right;
            } else {
                break; // 找到
            }
        }

        if(p == null){
            return null;
        }

        //情况1:只有左孩子没有右孩子
        if(p.left != null && p.right == null){
            shift(parent ,p, p.left);
        } else if(p.left == null && p.right != null){  //情况2:只有右孩子没有左孩子
            shift(parent, p, p.right);
        }else if(p.left == null && p.right ==null){  // 情况3:删除节点为叶子节点
            shift(parent, p , null);
        } else{
            //情况4:
            //4.1 寻找后继节点s
            TreeNode s = p.right;
            TreeNode sParent = null;
            while (s.left != null){
                sParent = s;
                s = s.left;
            }
            // 4.11 后继点解与被删节点不相邻
            if(sParent !=  p){
                //处理后继的后事
                shift(sParent, s, s.right);
                s.right = p.right;
            }
            //4.12 后继节点与被删除节点相邻
            //4.2 处理后继的事儿
            shift(parent,p,s);
            //4.3 后继取代删除节点
            s.left = p.left;
        }
        return p.value;
    }

    /**
     * 执行删除操作
     */
    private void shift(TreeNode parent,TreeNode deleted, TreeNode child){
        if(parent == null){  //删除节点为根节点
            root = child;
        }else if(parent.right == deleted){ //被删除节点为父节点的右节点
            parent.right = child;
        }else{
            parent.left = child; //被删除节点为父节点的左节点
        }
    }
}

范围查询

package com.datastructure.binarytree.bstree;


import com.sun.source.tree.Tree;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * 二叉搜索树
 */
public class BSTree1 {
    
    ...


    /*
        查找二叉搜索树比key小的所有value
        思路:采用中序遍历,中序遍历的结果是一个升序
    */
    public List<Object> less(int key) {
        List<Object> result = new ArrayList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();

        TreeNode curr= root;
        while (curr!= null || !stack.isEmpty()) {
            if (curr!= null) {
                stack.push(curr);
                curr= curr.left;
            } else { //往回走
                TreeNode pop = stack.pop();
                //处理值
                if(pop.key < key){
                    result.add(pop.value);
                }else{
                    break; // 因为是中序遍历是升级,一旦发现大于的就可以结束循环
                }
                curr= pop.right;
            }
        }
        return result;

    }


    /*
        查找二叉树搜索树比key大的所有value
        思路:如果采用中序遍历查找的速度慢,因为中序遍历的结果是升序,
            可以使用 反向中序遍历: 右值左
     */
    public List<Object> greater(int key) {
        List<Object> result = new ArrayList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();

        TreeNode curr= root;
        while (curr!= null || !stack.isEmpty()) {
            if (curr!= null) {
                stack.push(curr);
                curr= curr.right;
            } else { //往回走
                TreeNode pop = stack.pop();
                //处理值
                if(pop.key > key){
                    result.add(pop.value);
                }
                curr= pop.left;
            }
        }
        return result;
    }

    /*
        查找 >= key1 并且 <= key2 的所有value
     */
    public List<Object> between(int key1, int key2){
        List<Object> result = new ArrayList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();

        TreeNode curr= root;
        while (curr!= null || !stack.isEmpty()) {
            if (curr!= null) {
                stack.push(curr);
                curr= curr.left;
            } else { //往回走
                TreeNode pop = stack.pop();
                //处理值
                if(pop.key >= key1 && pop.key <= key2){
                    result.add(pop.value);
                } else if (pop.key > key2) {
                    break;
                }
                curr= pop.right;
            }
        }
        return result;
    }
}

判断否是一个合法的二叉搜索树

解法1:运用中序遍历,如果发现前面的节点值比当前节点值大,返回false

class Solution {
    public boolean isValidBST(TreeNode node) {
        TreeNode curr = node;
        LinkedList<TreeNode> stack = new LinkedList<>();
        long prev = Long.MIN_VALUE;
        while (curr != null || !stack.isEmpty()) {
            if (curr != null) {
                stack.push(curr);
                curr = curr.left;
            } else {
                TreeNode pop = stack.pop();
                // 处理值
                if (prev >= pop.val) {
                    return false;
                }
                prev = pop.val;
                curr = pop.right;
            }
        }
        return true;
    }
}

范围求和

package com.datastructure.binarytree;


import java.util.LinkedList;

/**
 * 范围求和: 给定二叉搜索树的根节点root,返回位于 范围[low,high] 之间所有节点的值的和
 */
public class E05Leetcode938 {


    //解法1:中序遍历非递归实现,运行耗时 1ms
    public int rangeSumBST(TreeNode root, int low, int hight) {

        LinkedList<TreeNode> stack = new LinkedList<>();
        int sum = 0;
        TreeNode curr = root;
        while (curr != null || !stack.isEmpty()) {
            if (curr != null) {
                stack.push(curr);
                curr = curr.left;
            } else {
                TreeNode pop = stack.pop();
                //处理值
                if (pop.val >= hight) {
                    break;
                }

                if (pop.val >= low) {
                    sum += pop.val;
                }
                curr = pop.right;
            }
        }
        return sum;
    }


    //解法2:递归求解,运行耗时: 0 ms
    public int rangeSumBST2(TreeNode node, int low, int hight) {

        if (node == null) {
            return 0;
        }
        //情况1: 当前节点小于下限,左子树不再考虑,递归求解右子树累加的结果
        if (node.val < low) {
            return rangeSumBST(node.right, low, hight);
        }

        //情况2:当前节点大于上限
        if (node.val > hight) { //不再考虑右子树,返回左子树的累加结果
            return rangeSumBST(node.left, low, hight);
        }

        // 在[low,hight]
        return node.val + rangeSumBST(node.right, low, hight) + rangeSumBST(node.left, low, hight);
    }
}

前序遍历构造二叉树

解法1:

package com.datastructure.binarytree;


/**
 * 根据前序遍历构造二叉树
 * 题目说明:
 * 1.preorder 长度 >= 1
 * 2.preorder 没有重复值
 */
public class E06Leetcode1008 {

    /*
    8,5,1,7,10,12
        8
       / \
      5   10
     / \   \
    1   7  12
     */

    public TreeNode bstFromPreorder(int[] preorder) {
        TreeNode root = new TreeNode(preorder[0]);
        for (int i = 1; i < preorder.length; i++) {
            insert(root, preorder[i]);
        }
        return root;
    }

    private TreeNode insert(TreeNode node, int val) {  // 5, 1
        if (node == null) { //找到空位
            return new TreeNode(val);
        }
        // node 不为空
        if (val < node.val) {
            node.left = insert(node.left, val);  //建立父子关系
        } else if (node.val < val) {
            node.right = insert(node.right, val); //建立父子关系
        }
        return node;
    }
}

解法2:

package com.datastructure.binarytree;

/**
 * 根据前序遍历构造二叉树
 * 题目说明:
 * 1.preorder 长度 >= 1
 * 2.preorder 没有重复值
 */
public class E06Leetcode1008 {


    /**
     * 解法2:分治法
     * 8,5,1,7,10,12
     *   根 8, 左子树,5,1,7,右子树10,12
     */
    public TreeNode bstFromPreorder(int[] preorder) {

        return partition(preorder, 0, preorder.length - 1);
    }

    private TreeNode partition(int[] preorder, int start, int end) {
        if (start > end) {
            return null;
        }
        TreeNode node = new TreeNode(preorder[start]);

        //寻找比根节点大的第一个索引
        int index = start + 1;
        while (index <= end) {
            if (preorder[index] > preorder[start]) {
                break;
            }
            index++;
        }

        //找到比根节点大的第一个索引,递归
        node.left = partition(preorder, start + 1, index - 1);
        node.right = partition(preorder, index, end);
        return node;
    }
}
posted @ 2023-12-30 10:42  chuangzhou  阅读(4)  评论(0编辑  收藏  举报