(数据结构)树(一)二叉树

最近开始准备找实习,把一些基础的东西捡捡,进行一下汇总。数据结构中有很多树的结构,其中包括二叉树、二叉搜索树、2-3树、红黑树等等。本文中对数据结构中常见的几种树的概念和用途进行了汇总,不求严格精准,但求简单易懂。
树是由根结点和若干颗子树构成的。树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构。在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点,或称为树根。
将用python实现对树的完整实现,首先对树进行抽象定义,下文将进行具体实现。

class Tree():
    """
    树的抽象基类
    """

    # 叫做位置的内嵌类,用于封装节点
    class Position():

        def element(self):
            raise NotImplementedError('must be implemented by subclass')

        def __eq__(self, other):
            raise NotImplementedError('must be implemented by subclass')

    def root(self):
        """
        return 根节点的position
        """
        raise NotImplementedError('must be implemented by subclass')

    def parent(self,p):
        """

        :param p:一个位置对象
        :return: 返回p的父节点的position对象,如果p是根节点则饭后空
        """
        raise NotImplementedError('must be implemented by subclass')

    def num_children(self,p):
        """

        :param p:一个位置对象
        :return: 返回该位置的孩子节点的数量
        """
        raise NotImplementedError('must be implemented by subclass')

    def children(self,p):
        """

        :param p: 一个位置对象
        :return: 返回位置p的孩子的迭代
        """
        raise NotImplementedError('must be implemented by subclass')

    def __len__(self):
        """

        :return: 返回整个树的节点个数
        """

     raise NotImplementedError('must be implemented by subclass')

    def is_root(self,p):
        return self.root() == p

    def is_leaf(self,p):
        return self.num_children(p) == 0

    def is_empty(self):
        return len(self) == 0

	def depth(self,p):
    """
    计算节点在树中的深度
    """
    if self.is_root(p):
        return 0
    else:
        return 1 + self.depth(self.parent(p))

	def height(self,p):
	    """
	    计算节点在树中的深度 
	    """
	    if self.is_leaf(p):
	        return 0
	    else:
	        return 1 + max(self.height(c) for c in self.children(p))

1.二叉树

二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构。

二叉树的定义:二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有 2 i − 1 2^{i-1} 2i1 个结点;深度为k的二叉树至多有 2 k − 1 2^{k-1} 2k1个结点;对任何一棵二叉树T,如果其终端结点数为 n 0 n_{0} n0,度为2的结点数为 n 2 n_{2} n2,则 n 0 = n 2 + 1 n_{0}=n_{2}+1 n0=n2+1

二叉树的示例:
  二叉树
满二叉树和完全二叉树

满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。也可以这样理解,除叶子结点外的所有结点均有两个子结点。节点数达到最大值,所有叶子结点必须在同一层上。

满二叉树的性质:

  1. 一颗树深度为h,最大层数为k,深度与最大层数相同,k=h;
  2. 叶子数为 2 h 2^{h} 2h;
  3. 第k层的结点数是: 2 k − 1 2^{k-1} 2k1;
  4. 总结点数是: 2 k − 1 2^{k-1} 2k1,且总节点数一定是奇数;

完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~(h-1)层) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。

注:完全二叉树是效率很高的数据结构,堆是一种完全二叉树或者近似完全二叉树,所以效率极高,像十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能优化,二叉排序树的效率也要借助平衡性来提高,而平衡性基于完全二叉树。

在这里插入图片描述
二叉树的性质:

  1. 在非空二叉树中,第i层的结点总数不超过 2 i − 1 2^{i-1} 2i1, i>=1;

  2. 深度为h的二叉树最多有 2 h − 1 2^{h}-1 2h1 个结点(h>=1),最少有h个结点

  3. 对于任意一棵二叉树,如果其叶结点数为 N 0 N_{0} N0,而度数为2的结点总数为 N 2 N_{2} N2,则 N 0 N_{0} N0= N 2 N_{2} N2+1;

  4. 具有n个结点的完全二叉树的深度为 l o g 2 ( n + 1 ) log_{2}(n+1) log2(n+1);

  5. 有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
        若I为结点编号则 如果I>1,则其父结点的编号为I/2;
        如果2I<=N,则其左儿子(即左子树的根结点)的编号为2I;若2I>N,则无左儿子;
        如果2I+1<=N,则其右儿子的结点编号为2I+1;若2I+1>N,则无右儿子。

  6. 给定N个节点,能构成h(N)种不同的二叉树,其中h(N)为卡特兰数的第N项,h(n)=C(2*n, n)/(n+1)

  7. 设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和 J = I + 2 i J=I+2^{i} J=I+2i

二叉树相关python实现源码:

