9.二叉搜索树
9.1 概述
二叉搜索树(Binary Search Tree,BST)是一种特殊的二叉树数据结构,其设计目的是为了支持高效的查找、插入和删除操作。在二叉搜索树中,每个节点都具有以下性质:
节点值的排序性:
节点的左子树(如果存在)包含的所有节点的值都小于该节点的值。
节点的右子树(如果存在)包含的所有节点的值都大于该节点的值。
递归定义:一个空树是二叉搜索树。
如果一个非空树的根节点满足上述排序性,并且它的左子树和右子树分别都是二叉搜索树,则这个树整体上也是一个二叉搜索树。
操作特性:查找:通过比较待查找值与当前节点值来决定是向左子树还是右子树进行查找,可以将查找时间减少到对数级别(O(log n)),在最优情况下。
插入:新插入的节点根据其值被放到合适的位置以保持树的排序性质。
删除:删除节点可能涉及替换、移动或重新连接节点以维持树的完整性和排序规则。
遍历顺序:中序遍历(Inorder Traversal):对于二叉搜索树来说,中序遍历的结果是一个递增排序的序列。
由于这些特点,二叉搜索树在计算机科学中广泛应用,特别是在需要频繁查询和更新动态集合的情况下,如数据库索引和文件系统等。
9.2 get方法实现
9.2.1 普通get方法
public class BSTree1 { BSTNode root;//根节点 public BSTree1(BSTNode root) { this.root = root; } public BSTree1() { } //根据key查找相应的值 非递归实现 public Object get(int key) { BSTNode node=root; while (node!=null){ if(node.key>key){ node=node.left; } else if (node.key<key) { node=node.right; }else{ return node.value; } } return null; } //根据key查找相应的值 递归实现 /*public Object get(int key) { return get(root, key); } public Object get(BSTNode node, int key) { if (node == null) { return null; } if (node.key > key) { return get(node.left, key); } else if (node.key < key) { return get(node.right, key); } else { return node.value; } }*/ static class BSTNode { int key; Object value; BSTNode left; BSTNode right; public BSTNode(int key) { this.key = key; } public BSTNode(int key, Object value) { this.key = key; this.value = value; } public BSTNode(int key, Object value, BSTNode left, BSTNode right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.2.2 泛型key,value
只要具备比较大小功能的类都可以作为key,而具备比较大小功能只需要继承Comparable接口即可,value也可以用泛型来指定。
public class BSTree2<T extends Comparable<T>,V> { BSTNode<T,V> root;//根节点 public BSTree2(BSTNode root) { this.root = root; } public BSTree2() { } //泛型key 优化,接口必须继承Comparable public V get(T key){ BSTNode<T,V> node=root; while (node!=null){ int res = node.key.compareTo(key); if(res>0){ node=node.left; } else if (res<0) { node=node.right; }else{ return node.value; } } return null; } static class BSTNode<T,V> { T key; V value; BSTNode<T,V>left; BSTNode<T,V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T,V> left, BSTNode<T,V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.3 min,max方法实现
public class BSTree3<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree3(BSTNode root) { this.root = root; } public BSTree3() { } //非递归实现 public V min() { BSTNode<T, V> curr = root; if(curr ==null) return null; while (curr.left != null) { curr = curr.left; } return curr.value; } public V max() { BSTNode<T, V> curr = root; if(curr ==null) return null; while (curr.right != null) { curr = curr.right; } return curr.value; } //递归实现 /*public V min(){ return min(root); } public V min(BSTNode<T,V> node){ if(node ==null) return null; if(node.left!=null){ return min(node.left); }else{ return node.value; } } public V max(){ return max(root); } public V max(BSTNode<T,V> node){ if(node ==null) return null; if(node.right!=null){ return max(node.right); }else{ return node.value; } }*/ static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.4 put方法实现
//put public class BSTree4<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree4(BSTNode root) { this.root = root; } public BSTree4() { } /* * 有该节点,更新value,没有则新增 * */ public void put(T key,V value){ BSTNode<T,V>node=root; BSTNode<T,V>parent=node; while (node!=null){ parent=node; int res = node.key.compareTo(key); if(res>0){ node=node.left; } else if (res<0) { node=node.right; }else{ node.value=value; return; } } //没找到新增节点 BSTNode<T,V>newNode=new BSTNode<>(key,value); //树为空 if(parent==null){ root=newNode; return; } int res = parent.key.compareTo(key); if(res>0){ parent.left=newNode; }else if(res<0){ parent.right=newNode; } } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.5 前任 后继方法实现
对于一般的二叉搜索树来说,其特性是左子树的所有节点值都小于根节点,右子树的所有节点值都大于根节点。 我们首先需要找到目标节点。
然后从目标节点开始回溯到其父节点,如果目标节点是其父节点的左孩子,则其“前任”就是其父节点;
如果不是(即目标节点是其父节点的右孩子),则继续向上回溯至其父节点的父节点,重复此过程。
如果在回溯过程中,我们到达了根节点且根节点是目标节点的父节点,并且目标节点是其右孩子,那么说明目标节点没有“前任”。
前任:
如果目标有左子树,此时前任就是左子树的最大值。
如果目标没有左子树,离他最近的自左而来的祖先就是他的前任。
后继:
如果目标有右子树,此时前任就是右子树的最小值。
如果目标没有右子树,离他最近的自右而来的祖先就是他的前任。
//前任后继 public class BSTree5<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree5(BSTNode root) { this.root = root; } public BSTree5() { } /* * 如果目标有左子树,此时前任就是左子树的最大值。 * 如果目标没有左子树,离他最近的自左而来的祖先就是他的前任。 * */ public V successor(T key) { BSTNode<T, V> curr = root; BSTNode<T, V> leftAncestor = null; while (curr != null) { int res = key.compareTo(curr.key); if (res < 0) { curr = curr.left; } else if (res > 0) { leftAncestor=curr; curr = curr.right; } else { //curr为查找到的节点 break; } } //未找到 if(curr==null){ return null; } //有左子树 if(curr.left!=null){ curr=curr.left; while (curr.right!=null){ curr=curr.right; } return curr.value; } //节点没有左子树 return leftAncestor==null?null:leftAncestor.value; } /* * 如果目标有右子树,此时前任就是右子树的最大值。 * 如果目标没有右子树,离他最近的自右而来的祖先就是他的前任。 * */ public V predecessor(T key){ BSTNode<T, V> curr = root; BSTNode<T, V> rightAncestor = null; while (curr != null) { int res = key.compareTo(curr.key); if (res < 0) { rightAncestor=curr; curr = curr.left; } else if (res > 0) { curr = curr.right; } else { //curr为查找到的节点 break; } } //未找到 if(curr==null){ return null; } //有左子树 if(curr.right!=null){ curr=curr.right; while (curr.left!=null){ curr=curr.left; } return curr.value; } //节点没有左子树 return rightAncestor==null?null:rightAncestor.value; } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.6 删除方法实现
1.删除没有子节点的节点:如果要删除的节点没有子节点,那么可以直接删除该节点,并将其父节点指向该节点的引用设置为 None。
2.删除只有一个子节点的节点:如果要删除的节点只有一个子节点(左或右)。
2.1 删除元素为父元素左子结点:子节点作为父元素的左子结点。
2.2 删除元素为父元素右子结点:子节点作为父元素的右子结点。
3.删除有两个子节点的节点:让被删除节点的后继节点顶替当前节点,分为两种
3.1 后继节点为删除节点的直接子节点:直接顶替删除节点即可。
3.2 后继节点非删除节点的直接子节点:要将后任节点的子节点交给后继节点的父节点,然后用后继节点顶替被删除节点。
9.6.1 非递归实现
//删除节点 public class BSTree6<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree6(BSTNode root) { this.root = root; } public BSTree6() { } public V delete(T key) { BSTNode<T, V> curr = root; // 父节点 BSTNode<T, V> parent = null; while (curr != null) { int res = key.compareTo(curr.key); if (res < 0) { parent = curr; curr = curr.left; } else if (res > 0) { parent = curr; curr = curr.right; } else { //curr为查找到的节点 break; } } //未找到 if (curr == null) { return null; } //删除操作 if (curr.left == null) { //有右子节点,没有左子节点 将元素托管给父元素 shift(parent, curr, curr.right); } else if (curr.left != null) { //有左子节点,没有右子节点 将元素托管给父元素 shift(parent, curr, curr.left); } else { //左右子节点都存在 // 1.查找被删除节点后继节点 BSTNode<T, V> proNode = curr.right; BSTNode<T, V> sParent = curr; while (proNode != null) { //保存父节点信息 sParent = proNode; //找到右子树最小值,没有left属性的元素 proNode = proNode.left; } // 2.处理被删除节点后继节点的情况后 // 2.1继节点为被删除元素的直接子节点(直接替换) // 2.2继节点为被删除元素的非直接子节点 将后任节点的子节点交给后继节点的父节点 if (sParent != curr) { //删除后继节点并将后继节点右侧节点托管给后继节点的父节点(因为查找后继节点的操作,不断像left查找,不可能存在左侧节点) shift(sParent, proNode, proNode.right); //还要将被删除节点的右侧节点托管给后继节点 proNode.right=curr.right; } // 3.用后继节点替换被删除节点(删除当前节点将后继节点托管给父节点) shift(parent, curr, proNode); //将被删除节点的左侧节点托管给后继节点 proNode.left = curr.left; } return curr.value; } private void shift(BSTNode<T, V> parent, BSTNode<T, V> curr, BSTNode<T, V> child) { if (parent == null) { //被删除节点没有父元素 root = curr; } else if (curr == parent.left) { //删除节点为父节点的左孩子 parent.left = child; } else { //删除节点为父节点的右孩子 parent.right = child; } } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.6.2 递归实现
//删除节点 非递归 public class BSTree6<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree6(BSTNode root) { this.root = root; } public BSTree6() { } public V delete(T key) { ArrayList<V> arrayList = new ArrayList<>(); root = delete(key, root, arrayList); return arrayList.isEmpty() ? null : arrayList.get(0); } private BSTNode delete(T key, BSTNode<T, V> node, ArrayList<V> arrayList) { if (node == null) { return null; } int res = key.compareTo(node.key); if (res < 0) { //当前节点的左侧子元素更新为删除结束后剩下的元素 node.left = delete(key, node.left, arrayList); return node; } if (res > 0) { node.right = delete(key, node.right, arrayList); return node; } arrayList.add(node.value); //只有右子元素 if (node.left == null) { return node.right; } //只有左子元素 if (node.right == null) { return node.left; } //有两个子元素 //删除节点与后继节点相邻 BSTNode<T, V> s = node.left; BSTNode<T, V> sParent = node; while (s.left != node) { s = s.left; } //删除节点与后继节点不相邻 s.right = delete(s.key, node.right, new ArrayList<>()); //更新后继节点的左子元素为删除节点的左子元素 s.left = node.left; return s; } private void shift(BSTNode<T, V> parent, BSTNode<T, V> curr, BSTNode<T, V> child) { if (parent == null) { //被删除节点没有父元素 root = curr; } else if (curr == parent.left) { //删除节点为父节点的左孩子 parent.left = child; } else { //删除节点为父节点的右孩子 parent.right = child; } } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.7 范围查询
利用中序遍历,从小到大遍历,将符合条件的值添加到List中:
//范围查询 public class BSTree8<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree8(BSTNode root) { this.root = root; } public BSTree8() { } /* * 中序遍历可以得到由小到大的结果 * 判断与key的关系添加到list内 * 将list返回 * */ //查找小于key public ArrayList<V> less(T key){ ArrayList<V>res=new ArrayList<>(); BSTNode<T,V>p=root; LinkedList<BSTNode<T,V>> stack=new LinkedList(); while (p!=null || !stack.isEmpty()){ if(p!=null){ //未走到最左 stack.push(p); p=p.left; }else{ //到头之后弹栈 BSTNode<T, V> pop = stack.pop(); //处理值 int popRes = pop.key.compareTo(key); if(popRes<0){ res.add(pop.value); }else { break; } //弹出之后走右侧节点 p=pop.right; } } return res; } //查找大于key public ArrayList<V> greater(T key){ ArrayList<V>res=new ArrayList<>(); BSTNode<T,V>p=root; LinkedList<BSTNode<T,V>> stack=new LinkedList(); while (p!=null || !stack.isEmpty()){ if(p!=null){ //未走到最左 stack.push(p); p=p.left; }else{ //到头之后弹栈 BSTNode<T, V> pop = stack.pop(); //处理值 int popRes = pop.key.compareTo(key); if(popRes>0){ res.add(pop.value); } //弹出之后走右侧节点 p=pop.right; } } return res; } //查找大于等于key1,小于等于key2 public ArrayList<V> between(T key1,T key2){ ArrayList<V>res=new ArrayList<>(); BSTNode<T,V>p=root; LinkedList<BSTNode<T,V>> stack=new LinkedList(); while (p!=null || !stack.isEmpty()){ if(p!=null){ //未走到最左 stack.push(p); p=p.left; }else{ //到头之后弹栈 BSTNode<T, V> pop = stack.pop(); //处理值 int popRes1 = pop.key.compareTo(key1); int popRes2 = pop.key.compareTo(key2); if(popRes1>0 && popRes2<0){ res.add(pop.value); } //弹出之后走右侧节点 p=pop.right; } } return res; } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }