数据结构之树(二叉搜索树)

二叉搜索树

二叉搜索树(Binary Search Tree,BST)是一种基本的数据结构,用于存储和管理有序数据集。它是一种树状结构,其中每个节点都包含一个值,且满足以下性质:

  1. 左子树中的所有节点的值都小于或等于该节点的值。
  2. 右子树中的所有节点的值都大于该节点的值。

这些性质保证了二叉搜索树的有序性,使得在树中查找、插入和删除操作都可以在平均情况下在O(log n)时间内完成,其中n是树中节点的数量。

然而,最坏情况下,BST可能退化为链表,导致操作的时间复杂度为O(n)。

为了解决这个问题,二叉搜索树的一种变种是二次搜索树(Balanced Binary Search Tree),也称为自平衡二叉搜索树。这些树具有额外的性质,以确保它们的高度保持较小的范围,从而保持操作的平均时间复杂度为O(log n)。

常见的二次搜索树包括:

  1. AVL树:AVL树是一种自平衡二叉搜索树,通过维护节点的平衡因子来确保树的高度差不超过1。当插入或删除节点时,AVL树会自动调整以恢复平衡。

  2. 红黑树:红黑树也是一种自平衡二叉搜索树,通过规定节点的颜色和一些规则来保持平衡。它的性能非常稳定,适用于大多数情况。

  3. Splay树:Splay树是一种自平衡二叉搜索树,它通过在每次访问节点时将其移动到根节点来保持平衡。这使得最常访问的节点保持在靠近根的位置,提高了操作的效率。

  4. Treap:Treap是一种结合了二叉搜索树和堆(heap)性质的自平衡树,节点具有键值和随机的优先级值。它通过维护这两个属性来保持平衡。

这些二次搜索树的选择取决于应用的具体需求和性能要求。它们在不同情况下都表现出不同的性能特点,因此需要根据具体情况选择合适的数据结构。

 

1. 自平衡二叉搜索树 (Balanced Binary Search Tree): 自平衡二叉搜索树是一种特殊类型的二叉搜索树,它确保树的高度始终保持在较小范围内,从而保持了查找、插入和删除操作的平均时间复杂度为 O(log n)。这些树根据节点之间的平衡特性来维护其结构,以避免退化成链表。

2. 最佳实践: 选择自平衡二叉搜索树时,通常可以考虑以下几种常见的选项:

  • AVL树:AVL树通过维护节点的平衡因子(左子树高度与右子树高度之差)来保持平衡。在大多数情况下,AVL树是一个很好的选择,但它的平衡维护可能需要更多的旋转操作。

  • 红黑树:红黑树是另一种常用的自平衡二叉搜索树,它通过节点的颜色属性和一组规则来保持平衡。红黑树的性能非常稳定,适用于许多应用场景。

  • Splay树:Splay树通过将最近访问的节点移动到根节点来保持平衡,以提高最常访问的节点的效率。这对于缓存和频繁访问相同数据的应用很有用。

  • Treap:Treap是一种结合了二叉搜索树和堆属性的自平衡树,节点具有键值和随机的优先级值。这种树在同时要求搜索和插入性能时可能很有用。

Python示例

自平衡二叉排序树-红黑树

复制代码
from sortedcontainers import SortedDict

# 创建一个红黑树
rb_tree = SortedDict()

# 插入元素
rb_tree[3] = 'apple'
rb_tree[1] = 'banana'
rb_tree[5] = 'cherry'
rb_tree[2] = 'date'

# 查找元素
print(rb_tree[3])  # 输出 'apple'

# 删除元素
del rb_tree[1]

# 遍历树中的元素
for key, value in rb_tree.items():
    print(key, value)
复制代码

输出:

1
2
3
4
apple
2 date
3 apple
5 cherry

  

python自定义一个二叉搜索树

1. 二叉搜索树的实现:

在Python中,你可以使用类来实现二叉搜索树。每个节点(也是一个类)由一个值、左子节点和右子节点组成。

复制代码
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

class BinarySearchTree:
    def __init__(self):
        self.root = None
复制代码

2. 插入操作:

要向BST中插入一个新节点,需要遵循BST的性质。递归地比较新节点的值与当前节点的值,并根据大小关系选择左子树或右子树进行插入。

复制代码
def insert(self, value):
    if not self.root:
        self.root = TreeNode(value)
    else:
        self._insert(self.root, value)