class BinaryTree(Tree):

    class Node():

        def __init__(self, element, parent=None, left=None, right=None):
            self.element = element
            self.parent = parent
            self.left = left
            self.right = right

    class Position(Tree.Position):

        def __init__(self, container, node):
            self.container = container
            self.node = node

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

        def __eq__(self, other):
            return isinstance(other, type(self)) and other.node is self.node

    def validate(self, p):
        """
        进行位置验证
        """
        if not isinstance(p, self.Position):
            raise TypeError('p must be proper Position type')
        if p.container is not self:
            raise ValueError('p does not belong to this container')
        if p.node.parent is p.node:
            raise ValueError('p is no longer valid')
        return p.node

    def make_position(self, node):
        """
        封装节点
        """
        return self.Position(self, node) if node is not None else None

    def __init__(self):
        self._root = None
        self.size = 0

    def __len__(self):
        return self.size

    def root(self):
        return self.make_position(self._root)

    def parent(self, p):
        node = self.validate(p)
        return self.make_position(node.parent)

    def left(self, p):
        node = self.validate(p)
        return self.make_position(node.left)

    def right(self, p):
        node = self.validate(p)
        return self.make_position(node.right)

    def sibling(self, p):
        parent = self.parent(p)
        if parent is None:
            return None
        else:
            if p == self.left(parent):
                return self.right(parent)
            else:
                return self.left(parent)

    def num_children(self, p):
        node = self.validate(p)
        count = 0
        if node.left is not None:
            count += 1
        if node.right is not None:
            count += 1
        return count
  

    def children(self,p):
        if self.left(p) is not None:
            yield self.left(p)
        if self.right(p) is not None:
            yield self.right(p)
     def add_root(self, e):
    if self._root is not None:
        raise ValueError('Root exists')
    self.size += 1
    self._root = self.Node(e)
    return self.make_position(self._root)

	def add_left(self, e, p):
	    node = self.validate(p)
	    if node.left is not None:
	        raise ValueError('Left child exists')
	    self.size += 1
	    node.left = self.Node(e, node)
	    return self.make_position(node.left)
	
	def add_right(self, e, p):
	    node = self.validate(p)
	    if node.right is not None:
	        raise ValueError('Left child exists')
	    self.size += 1
	    node.right = self.Node(e, node)
	    return self.make_position(node.right)
	
	def replace(self, p, e):
	    node = self.validate(p)
	    old = node.element
	    node.element = e
	    return old
	
	def delete(self, p):
	    """
	    删除该位置的节点,如果该节点有两个孩子,则会产生异常,如果只有一个孩子,
	    则使其孩子代替该节点与其双亲节点连接
	    """
	    node = self.validate(p)
	    if self.num_children(p) == 2:
	        raise ValueError('p has two children')
	    child = node.left if node.left else node.right
	    if child is not None:
	        child.parent = node.parent
	    if node is self._root:
	        self._root = child
	    else:
	        parent = node.parent
	        if node is parent.left:
	            parent.left = child
	        else:
	            parent.right = child
	    self.size -= 1
	    node.parent = node
	    return node.element
	    
	def preorder(self,p):
    """
    先序遍历节点p为根节点的树
    """
    yield p
    for c in self.children(p):
        for other in self.preorder(c):
            yield other
       
    def postorder(self,p):
    """
    后序遍历节点p为根的树 
    """
    for c in self.children(p):
        for other in self.postorder(c):
            yield other
    yield p

	def breadthfirst(self):
	    if not self.is_empty():
	        queue = Queue()
	        queue.enqueue(self.root())
	        while not queue.is_empty():
	            p = queue.dequeue()
	            yield p
	            for i in self.children(p):
	                queue.enqueue(i)

2.二叉查找树

二叉查找树定义:又称为是二叉排序树(Binary Sort Tree)或二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

  1. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  2. 若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
  3. 左、右子树也分别为二叉排序树;
  4. 没有键值相等的节点。

二叉查找树的性质对二叉查找树进行中序遍历,即可得到有序的数列。

二叉查找树的时间复杂度:它和二分查找一样,插入和查找的时间复杂度均为O(logn),但是在最坏的情况下仍然会有O(n)的时间复杂度。原因在于插入和删除元素的时候,树没有保持平衡(比如,我们查找图(b)中的“93”,我们需要进行n次查找操作)。我们追求的是在最坏的情况下仍然有较好的时间复杂度,这就是平衡查找树设计的初衷。
二叉查找树的高度决定了二叉查找树的查找效率。二叉查找树的插入过程如下:

  1. 若当前的二叉查找树为空,则插入的元素为根节点;
  2. 若插入的元素值小于根节点值,则将元素插入到左子树中;
  3. 若插入的元素值不小于根节点值,则将元素插入到右子树中。

二叉查找树的删除,分三种情况进行处理:

  1. p为叶子节点,直接删除该节点,再修改其父节点的指针(注意分是根节点和不是根节点),如图a;
  2. p为单支节点(即只有左子树或右子树)。让p的子树与p的父亲节点相连,删除p即可(注意分是根节点和不是根节点),如图b;
  3. p的左子树和右子树均不空。找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替p的值;或者方法二是找到p的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点。如图c。

在这里插入图片描述
参考:https://www.cnblogs.com/maybe2030/p/4732377.html
https://www.cnblogs.com/sfencs-hcy/p/10341449.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值