用Python实现数据结构之二叉搜索树

二叉搜索树

二叉搜索树是一种特殊的二叉树,它的特点是:

  • 对于任意一个节点p,存储在p的左子树的中的所有节点中的值都小于p中的值
  • 对于任意一个节点p,存储在p的右子树的中的所有节点中的值都大于p中的值

一个图例:

基于二叉搜索树的这种关系,我们可以用它来实现有序映射

遍历二叉搜索树

基于二叉搜索树的特性,采用中序遍历的方式可以使得遍历结果是按照从小到大的顺序排列的。了解中序遍历可以参考用Python实现数据结构之树

这里还需要思考的一个内容是在基于中序遍历的前提下,如何求一个节点的后继节点或前驱节点。

显然这就是求比该节点大的下一个节点或比它小的前一个节点。我们拿求后继节点为例:

  • 当该节点有右孩子时,那么后继结点就是右子树中最左边的节点
  • 当该节点没有右孩子时,那么后继节点就是第一个是左孩子的祖先的父节点

算法用伪代码表示为:


def after(p):
    """寻找二叉搜索树的后继节点的伪代码"""

    if right(p) is not None:
        walk = right(p)
        while left(right(p)) is not None:  # 找最左
            walk = left(walk)
        return walk
    else:
        walk = p
        ancestor = parent(walk)
        while ancestor is not None and walk == right(ancestor):  # 当walk是左孩子时或walk是根节点时停止
            walk = ancestor
            ancestor = parent(walk)
        return ancestor

找前驱同理

搜索

既然叫做二叉搜索树,那它很重要的一个用途就是搜索,搜索的方式为:

  • 与根节点比较,如果相等则根节点就是要搜索的位置
  • 比根节点小,则递归的与左孩子比较
  • 比根节点大,则递归的与有孩子比较
  • 如果最后没找到,则返回最后一个与之比较的节点,意味着可以在这个节点位置插入刚才搜索的值

算法用伪代码表示为:


def search(T,p,k):
    """二叉树搜索的伪代码,k是要搜索的值"""
    if k == p.key():
        return p
    elif k < p.key() and T.left(p) is not None:
        return search(T,T.left(p))
    elif k > p.key() and T.right(p) is not None:
        return search(T,T.right(p))
    return p

搜索的时间与高度有关,是O(h),也就是最坏的情况下为O(n),最好的情况下是O(log(n))

插入

插入算法较简单,它依赖于搜索算法,将搜索的返回的位置的值与key进行比较,

  • 如果相等,则就在此位置修改
  • 如果小于返回位置的值,则插入到返回位置的左孩子
  • 如果小于返回位置的值,则插入到返回位置的右孩子

删除

删除操作较为复杂,因为删除的位置可以是任意的位置,设删除的位置为p

  • 如果删除的位置没有孩子,则直接删了就行
  • 如果删除的位置有一个孩子,则删除之后把它的孩子接到它原来的位置上
  • 如果删除的位置有两个孩子,则:

1.先找到位置p的前驱r,前驱在左子树中

2.把p删除,将r代替p

3.把r原来的位置删除

使用前驱的原因是它必然比p的右子树的所有节点小,也必然比除了r的p的左子树的所有节点大

python实现

我们利用二叉树来实现有序映射


class OrderedMap(BinaryTree,MutableMapping):
    """使用二叉搜索树实现的有序映射"""

    class _item():

        def __init__(self, key, value):
            self.key = key
            self.value = value

        def __eq__(self, other):
            return self.key == other.key

        def __ne__(self, other):
            return self.key != other.key

        def __lt__(self, other):
            return self.key < other.key

    class Position(BinaryTree.Position):

        def key(self):
            return self.element().key

        def value(self):
            return self.element().value

BinaryTree是在之前文章中定义的二叉树类,具体参考用Python实现数据结构之树

首先定义了两个内嵌类,一个表示键值项,一个用于封装节点

然后定义些非公开方法用于其他方法使用:


def _subtree_search(self, p, k):
    """搜索算法"""
    if k == p.key():
        return p
    elif k < p.key():
        if self.left(p) is not None:
            return self._subtree_search(self.left(p), k)
    else:
        if self.right(p) is not None:
            return self._subtree_search(self.right(p), k)
    return p

