【数据结构】二叉搜索树-LMLPHP

二叉搜索树

二叉搜索树不是空树就包含以下性质:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。
  • 它的左右子树也分别为二叉搜索树。

也就是说根节点左边比根节点小右边比根节点大。

二叉搜索树的模拟实现

一般实现的二叉搜索树中都不含有相同值的节点。

接口实现

实现的接口(成员方法)如下:

public class BinarySearchTree {
    // 插入一个元素
    public boolean insert(int key) {}
    //查找key是否存在
    public TreeNode search(int key) {}
    //删除key的值
    public boolean remove(int key) {}
}

成员变量

我们使用内部类来表示二叉树的每一个节点。并定义出来根节点。

static class TreeNode {
        public int key;
        public TreeNode left;
        public TreeNode right;
        TreeNode(int key) {
            this.key = key;
        }
    }
    public TreeNode root;

插入一个元素

逻辑就是插入一个元素总能在空节点位置找到该插入的位置。
就是下图中由黑线连接的空节点。
【数据结构】二叉搜索树-LMLPHP

实现思路:

  1. 如果当前树为空则插入节点为根节点。
  2. 依次向下找到该插入的位置,同时要获得该位置的父亲节点。
  3. 最后插入。

注意:**一般实现的二叉搜索树中都不含有相同值的节点。**所以插入已有元素是不会成功的。

	// 插入一个元素
	public boolean insert(int key) {
        TreeNode node = new TreeNode(key);
        if(root == null) root = node;
        TreeNode pre = null;
        TreeNode cur = root;
        
        while (cur != null){
            if(cur.key == key) {
                return false;
            } else if(cur.key > key) {
                pre = cur;
                cur = cur.left;
            } else {
                pre = cur;
                cur = cur.right;
            }
        }
        if(pre.key > key) pre.left = node;
        if(pre.key < key) pre.right = node;
        return true;
    }

查找key是否存在

逻辑与二分查找类似。
代码实现思路:

  1. 当前节点等于key,返回当前节点。
  2. 当前节点比key大就去左子树找。
  3. 当前节点比key小就去右子树找。
  4. 都没找到返回空。
//查找key是否存在
    public TreeNode search(int key) {
        TreeNode cur = root;
        while(cur != null) {
            if(cur.key == key) return cur;
            if(cur.key < key) cur = cur.right;
            if(cur.key > key) cur = cur.left;
        }
        return null;
    }

查询分析:
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:logN。
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N / 2。

删除key的值

实现思路:

  1. 设待删除结点为 cur, 待删除结点的双亲结点为 pre。

  2. cur.left == null:
    1) cur 是 root,则 root = cur.right,
    2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.right,
    3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.right。

  3. cur.right == null:
    1) cur 是 root,则 root = cur.left,
    2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.left,
    3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.left。

  4. cur.left != null && cur.right != null:
    两个思路:在左子树找最大值进行替换,右子树找最小值进行替换。

//删除key的值
    public boolean remove(int key) {
        TreeNode node = new TreeNode(key);
        if(root == null) throw new NullPointerException();
        TreeNode pre = null;
        TreeNode cur = root;

        while (cur != null){
            if(cur.key == key) {
                removeNode(cur,pre);
                return true;
            } else if(cur.key > key) {
                pre = cur;
                cur = cur.left;
            } else {
                pre = cur;
                cur = cur.right;
            }
        }
        return false;
    }

    private void removeNode(TreeNode cur, TreeNode pre) {
        if(cur.left == null) {
          if(cur == root) {
              root = cur.right;
          } else if(cur == pre.right) {
              pre.right = cur.right;
          } else {
              pre.left = cur.right;
          }
        } else if(cur.right == null) {
            if(cur == root) {
                root = cur.left;
            } else if(cur == pre.right) {
                pre.right = cur.left;
            } else {
                pre.left = cur.left;
            }
        } else {
            //找到右树最小值替换
            TreeNode target = cur;
            pre =  cur;
           cur = cur.right;
           while (cur.left != null) {
               pre = cur;
               cur = cur.left;
           }
           //排除待删除节点左子树为空
           if(pre.right == cur) {
               pre = cur;
               return;
           }
           target.key = cur.key;
           cur = null;
        }
    }

TreeMap 和 TreeSet 即 java 中利用搜索树实现的 Map 和 Set;实际上用的是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础之上 + 颜色以及红黑树性质验证。

练习

二叉搜索树与双向链表

题目描述:

【数据结构】二叉搜索树-LMLPHP

题解

解题思路:
要有一个前驱pre节点。 递归到上一级的时候 当前的根节点(pRootOfTree)就是pre的后继。
按中序遍历来遍历二叉搜索树即可。

public class Solution {
    TreeNode pre = null;
    TreeNode head = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree ==null) return null;
        Convert(pRootOfTree.left);
        if(head == null) {
            head = pRootOfTree;
        }
        if(pre != null) {
            pRootOfTree.left=pre;
            pre.right=pRootOfTree;
        }
        pre=pRootOfTree;
        Convert(pRootOfTree.right);
        return head;
    }
}
07-31 20:13