A binary search tree (BST) is a binary tree where each node has a Comparable key (and an associated value) and satisfies the restriction that the key in any node is larger than the keys in all nodes in that node’s left subtree and smaller than the keys in all nodes in that node’s right subtree.
Representation. We define a private nested class to define nodes in BSTs, just as we did for linked lists. Each node contains a key, a value, a left link, a right link, and a node count (when relevant, we include node counts in red above the node in our drawings). The left link points to a BST for items with smaller keys, and the right link points to a BST for items with larger keys. The instance variable N gives the node count in the subtree rooted at the node. This field facilitates the implementation of various ordered symbol-table operations, as you will see. The private size() method in below algorithm is implemented to assign the value 0 to null links, so that we can maintain this field by making sure that the invariant:
size(x) = size(x.left) + size(x.right) + 1
holds for every node x in the tree.
A BST represents a set of keys (and associated values), and there are many different BSTs that represent the same set. If we project the keys in a BST such that all keys in each node’s left subtree appear to the left of the key in the node and all keys in each node’s right subtree appear to the right of the key in the node, then we always get the keys in sorted order. We take advantage of the flexibility inherent in having many BSTs represent this sorted order to develop efficient algorithms for building and using BSTs.
public class BST<Key extends Comparable<Key>, Value> { private Node root; // root of BST private class Node { private Key key; // sorted by key private Value val; // associated data private Node left, right; // left and right subtrees private int N; // number of nodes in subtree public Node(Key key, Value val, int N) { this.key = key; this.val = val; this.N = N; } } // is the symbol table empty? public boolean isEmpty() { return size() == 0; } // return number of key-value pairs in BST public int size() { return size(root); } // return number of key-value pairs in BST rooted at x private int size(Node x) { if (x == null) return 0; else return x.N; } /** * ******************************************************************** * Search BST for given key, and return associated value if found, * return null if not found * ********************************************************************* */ // does there exist a key-value pair with given key? public boolean contains(Key key) { return get(key) != null; } // return value associated with the given key, or null if no such key exists public Value get(Key key) { return get(root, key); } private Value get(Node x, Key key) { if (x == null) return null; int cmp = key.compareTo(x.key); if (cmp < 0) return get(x.left, key); else if (cmp > 0) return get(x.right, key); else return x.val; } /** * ******************************************************************** * Insert key-value pair into BST * If key already exists, update with new value * ********************************************************************* */ public void put(Key key, Value val) { if (val == null) { delete(key); return; } root = put(root, key, val); assert check(); } private Node put(Node x, Key key, Value val) { if (x == null) return new Node(key, val, 1); int cmp = key.compareTo(x.key); if (cmp < 0) x.left = put(x.left, key, val); else if (cmp > 0) x.right = put(x.right, key, val); else x.val = val; x.N = 1 + size(x.left) + size(x.right); return x; } /** * ******************************************************************** * Delete * ********************************************************************* */ public void deleteMin() { if (isEmpty()) throw new NoSuchElementException("Symbol table underflow"); root = deleteMin(root); assert check(); } private Node deleteMin(Node x) { if (x.left == null) return x.right; x.left = deleteMin(x.left); x.N = size(x.left) + size(x.right) + 1; return x; } public void deleteMax() { if (isEmpty()) throw new NoSuchElementException("Symbol table underflow"); root = deleteMax(root); assert check(); } private Node deleteMax(Node x) { if (x.right == null) return x.left; x.right = deleteMax(x.right); x.N = size(x.left) + size(x.right) + 1; return x; } public void delete(Key key) { root = delete(root, key); assert check(); } private Node delete(Node x, Key key) { if (x == null) return null; int cmp = key.compareTo(x.key); if (cmp < 0) x.left = delete(x.left, key); else if (cmp > 0) x.right = delete(x.right, key); else { if (x.right == null) return x.left; if (x.left == null) return x.right; Node t = x; x = min(t.right); x.right = deleteMin(t.right); x.left = t.left; } x.N = size(x.left) + size(x.right) + 1; return x; } /** * ******************************************************************** * Min, max, floor, and ceiling * ********************************************************************* */ public Key min() { if (isEmpty()) return null; return min(root).key; } private Node min(Node x) { if (x.left == null) return x; else return min(x.left); } public Key max() { if (isEmpty()) return null; return max(root).key; } private Node max(Node x) { if (x.right == null) return x; else return max(x.right); } public Key floor(Key key) { Node x = floor(root, key); if (x == null) return null; else return x.key; } private Node floor(Node x, Key key) { if (x == null) return null; int cmp = key.compareTo(x.key); if (cmp == 0) return x; if (cmp < 0) return floor(x.left, key); Node t = floor(x.right, key); if (t != null) return t; else return x; } public Key ceiling(Key key) { Node x = ceiling(root, key); if (x == null) return null; else return x.key; } private Node ceiling(Node x, Key key) { if (x == null) return null; int cmp = key.compareTo(x.key); if (cmp == 0) return x; if (cmp < 0) { Node t = ceiling(x.left, key); if (t != null) return t; else return x; } return ceiling(x.right, key); } /** * ******************************************************************** * Rank and selection * ********************************************************************* */ public Key select(int k) { if (k < 0 || k >= size()) return null; Node x = select(root, k); return x.key; } // Return key of rank k. private Node select(Node x, int k) { if (x == null) return null; int t = size(x.left); if (t > k) return select(x.left, k); else if (t < k) return select(x.right, k - t - 1); else return x; } public int rank(Key key) { return rank(key, root); } // Number of keys in the subtree less than x.key. private int rank(Key key, Node x) { if (x == null) return 0; int cmp = key.compareTo(x.key); if (cmp < 0) return rank(key, x.left); else if (cmp > 0) return 1 + size(x.left) + rank(key, x.right); else return size(x.left); } /** * ******************************************************************** * Range count and range search. * ********************************************************************* */ public Iterable<Key> keys() { return keys(min(), max()); } public Iterable<Key> keys(Key lo, Key hi) { Queue<Key> queue = new Queue<Key>(); keys(root, queue, lo, hi); return queue; } private void keys(Node x, Queue<Key> queue, Key lo, Key hi) { if (x == null) return; int cmplo = lo.compareTo(x.key); int cmphi = hi.compareTo(x.key); if (cmplo < 0) keys(x.left, queue, lo, hi); if (cmplo <= 0 && cmphi >= 0) queue.enqueue(x.key); if (cmphi > 0) keys(x.right, queue, lo, hi); } public int size(Key lo, Key hi) { if (lo.compareTo(hi) > 0) return 0; if (contains(hi)) return rank(hi) - rank(lo) + 1; else return rank(hi) - rank(lo); } // height of this BST (one-node tree has height 0) public int height() { return height(root); } private int height(Node x) { if (x == null) return -1; return 1 + Math.max(height(x.left), height(x.right)); } // level order traversal public Iterable<Key> levelOrder() { Queue<Key> keys = new Queue<Key>(); Queue<Node> queue = new Queue<Node>(); queue.enqueue(root); while (!queue.isEmpty()) { Node x = queue.dequeue(); if (x == null) continue; keys.enqueue(x.key); queue.enqueue(x.left); queue.enqueue(x.right); } return keys; } /** * ********************************************************************** * Check integrity of BST data structure * *********************************************************************** */ private boolean check() { if (!isBST()) StdOut.println("Not in symmetric order"); if (!isSizeConsistent()) StdOut.println("Subtree counts not consistent"); if (!isRankConsistent()) StdOut.println("Ranks not consistent"); return isBST() && isSizeConsistent() && isRankConsistent(); } // does this binary tree satisfy symmetric order? // Note: this test also ensures that data structure is a binary tree since order is strict private boolean isBST() { return isBST(root, null, null); } // is the tree rooted at x a BST with all keys strictly between min and max // (if min or max is null, treat as empty constraint) // Credit: Bob Dondero's elegant solution private boolean isBST(Node x, Key min, Key max) { if (x == null) return true; if (min != null && x.key.compareTo(min) <= 0) return false; if (max != null && x.key.compareTo(max) >= 0) return false; return isBST(x.left, min, x.key) && isBST(x.right, x.key, max); } // are the size fields correct? private boolean isSizeConsistent() { return isSizeConsistent(root); } private boolean isSizeConsistent(Node x) { if (x == null) return true; if (x.N != size(x.left) + size(x.right) + 1) return false; return isSizeConsistent(x.left) && isSizeConsistent(x.right); } // check that ranks are consistent private boolean isRankConsistent() { for (int i = 0; i < size(); i++) if (i != rank(select(i))) return false; for (Key key : keys()) if (key.compareTo(select(rank(key))) != 0) return false; return true; } }
The most difficult BST operation to implement is the delete() method that removes a key-value pair from the symbol table. We can proceed in a similar manner with deleteMin() to delete any node that has one child (or no children), but what can we do to delete a node that has two children? We are left with two links, but have a place in the parent node for only one of them. An answer to this dilemma, first proposed by T. Hibbard in 1962, is to delete a node x by replacing it with its successor. Because x has a right child, its successor is the node with the smallest key in its right subtree. The replacement preserves order in the tree because there are no keys between x.key and the successor’s key. We can accomplish the task of replacing x by its successor in four (!) easy steps:
■ Save a link to the node to be deleted in t.
■ Set x to point to its successor min(t.right).
■ Set the right link of x (which is supposed to point to the BST containing all the keys larger than x.key) to deleteMin(t.right), the link to the BST containing all the keys that are larger than x.key after the deletion.
■ Set the left link of x (which was null) to t.left (all the keys that are less than both the deleted key and its successor).
Our standard recursive setup accomplishes, after the recursive calls, the task of setting the appropriate link in the parent and decrementing the node counts in the nodes on the path to the root (again, we accomplish the task of updating the counts by setting the counts in each node on the search path to be one plus the sum of the counts in its children). While this method does the job, it has a flaw that might cause performance problems in some practical situations. The problem is that the choice of using the successor is arbitrary and not symmetric. Why not use the predecessor? In practice, it is worthwhile to choose at random between the predecessor and the successor.
BSTs are not difficult to implement and can provide fast search and insert for practical applications of all kinds if the key insertions are well-approximated by the random-key model. For our examples (and for many practical applications) BSTs make the difference between being able to accomplish the task at hand and not being able to do so. Moreover, many programmers choose BSTs for symbol-table implementations because they also support fast rank, select, delete, and range query operations. Still, as we have emphasized, the bad worst-case performance of BSTs may not be tolerable in some situations. Good performance of the basic BST implementation is dependent on the keys being sufficiently similar to random keys that the tree is not likely to contain many long paths. With quicksort, we were able to randomize; with a symbol-table API, we do not have that freedom, because the client controls the mix of operations. Indeed, the worst-case behavior is not unlikely in practice—it arises when a client inserts keys in order or in reverse order, a sequence of operations that some client certainly might attempt in the absence of any explicit warnings to avoid doing so. This possibility is a primary reason to seek better algorithms and data structures.
相关推荐
二叉搜索树(Binary Search Tree,BST)是一种特殊类型的二叉树,它的每个节点都包含一个键(key)、一个关联的值、一个指向左子树的...对于编程人员来说,理解和运用二叉搜索树是提升数据结构和算法能力的重要步骤。
最优二叉查找树(Optimal Binary Search Tree, OBST)是计算机科学领域内一种高效的搜索数据结构,其设计目标是在已知各节点访问概率的情况下,构建一棵使得平均查找时间最短的二叉查找树。该理论最早由Donald E. ...
二叉搜索树(Binary Search Tree,简称BST)是一种在计算机科学中广泛应用的数据结构,它具有以下特性:每个节点包含一个键(key)、一个关联的值、一个指向左子树的引用以及一个指向右子树的引用。在二叉搜索树中,...
### 数据结构案例:二叉搜索树(Binary Search Tree, BST) #### 一、二叉搜索树(BST)定义 二叉搜索树(Binary Search Tree, BST)是一种非常重要的数据结构,其核心特性在于能够快速地进行查找、插入以及删除操作...
- **性能评估**:对不同的数据结构和算法进行时间和空间复杂度的分析,评估其效率。 - **团队合作**:在项目开发过程中,培养团队协作能力,共同完成一个大型项目的设计与实现。 通过本教程的学习,不仅能够掌握...
在IT领域,掌握数据结构和算法是至关重要的,特别是对于编程语言如C#的开发者来说。数据结构是存储和组织数据的方式,而算法是解决问题或执行任务的特定步骤。本资源"**c#数据结构和算法源码**"提供了一个实践学习的...
通过以上内容的总结,我们可以看出Java数据结构与算法的学习涵盖了从基础知识到具体应用的各个方面,对于软件开发人员来说非常重要。掌握这些知识不仅可以提高代码质量和效率,还能更好地理解和解决实际问题。
通过《数据结构与算法分析C语言描述》这本书,读者不仅可以学到丰富的数据结构知识和算法分析技能,还能掌握如何使用C语言实现这些数据结构和算法。本书通过大量的示例代码和实际案例,让理论知识与实践紧密结合,...
【平衡二叉树】是计算机科学中的一个重要概念,特别是在数据结构和算法领域。在LeetCode平台上,这是一道常见的编程挑战题目。平衡二叉树(Balanced Binary Tree)是一种特殊的二叉树,它的左右两个子树的高度差的...
各种数据结构、算法及实用的C#源代码 C#,二叉搜索树(Binary Search Tree)的迭代方法与源代码 二叉搜索树(BST,Binary Search Tree)又称二叉查找树或二叉排序树。 一棵二叉搜索树是以二叉树来组织的,可以使用一...
- **定义**:二叉查找树(Binary Search Tree, BST)是一种特殊的二叉树,其中任意节点的值都大于其左子树上任意节点的值,并且小于其右子树上任意节点的值。 - **实现**:在C语言中,可以使用结构体来定义二叉树...
在IT领域,数据结构与算法是编程基础的重要组成部分,尤其对于Java开发者来说,掌握二叉树这一经典数据结构及其相关的算法至关重要。本资料主要聚焦于Java实现的二叉树问题,特别是那些常出现在面试和在线编程挑战...
数据结构与算法分析是计算机科学中的核心领域,它关乎如何高效地存储和处理数据,以及设计和实现高效的算法。在Java语言中,这些概念尤为重要,因为Java提供了丰富的库和工具来支持各种数据结构和算法的实现。《数据...
二叉搜索树(Binary Search Tree,简称BST)是一种自平衡的二叉树数据结构,它在计算机科学中被广泛用于实现动态查找表。在BST中,每个节点包含一个键(key)、一个关联的值、一个指向左子节点的指针和一个指向右子...
在计算机科学中,二叉树(Binary Tree)是一种数据结构,每个节点最多有两个子节点,通常分为左子节点和右子节点。这种数据结构广泛应用于各种算法和问题解决,如搜索、排序、文件系统等。在C语言中实现二叉树,我们...
TestBinarySearchTree.cpp: Test program for binary search tree AvlTree.h: AVL tree TestAvlTree.cpp: Test program for AVL trees mapDemo.cpp: Map demos WordLadder.cpp: Word Ladder Program and Word ...