def _subtree_first_position(self, p):
    """返回树的最左节点"""
    walk = p
    while self.left(walk) is not None:
        walk = self.left(walk)
    return walk

def _subtree_last_position(self, p):
    """返回树的最右节点"""
    walk = p
    while self.right(walk) is not None:
        walk = self.right(walk)
    return walk

下面是一些公开的访问方法:


def first(self):
    return self._subtree_first_position(
        self.root()) if len(self) > 0 else None

def last(self):
    return self._subtree_last_position(
        self.root()) if len(self) > 0 else None

def before(self, p):
    """前驱位置"""
    if self.left(p):
        return self._subtree_last_position(self.left(p))
    else:
        walk = p
        above = self.parent(walk)
        while above is not None and walk == self.left(above):
            walk = above
            above = self.parent(walk)
        return above

def after(self, p):
    """后继位置"""
    if self.right(p):
        return self._subtree_first_position(self.right(p))
    else:
        walk = p
        above = self.parent(walk)
        while above is not None and walk == self.right(above):
            walk = above
            above = self.parent(walk)
        return above

def find_position(self,k):
    if self.is_empty():
        return None
    else:
        p = self._subtree_search(self.root(),k)
        return p

接下来是映射的核心方法:


def __getitem__(self, k):
    if self.is_empty():
        raise KeyError(‘Key Error‘+repr(k))
    else:
        p = self._subtree_search(self.root(),k)
        if k!=p.key():
            raise KeyError(‘Key Error‘ + repr(k))
        return p.value()

def __setitem__(self, k,v):
    if self.is_empty():
        leaf = self.add_root(self._Item(k,v))
    else:
        p = self._subtree_search(self.root(),k)
        if p.key() == k:
            p.element().value = v
            return
        else:
            item = self._Item(k,v)
            if p.key() < k:
                leaf = self.add_right(p,item)
            else:
                leaf = self.add_left(p, item)

def __iter__(self):
    p = self.first()
    while p is not None:
        yield p.key()
        p = self.after(p)

def mapdelete(self,p):
    if self.left(p) and self.right(p):      #两个孩子都有的时候
        replacement = self._subtree_last_position(self.left(p)) #用左子树最右位置代替
        self.replace(p,replacement.element())
        p = replacement
    self.delete(p)

def __delitem__(self, k):
    if not self.is_empty():
        p = self._subtree_search(self.root(),k)
        if k == p.key():
            self.mapdelete(p)
            return
    raise KeyError(‘Key Error‘ + repr(k))

最后是一些有序映射特有的方法:


def find_min(self):
    """找最小值,返回键值元组"""
    if self.is_empty():
        return None
    else:
        p = self.first()
        return(p.key(), p.value())

def find_ge(self, k):
    """找第一个大于等于k的键值元组"""
    if self.is_empty():
        return None
    else:
        p = self.find_position(k)
        if p.key() < k:
            p = self.after(p)
        return (p.key(), p.value()) if p is not None else None

def find_range(self, start, stop):

    if not self.is_empty():
        if start is None:
            p = self.first()
        else:
            p = self.find_position(start)
            if p.key() < start:
                p = self.after(p)
        while p is not None and (stop is None or p.key() < stop):
            yield (p.key(), p.value())
            p = self.after(p)

?

原文地址:https://www.cnblogs.com/sfencs-hcy/p/10353006.html

时间: 2024-07-29 06:29:32

用Python实现数据结构之二叉搜索树的相关文章

Java数据结构之二叉搜索树

Java数据结构之二叉搜索树 1.二叉搜索树组成 二叉搜索树又称为二叉排序树,它或者是一颗空树,或者是一颗具有如下特性的非空二叉树,需要满足一下三个条件: (1)若它的左子树非空,则左子树上所有结点的关键字均小于根结点的关键字: (2)若它的右子树非空,则右子树上所有结点的关键字均大于(可以等于)根结点的关键字. (3)左子树右子树本身又各是一颗二叉搜索树 在算法描述中,均以结点值的比较来代表其关键字的比较,因为若结点的值为类类型时,该类必须实现系统提供的java.lang.comparable

