树:是n(n>n)个结点的有限集,当n=0时称为空树。

特点:

  1. 在任意一课非空树中,有且仅有一个特定的称为根(root)的结点;
  2. 子树的个数是没有限制的,但它们互相是一定不会相交的。

二叉树(Binary Tree)

二叉树是n个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根节点和两颗互不相交的,分别称为根节点的左子树和右子树的二叉树组成

满二叉树:所有的分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为:满二叉树。

 

 

 

 

满二叉树的特点:

  1. 叶子只能出现在最下一层。
  2. 非叶子结点的度一定是二。
  3. 在同样的深度的二叉树中,满二叉树的节点个数一定是最多的。同时叶子也是最多的。

完全二叉树:对一颗具有n个结点的二叉树按层编号,如果编号为X(1<=i<=n)的结点与同样深度的满二叉树中的X的结点位置完全相同,则这颗二叉树称为完全二叉树。

 

非完全二叉树:

 

 

 

完全二叉树的特点:

  1. 叶子结点只能出现在最下两层。.
  2. 最下层的叶子一定集中在左部连续位置。
  3. 倒数第二层,若有结点一定都在右边连续位置。
  4. 如果结点度为1,则该结点只有左孩子。
  5. 同样结点的二叉树,完全二叉树的深度最小。

注意:满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树。

 

二叉树的性质:

  1. 在二叉树的第i层上至多有2^(i-1)个结点。
  2. 深度为k的二叉树至多有2k-1个结点。
  3. 具有n个结点的完全二叉树的深度为( 的向下取整+1 ).
  4. 对于具有n个结点的完全二叉树对于任意结点i有如下性质:

a)       当i>1 则双亲结点是:i//2. 比如3的双亲结点为1

b)       如果:2i>n,则结点i无左孩子,如果2i<n,则有左孩子且左孩子为2i.

c)        如果:2i+1>n,则结点i无右孩子,如果2i+1<n,则有右孩子且右孩子为2i+1.

二叉树的存储结构

有循序存储和链式存储结构

二叉树的顺序存储结构就是用一堆数组存储二叉树中的各个节点,并且结点的存储位置能体现结点之间的逻辑关系

 

 

非完全二叉树:

 

但是对于一般二叉树来说比如斜树来说,用顺序存储结构浪费空间,所以我们就要考虑链式存储结构了。二叉树的存储在一般情况下都是用链式存储。

二叉树的链式存储

我们知道二叉树的每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域。这样的链表叫二叉链表

 

 

 

将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接。

 

 

二叉树链式存储的遍历

二叉树的遍历是指按照某种次序依次访问二叉树中所有的节点,使得每个结点被访问一次。

树的节点之间不存在线性结构只有前驱和后继这样的关系,在访问数的一个节点后,下一个被访问的节点面临着不同的选择。即一对多的选择。

二叉树的遍历有4种标准类型,分别叫前序遍历,中序遍历,后序遍历,层序遍历。

前序遍历:若二叉树为空,则空操作返回,否则先访问跟节点,然后遍历左子树,最后遍历右子树。

中序遍历:先访问左子树,在访问根节点,最后遍历右子树。注意没有左子树就先访问根。

后序遍历:先访问左子树,然后访问右子树,最后访问根。

层序遍历:首先从0层开始,在每一层级按照从左到右的顺序访问节点。

 

前序遍历顺序为: E A C B D G F

中序遍历顺序为: A B C D E G F

后序遍历顺序为: B D C A F G E

层序遍历顺序为: E A G C F B D

 

其实前中后指的是遍历根节点的位置。先遍历根叫前序遍历,第二遍历根叫中序,最后遍历根叫后序遍历

二叉树的遍历代码:

 

 1 from collections import deque  #双向队列
 2 from  queue import Queue       #单向队列
 3 class BTreeNode:
 4     def __init__(self,data):
 5         self.data=data
 6         self.lchild=None
 7         self.rchild=None
 8     @classmethod #这里为什么用类方法因为递归的时候不用在传self
 9     def pre_order(cls,root):#