def _insert(self, node, value):
    if value < node.value:
        if node.left:
            self._insert(node.left, value)
        else:
            node.left = TreeNode(value)
    elif value > node.value:
        if node.right:
            self._insert(node.right, value)
        else:
            node.right = TreeNode(value)
复制代码

3. 查找操作:

查找操作也是递归的。从根节点开始,与目标值进行比较,然后选择左子树或右子树来查找。

复制代码
def find(self, value):
    return self._find(self.root, value)

def _find(self, node, value):
    if not node:
        return False
    if value == node.value:
        return True
    elif value < node.value:
        return self._find(node.left, value)
    else:
        return self._find(node.right, value)
复制代码

4. 删除操作:

删除节点涉及到多种情况,包括没有子节点、有一个子节点、有两个子节点等情况。这需要小心处理。你可以实现一个 _delete 方法,来处理这些情况。

 

5. 综合示例

 

复制代码
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None


class BinarySearchTree:
    def __init__(self):
        self.root = None

    def insert(self, value):
        if not self.root:
            self.root = TreeNode(value)
        else:
            self._insert(self.root, value)

    def _insert(self, node, value):
        if value < node.value:
            if node.left:
                self._insert(node.left, value)
            else:
                node.left = TreeNode(value)
        elif value > node.value:
            if node.right:
                self._insert(node.right, value)
            else:
                node.right = TreeNode(value)

    def find(self, value):
        return self._find(self.root, value)

    def _find(self, node, value):
        if not node:
            return False
        if value == node.value:
            return True
        elif value < node.value:
            return self._find(node.left, value)
        else:
            return self._find(node.right, value)


bst = BinarySearchTree()
bst.insert(10)
bst.insert(5)
bst.insert(15)
bst.insert(2)
bst.insert(7)

print(bst.find(5))  # 输出 True
print(bst.find(12))  # 输出 False


# 中序遍历二叉搜索树
def inorder_traversal(node):
    if node:
        inorder_traversal(node.left)
        print(node.value)
        inorder_traversal(node.right)


inorder_traversal(bst.root)
复制代码

输出:

1
2
3
4
5
6
7
True
False
2
5
7
10
15

  

Java实现二叉搜索树

1. 树节点

复制代码
public class TreeNode {
    private int value;

    private TreeNode left;
    private TreeNode right;

    TreeNode(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public TreeNode getLeft() {
        return left;
    }

    public void setLeft(TreeNode left) {
        this.left = left;
    }

    public TreeNode getRight() {
        return right;
    }

    public void setRight(TreeNode right) {
        this.right = right;
    }
}
复制代码

 

2. 二叉搜索树

复制代码
public class BinarySearchTree {
    private TreeNode root;

    public void insert(int value){
        if(root == null){
            root = new TreeNode(value);
        }else {
            insert(root,value);
        }

    }

    /**
     * 插入节点
     * @param node
     * @param value
     */
    private void insert(TreeNode node,int value){
        // 插入值小于当前节点value,则插入在其左边树
        if(value < node.getValue()){
            if(node.getLeft() != null){
                insert(node.getLeft() ,value);
            }else {
                node.setLeft(new TreeNode(value));
            }
        }else {
            if(node.getRight() != null){
                insert(node.getRight(),value);
            }else {
                node.setRight(new TreeNode(value));
            }
        }
    }


    public boolean find(int value){
        return find(this.root,value);
    }

    private boolean find(TreeNode node,int value){
        if(node == null){
            return false;
        }
        if(value == node.getValue()){
            return true;
        }else if(value < node.getValue()){
            return find(node.getLeft(),value);
        }else {
            return find(node.getRight(),value);
        }
    }
}
复制代码

测试:

复制代码
public static void inOrderTraversal(TreeNode node){
        if(node != null){
            inOrderTraversal(node.getLeft());
            System.out.println(node.getValue());
            inOrderTraversal(node.getRight());
        }
    }
    public static void main(String[] args) {
        BinarySearchTree binarySearchTree = new BinarySearchTree();
        binarySearchTree.insert(10);
        binarySearchTree.insert(5);
        binarySearchTree.insert(15);
        binarySearchTree.insert(2);
        binarySearchTree.insert(7);
        System.out.println(binarySearchTree.find(5));
        System.out.println(binarySearchTree.find(12));
        inOrderTraversal(binarySearchTree.root);
    }
复制代码

输出:

1
2
3
4
5
6
7
true
false
2
5
7
10
15

  

posted @   Allen_Hao  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示