数据结构之二叉搜索树、AVL自平衡树

前言 最近在帮公司校招~~ 所以来整理一些数据结构方面的知识,这些知识呢,光看一遍理解还是很浅的,看过跟动手做过一遍的同学还是很容易分辨的哟~ 一直觉得数据结构跟算法,就好比金庸小说里的<九阳神功>,学会九阳神功后,有了内功基础,再去学习其他武功,速度就有质的提升 内容大概包含这些,会分多篇文章来整理: 二叉搜索树 平衡二叉树(AVL) 二叉堆 堆排序 四叉树 八叉树 图,深度优先DFS.广度优先BFS 最短路径 二叉树 二叉树,也就是每个节点最多有两个孩子的树.多用于搜索,查找,还有可以用来

数据结构之二叉搜索树的实现

二叉搜索树的接口类 public interface BinarySearchTree extends BinaryTree {     Object find(Object obj);     Object update(Object obj);     boolean insert(Object obj);     boolean delete(Object obj);     void ascend(); } 二叉搜索树的链接存储类的实现 public class LinkBinarySe

数据结构之二叉搜索树

C语言实现二叉搜索树很简单,权当复习下指针知识//// Created by SuperHakce on 2018/3/29.// #ifndef BINARYSEARCHTREE_BINARYTREE_H #define BINARYSEARCHTREE_BINARYTREE_H typedef struct BinaryTreeNode{ int key; struct BinaryTreeNode *leftChild; struct BinaryTreeNode *rightChild;

数据结构 - 从二叉搜索树说到AVL树(一)之二叉搜索树的操作与详解(Java)

二叉搜索树(Binary Search Tree),简称BST,顾名思义,一颗可以用于搜索的二叉树.BST在数据结构中占有很重要的地位,一些高级树结构都是其的变种,例如AVL树.红黑树等,因此理解BST对于后续树结构的学习有很好的作用.同时利用BST可以进行排序,称为二叉排序,也是很重要的一种思想. 二叉树的性质:任意一个节点的所有左子树元素都比该元素值要小,而所有右子树元素都比该元素值要大. 符合该性质的二叉树就是一颗二叉搜索树,当然前提下是树中不允许有重复元素. 所有的二叉搜索树的中序遍历序

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值

数据结构-二叉搜索树的js实现

一.树的相关概念 1.基本概念 子树 一个子树由一个节点和它的后代构成. 节点的度 节点所拥有的子树的个数. 树的度 树中各节点度的最大值 节点的深度 节点的深度等于祖先节点的数量 树的高度 树的高度等于所有节点深度的最大值 森林 若干课互不相交的树的集合.任何一棵树,删去根节点就变成了森林. 2. 二叉树 二叉树的定义 (1)二叉树中每个节点的度不大于2 (2)二叉树是有序的,其子树有左右之分,其次序不能随意颠倒 二叉树的性质 第k层上最多有2^(k-1)个节点 深度为k的二叉树最多有2^(k

数据结构之二叉堆、堆排序

前言 上一篇写了数据结构之二叉搜索树.AVL自平衡树,这次来写堆. 堆的创造者 很久以前排序算法的时间复杂度一直是O(n^2), 当时学术界充斥着"排序算法不可能突破O(n^2)"的声音,直到1959年,由D.L.Shell提出了一种排序算法,希尔排序(Shell Sort),才打破了这种不可能的声音,把排序算法的时间复杂度提升到了O(n^3/2)! 当科学家们知道这种"不可能"被突破之后,又相继有了更快的排序算法,"不可能超越O(n^2)"彻底

小白专场-是否同一颗二叉搜索树-python语言实现

目录 一.二叉搜索树的相同判断 二.问题引入 三.举例分析 四.方法探讨 4.1 中序遍历 4.2 层序遍历 4.3 先序遍历 4.4 后序遍历 五.总结 六.代码实现 更新.更全的<数据结构与算法>的更新网站,更有python.go.人工智能教学等着你:https://www.cnblogs.com/nickchen121/p/11407287.html 一.二叉搜索树的相同判断 二叉搜索树是一种特殊的二叉树,在一定程度上是基于二分查找思想产生的,在它的任何一个节点node处,node的左子