内容来自刘宇波老师算法与数据结构体系课
1、二分搜索树的结构


2、实现二分搜索树

| 深度优先 |
| - 前序遍历:中左右 |
| - 中序遍历:左中右,二分搜索树的中序遍历结果是顺序的 |
| - 后序遍历:左右中,为二分搜索树释放内存 |
| |
| 广度优先 |
| - 层序遍历:更快找到问题的解,常用于算法设计中 - 无权图最短路径 |
| |
| |
| |
| |
| |
| |
| public class BST<E extends Comparable<E>> { |
| |
| private class Node { |
| public E e; |
| public Node left; |
| public Node right; |
| |
| public Node(E e) { |
| this.e = e; |
| this.left = null; |
| this.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) { |
| root = 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.left = add(node.left, e); |
| else if (e.compareTo(node.e) > 0) node.right = add(node.right, e); |
| |
| return node; |
| } |
| |
| 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; |
| if (e.compareTo(node.e) < 0) return contains(node.left, e); |
| return contains(node.right, e); |
| } |
| |
| public E minimum() { |
| if (size == 0) throw new RuntimeException("BST is empty!"); |
| return minimum(root).e; |
| } |
| |
| |
| |
| |
| private Node minimum(Node node) { |
| if (node.left == null) return node; |
| return minimum(node.left); |
| } |
| |
| public E maximum() { |
| if (size == 0) throw new RuntimeException("BST is empty!"); |
| return maximum(root).e; |
| } |
| |
| |
| |
| |
| private Node maximum(Node node) { |
| if (node.right == null) return node; |
| return maximum(node.right); |
| } |
| |
| public E removeMin() { |
| E min = minimum(); |
| root = removeMin(root); |
| return min; |
| } |
| |
| |
| |
| |
| 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 removeMax() { |
| E max = maximum(); |
| root = removeMax(root); |
| return max; |
| } |
| |
| |
| |
| |
| private Node removeMax(Node node) { |
| if (node.right == null) { |
| Node leftNode = node.left; |
| node.left = null; |
| size--; |
| return leftNode; |
| } |
| |
| node.right = removeMax(node.right); |
| return node; |
| } |
| |
| public void remove(E e) { |
| root = remove(root, e); |
| } |
| |
| |
| |
| |
| private Node remove(Node node, E e) { |
| if (node == null) return null; |
| |
| if (e.compareTo(node.e) < 0) { |
| node.left = remove(node.left, e); |
| return node; |
| } else if (e.compareTo(node.e) > 0) { |
| node.right = remove(node.right, e); |
| return node; |
| } else { |
| if (node.left == null) { |
| Node rightNode = node.right; |
| node.right = null; |
| size--; |
| return rightNode; |
| } else if (node.right == null) { |
| Node leftNode = node.left; |
| node.left = null; |
| size--; |
| return leftNode; |
| } else { |
| |
| |
| |
| Node successor = minimum(node.right); |
| successor.right = removeMin(node.right); |
| successor.left = node.left; |
| node.left = node.right = null; |
| return successor; |
| } |
| } |
| } |
| |
| public void preOrder() { |
| preOrder(root); |
| } |
| |
| |
| |
| |
| private void preOrder(Node node) { |
| if (node == null) return; |
| |
| System.out.println(node.e); |
| preOrder(node.left); |
| preOrder(node.right); |
| } |
| |
| public void inOrder() { |
| inOrder(root); |
| } |
| |
| |
| |
| |
| private void inOrder(Node node) { |
| if (node == null) return; |
| |
| inOrder(node.left); |
| System.out.println(node.e); |
| inOrder(node.right); |
| } |
| |
| public void postOrder() { |
| postOrder(root); |
| } |
| |
| |
| |
| |
| private void postOrder(Node node) { |
| if (node == null) return; |
| |
| postOrder(node.left); |
| postOrder(node.right); |
| System.out.println(node.e); |
| } |
| |
| |
| |
| |
| public void levelOrder() { |
| Queue<Node> queue = new LoopQueue<>(); |
| queue.enqueue(root); |
| |
| Node cur; |
| while (!queue.isEmpty()) { |
| cur = queue.dequeue(); |
| System.out.println(cur.e); |
| if (cur.left != null) queue.enqueue(cur.left); |
| if (cur.right != null) queue.enqueue(cur.right); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| generateBSTString(root, 0, sb); |
| return sb.toString(); |
| } |
| |
| |
| |
| |
| private void generateBSTString(Node node, int depth, StringBuilder sb) { |
| if (node == null) { |
| sb.append(generateDepthString(depth)).append("null\n"); |
| return; |
| } |
| |
| sb.append(generateDepthString(depth)).append(node.e).append("\n"); |
| generateBSTString(node.left, depth + 1, sb); |
| generateBSTString(node.right, depth + 1, sb); |
| } |
| |
| private String generateDepthString(int depth) { |
| return "--".repeat(Math.max(0, depth)); |
| } |
| |
| |
| |
| |
| |
| public E floor(E e) { |
| if (isEmpty() || e.compareTo(minimum()) < 0) return null; |
| return floor(root, e).e; |
| } |
| |
| |
| |
| |
| private Node floor(Node node, E e) { |
| if (node == null) return null; |
| |
| if (node.e.compareTo(e) == 0) return node; |
| if (node.e.compareTo(e) > 0) return floor(node.left, e); |
| |
| Node tempNode = floor(node.right, e); |
| return tempNode != null ? tempNode : node; |
| } |
| |
| |
| |
| |
| |
| public E ceil(E e) { |
| if (isEmpty() || e.compareTo(maximum()) > 0) return null; |
| return ceil(root, e).e; |
| } |
| |
| |
| |
| |
| private Node ceil(Node node, E e) { |
| if (node == null) return null; |
| |
| if (node.e.compareTo(e) == 0) return node; |
| if (node.e.compareTo(e) < 0) return ceil(node.right, e); |
| |
| Node tempNode = ceil(node.left, e); |
| return tempNode != null ? tempNode : node; |
| } |
| } |
3、BST 集合
| public interface Set<E> { |
| |
| void add(E e); |
| |
| void remove(E e); |
| |
| boolean contains(E e); |
| |
| int getSize(); |
| |
| boolean isEmpty(); |
| |
| } |
| |
| |
| |
| public class BSTSet<E extends Comparable<E>> implements Set<E> { |
| |
| private final BST<E> bst; |
| |
| public BSTSet() { |
| bst = new BST<>(); |
| } |
| |
| @Override |
| public void add(E e) { |
| bst.add(e); |
| } |
| |
| @Override |
| public void remove(E e) { |
| bst.remove(e); |
| } |
| |
| @Override |
| public boolean contains(E e) { |
| return bst.contains(e); |
| } |
| |
| @Override |
| public int getSize() { |
| return bst.size(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return bst.isEmpty(); |
| } |
| } |
4、BST 映射
| public interface Map<K, V> { |
| |
| void add(K key, V value); |
| |
| V remove(K key); |
| |
| boolean contains(K key); |
| |
| V get(K key); |
| |
| void set(K key, V newValue); |
| |
| int getSize(); |
| |
| boolean isEmpty(); |
| |
| } |
| |
| |
| |
| public class BSTMap<K extends Comparable<K>, V> implements Map<K, V> { |
| |
| private class Node { |
| public K key; |
| public V value; |
| public Node left; |
| public Node right; |
| |
| public Node(K key, V value) { |
| this.key = key; |
| this.value = value; |
| this.left = null; |
| this.right = null; |
| } |
| } |
| |
| private Node root; |
| private int size; |
| |
| public BSTMap() { |
| root = null; |
| size = 0; |
| } |
| |
| |
| |
| |
| private Node getNode(Node node, K key) { |
| if (node == null) return null; |
| |
| if (key.compareTo(node.key) == 0) return node; |
| if (key.compareTo(node.key) < 0) return getNode(node.left, key); |
| return getNode(node.right, key); |
| } |
| |
| @Override |
| public void add(K key, V value) { |
| root = add(root, key, value); |
| } |
| |
| |
| |
| |
| private Node add(Node node, K key, V value) { |
| if (node == null) { |
| size++; |
| return new Node(key, value); |
| } |
| |
| if (key.compareTo(node.key) < 0) node.left = add(node.left, key, value); |
| else if (key.compareTo(node.key) > 0) node.right = add(node.right, key, value); |
| else node.value = value; |
| |
| return node; |
| } |
| |
| |
| |
| |
| private Node minimum(Node node) { |
| if (node.left == null) return node; |
| return minimum(node.left); |
| } |
| |
| |
| |
| |
| 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; |
| } |
| |
| @Override |
| public V remove(K key) { |
| Node node = getNode(root, key); |
| if (node != null) { |
| root = remove(root, key); |
| return node.value; |
| } |
| return null; |
| } |
| |
| |
| |
| |
| private Node remove(Node node, K key) { |
| if (node == null) return null; |
| |
| if (key.compareTo(node.key) < 0) { |
| node.left = remove(node.left, key); |
| return node; |
| } else if (key.compareTo(node.key) > 0) { |
| node.right = remove(node.right, key); |
| return node; |
| } else { |
| if (node.left == null) { |
| Node rightNode = node.right; |
| node.right = null; |
| size--; |
| return rightNode; |
| } else if (node.right == null) { |
| Node leftNode = node.left; |
| node.left = null; |
| size--; |
| return leftNode; |
| } else { |
| Node successor = minimum(node.right); |
| successor.right = removeMin(node.right); |
| successor.left = node.left; |
| node.left = node.right = null; |
| return successor; |
| } |
| } |
| } |
| |
| @Override |
| public boolean contains(K key) { |
| return getNode(root, key) != null; |
| } |
| |
| @Override |
| public V get(K key) { |
| Node node = getNode(root, key); |
| return node != null ? node.value : null; |
| } |
| |
| @Override |
| public void set(K key, V newValue) { |
| Node node = getNode(root, key); |
| if (node != null) node.value = newValue; |
| else throw new IllegalArgumentException(key + " doesn't exist!"); |
| } |
| |
| @Override |
| public int getSize() { |
| return size; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return size == 0; |
| } |
| } |
5、链表集合
| |
| |
| |
| public class LinkedListSet<E> implements Set<E> { |
| |
| private final LinkedList<E> list; |
| |
| public LinkedListSet() { |
| list = new LinkedList<>(); |
| } |
| |
| @Override |
| public void add(E e) { |
| if (!list.contains(e)) list.addFirst(e); |
| } |
| |
| @Override |
| public void remove(E e) { |
| list.removeElement(e); |
| } |
| |
| @Override |
| public boolean contains(E e) { |
| return list.contains(e); |
| } |
| |
| @Override |
| public int getSize() { |
| return list.getSize(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return list.isEmpty(); |
| } |
| } |
6、链表映射
| |
| |
| |
| public class LinkedListMap<K, V> implements Map<K, V> { |
| |
| private class Node { |
| public K key; |
| public V value; |
| public Node next; |
| |
| public Node(K key, V value, Node next) { |
| this.key = key; |
| this.value = value; |
| this.next = next; |
| } |
| |
| public Node() { |
| this(null, null, null); |
| } |
| |
| @Override |
| public String toString() { |
| return key.toString() + " : " + value.toString(); |
| } |
| } |
| |
| private final Node dummyHead; |
| private int size; |
| |
| public LinkedListMap() { |
| dummyHead = new Node(); |
| size = 0; |
| } |
| |
| private Node getNode(K key) { |
| Node cur = dummyHead.next; |
| while (cur != null) { |
| if (cur.key.equals(key)) return cur; |
| cur = cur.next; |
| } |
| return null; |
| } |
| |
| @Override |
| public void add(K key, V value) { |
| Node node = getNode(key); |
| if (node == null) { |
| dummyHead.next = new Node(key, value, dummyHead.next); |
| size++; |
| } else node.value = value; |
| } |
| |
| @Override |
| public V remove(K key) { |
| Node prev = dummyHead; |
| while (prev.next != null) { |
| if (prev.next.key.equals(key)) { |
| Node delNode = prev.next; |
| prev.next = delNode.next; |
| delNode.next = null; |
| size--; |
| return delNode.value; |
| } |
| prev = prev.next; |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public boolean contains(K key) { |
| return getNode(key) != null; |
| } |
| |
| @Override |
| public V get(K key) { |
| Node node = getNode(key); |
| return node != null ? node.value : null; |
| } |
| |
| @Override |
| public void set(K key, V newValue) { |
| Node node = getNode(key); |
| if (node != null) node.value = newValue; |
| else throw new IllegalArgumentException(key + " doesn't exist!"); |
| } |
| |
| @Override |
| public int getSize() { |
| return size; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return size == 0; |
| } |
| } |
7、二分搜索树非递归
栈和递归的密切关系
| |
| |
| |
| public class BSTNR<E extends Comparable<E>> { |
| |
| private class Node { |
| public E e; |
| public Node left; |
| public Node right; |
| |
| public Node(E e) { |
| this.e = e; |
| this.left = null; |
| this.right = null; |
| } |
| } |
| |
| private Node root; |
| private int size; |
| |
| public BSTNR() { |
| 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++; |
| return; |
| } |
| |
| Node parent = root; |
| while (true) { |
| if (e.compareTo(parent.e) == 0) return; |
| |
| if (e.compareTo(parent.e) < 0) { |
| if (parent.left == null) { |
| parent.left = new Node(e); |
| size++; |
| return; |
| } |
| parent = parent.left; |
| } |
| |
| if (e.compareTo(parent.e) > 0) { |
| if (parent.right == null) { |
| parent.right = new Node(e); |
| size++; |
| return; |
| } |
| parent = parent.right; |
| } |
| } |
| } |
| |
| public boolean contains(E e) { |
| Node cur = root; |
| while (cur != null) { |
| if (e.compareTo(cur.e) == 0) return true; |
| if (e.compareTo(cur.e) < 0) cur = cur.left; |
| else cur = cur.right; |
| } |
| return false; |
| } |
| |
| public void preOrder() { |
| Stack<Node> stack = new ArrayStack<>(); |
| stack.push(root); |
| |
| Node cur; |
| while (!stack.isEmpty()) { |
| 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 levelOrder() { |
| Queue<Node> queue = new LoopQueue<>(); |
| queue.enqueue(root); |
| |
| Node cur; |
| while (!queue.isEmpty()) { |
| cur = queue.dequeue(); |
| System.out.println(cur.e); |
| if (cur.left != null) queue.enqueue(cur.left); |
| if (cur.right != null) queue.enqueue(cur.right); |
| } |
| } |
| } |
8、更多话题
8.1、二分搜索树的顺序性
- minimum、maximun
- predecessor、successor
- floor、ceil
- rank、select:前序遍历可以实现



8.2、更多拓展
- 维护 size 的二分搜索树
- 维护 depth 的二分搜索树
- 支持重复元素的二分搜索树
- 维护 count 的二分搜索树




【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步