10         '''
11         前序遍历(root,l,r)
12         :param root:
13         :return:
14         '''
15         if root: #如果跟存在就递归调用该函数,输出root
16             print(root.data,end='')
17             cls.pre_order(root.lchild)
18             cls.pre_order(root.rchild)
19     @classmethod
20     def in_order(cls,root):
21         """
22         中序遍历(l,root,r)
23         :param root:
24         :return:
25         """
26         if root:
27             cls.in_order(root.lchild)
28             print(root.data,end='')
29             cls.in_order(root.rchild)
30 
31     @classmethod
32     def out_order(cls,root):
33         if root:
34             cls.out_order(root.lchild)
35             cls.out_order(root.rchild)
36             print(root.data,end='')
37 
38     @classmethod
39     def level_order(cls,root):
40         '''
41         层序遍历 先把root放到双向队列中,打印出root的值,如果root有左右孩子就把左右孩子方进入打印出其的值,循环判断双向队列中的值是否为0
42         :param root:
43         :return:
44         '''
45         queue=deque()
46         queue.append(root)
47         while len(queue)>0:
48             node=queue.popleft()
49             print(node.data,end='')
50             if node.lchild:
51                 queue.append(node.lchild)
52             if node.rchild: #这里不能用elif 因为elif是判断一个值的,if 是判断两个值的
53                 queue.append(node.rchild)
54 
55 
56 
57 
58 
59 
60 #创建二叉树
61 
62 a=BTreeNode('A')
63 b=BTreeNode('B')
64 c=BTreeNode('C')
65 d=BTreeNode('D')
66 e=BTreeNode('E')
67 
68 a.lchild=b
69 a.rchild=c
70 b.rchild=d
71 c.lchild=e
72 root=a
73 
74 #查看前序遍历的结果
75 
76 BTreeNode.pre_order(root)
77 
78 #查看中序遍历的结果
79 print('')
80 BTreeNode.in_order(root)
81 print('')
82 #查看后序遍历的结果
83 BTreeNode.out_order(root)
84 
85 print('')
86 #查看后序遍历的结果
87 BTreeNode.level_order(root)
遍历代码

遍历结果:

ABDCE  #前序遍历
BDAEC  #中序遍历
DBECA  #后序遍历
ABCDE  #层序遍历

 

二叉搜索树

 

二叉搜索树(binary search Tree)是一种数据结构:在二叉树中的节点上施加了一种特殊的顺序。它支持对数时间的搜索和插入,在庞大的数据中能够高效的搜索,插入,删除。

性质

  1. 一个节点如果它的左子树不为空,左子树上所有的值均小于该结点的值,
  2. 若它的右子树不为空,则右子树所有的节点均大于它的根节点的值
  3. 左右子树均为二叉树搜索树(这就是递归的定义过程)

 

对途中的二叉树搜索进行创建,添加,删除,查询,求最大节点和最小节点

删除节点有三种情况:

  1. 删除节点为叶子节点,则直接删除就可以了。
  2. 删除的节点只有左子树或只有右子树,则直接把左右子树接上去就可以了。
  3. 删除的节点既有左子树和右子树。解决方法为找到左子树中的最大节点替换掉删除的节点或找到右子树最小的节点替换掉删除的节点,然后记得把最大节点或最小节点在原来的子树中删除掉用递归

 

 

class Node:
    def __init__(self,data):
        self.data=data
        self.rchild=None
        self.lchild=None

    @classmethod
    def mid_sort(cls, root):
        """
        中序遍历
        :param root:
        :return:
        """
        if root:
            cls.mid_sort(root.lchild)
            print(root.data, end=' ')
            cls.mid_sort(root.rchild)
    @classmethod
    def insert(cls,root,item):
        """
        二叉搜索树的插入
        :param root:
        :param item:
        :return:
        """
        if root:
            if item>root.data:
                if root.rchild:
                    cls.insert(root.rchild,item)
                else:
                    root.rchild=Node(item)
            else:
                if root.lchild:
                    cls.insert(root.lchild,item)
                else:
                    root.lchild=Node(item)

    @classmethod
    def serach(cls,root,val):
        """
        二叉搜索树的查询
        :param root:
        :param val:
        :return:
        """
        if root:
            if val == root.data:
                return True
            elif root.data > val:
               return cls.serach(root.lchild,val)#这里要加return如果不加返回的始终都是None,,当递归函数有return时,在递归的地方也要return,不然永远返回的是None
            else:
                return cls.serach(root.rchild,val)
        else:
             return False

    @classmethod
    def find_min(cls,root):
        """
        找出最小的节点
        :param root:
        :return:
        """
        if root:
            if root.lchild:
                return cls.find_min(root.lchild)
            else:
                print(root.data)
                return root

    @classmethod
    def find_Max(cls,root):
        """
                找出最大的节点
                :param root:
                :return:
                """
        if root:
            if root.rchild:
                return cls.find_Max(root.rchild)
            else:
                print('最大值',root.data)
                return root

    @classmethod
    def delete(cls,root,val):
        """
        二叉树的删除
        :param root:
        :param val:
        :return:
        """
        if root:
            if root.data>val:
                cls.delete(root.lchild,val)
            elif root.data< val:
                cls.delete(root.rchild,val)
            else:#当val==root.data 时,分三种情况:无左右子树,有左右子树,左右子树只有一个
                if root.rchild and root.lchild:#当有左右子树时候,找到右子树的上最小的节点,
                    temp=cls.find_min(root.rchild)#
                    root.data=temp.data#把根的data属性改成子树的data属性
                    #在把右子树上的最小值删除
                    root.rchild=cls.delete(root.rchild,temp.data)
                elif root.rchild==None and root.lchild==None : #当没有左右子树的时候:
                    root=None
                elif root.rchild==None: #当没有右子树的时候
                    root=root.lchild
                else:
                    root=root.rchild
            return root
        else:
            print('没有要删除的数据')

    @classmethod
    def mid_sort(cls,root):
        if root:
            cls.mid_sort(root.lchild)
            print(root.data,end=' ')
            cls.mid_sort(root.rchild)




#创建二叉搜索树

p1=Node(17)
p2=Node(5)
p3=Node(2)
p4=Node(16)
p5=Node(35)
p6=Node(29)
p7=Node(38)

p1.lchild=p2
p1.rchild=p5
p2.lchild=p3
p2.rchild=p4
p5.lchild=p6
p5.rchild=p7
root=p1


#插入:
Node.insert(root,19)
Node.mid_sort(root)
print('')
#查询
# print(Node.serach(root,38))
#查询最小值
Node.find_min(root)
Node.find_Max(root)

#删除节点
Node.delete(root,35)
Node.mid_sort(root)
View Code

 

结果:

2 5 16 17 19 29 35 38
2
最大值 38
38
2 5 16 17 19 29 38

二叉搜索树的中序遍历打印出来的数列将是按照递增顺序排列。

参考https://www.cnblogs.com/lliuye/p/9118591.html

AVL树

  

二叉排序树根据节点插入的顺序不同,将导致不同的深度和平均查找长度不同,最后导致查询每个结点的平均次数也是不同,这样就影响到了查询效率,特别是遇到第三种情况,还没有不用二叉树结构快呢。为了解决二叉树查询效率不一致的问题?

 平衡二叉排序树就应运而生了。

平衡二叉树又称为AVL树。定义:要么它是空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1(也叫平衡因子)。平衡二叉树是在二叉搜索树的基础上来的,所以它要符合二叉搜索树。

上面的第二张图就是平衡二叉树,可以看出平衡二叉树的查询次数最少,查询效率最高。

平衡二叉树的目的让数的深度低。在给定的n个结点的情况下,AVL树最大高度为,也就是AVL树的时间负责度为O().

 

 

 

下图为平衡二叉树的识别图:

 AVL树的插入

右单旋转

 

 我们来分析一下:

当Nov插上去的时候,破坏了mar的平衡性,平衡因子变为-2了,mar被称为被破坏者,Nov被称为破坏者,因为,Nov在Mar的右子树的右子树上,需要把被破坏者Mar右旋,把May变成根节点。这就是右旋。

分析一下:在没有插入15的时候是AVL树,插入15后,5节点变成了被破坏者,15节点成了破坏者,破坏者在被破坏者的右子树的右子树上,这时候需要右旋,把5节点右旋,让10节点当根节点,你会发现

二叉树变成三叉树了,又因为8节点比10节点小,比5节点大,所以就放到5节点的右子树上

 左单旋转

 

 分析一下:

当Apr插入进来时,破坏了Mar节点和May节点的平衡性,这时候我们只需要考虑Mar的平衡性就可以了,Apr在Mar的左子树的左子树上,需要左单旋转,Mar左旋,Aug替代Mar的位置

左右旋转

 

分析一下:当jan插入进来时,May称为了被破坏者,且,Jan在May的左子树的右子树上,这时候需要左右旋转,先把May左旋转,再把Mar右旋转称为根节点,

右左旋转

 

 

 

 当插入Feb时aug的平衡被破坏,Feb在Aug的右子树的左子树上,所以用右左旋转,关键调整Aug、Jan、Dec节点的位置,Aug右旋转,jan不动,Dec左旋转,最后在调节Feb的位置。

 

要点:关键看,破坏者在被破坏者的哪个位置

 

 

posted on 2018-11-24 21:37  程序员一学徒  阅读(197)  评论(0)    收藏  举报