文章目录

  • 树的概念
  • 树的表示
  • 二叉树
  • 二叉树存储
  • 顺序存储
  • 链式存储
  • 二叉树的遍历
  • 中序遍历
  • 前序遍历
  • 后序遍历
  • 练习
  • 二叉树的恢复
  • 构建链式二叉树
  • 二叉排序树
  • 平衡树


树的概念

  • 一个前驱节点,多个后继节点的数据结构;
  • 树是n个节点的有限集合(n>=0),n为0的树为空树;
  • 非空树中,只有一个根节点,其余节点又可分为多个不相交的集合,形成子树;
  • 每个节点的分支数,称为该节点的度;所有节点的度的最大值,为该树的度
  • 同一层为兄弟节点,上层节点为祖先节点或者根节点,下层节点为子孙节点;
  • 从根节点开始,树的层数为树的深度;

树的表示

  • 双亲表示
    从根开始,用编号表示每个节点,节点对象存储数据和父节点
  • 孩子表示
  • 增加度(节点度)的孩子表示法
  • 数组+单链表
    数组存储每个节点,每个节点在指向包含所有子节点的单链表
  • 孩子兄弟表示

二叉树

  • 二叉树是树的一种特殊形式;使用比较多
  • 树的度最多为2,即最多有两个分支;所有的树都可以转为唯一的二叉树
  • 二叉树是有序树,所有节点都要区分是左子树、还是右子树;即使一个节点也要区分,而树在一个节点时 就不用区分;
  • 5种基本形态:空二叉树、只有一个根的二叉树、根+左子树、根+右子树、根+左右子树;
  • 性质
  • 非空二叉树 叶子节点数python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_数据结构 = 度为2的节点数python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_02
  • 非空二叉树,第i层最多有python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_python_03个节点
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_二叉树_04

  • 在一个深度为k的二叉树中,最多有python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_python_05个节点(等比数列)
  • n个节点的完全二叉树中,最大深度为python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_06
  • n个节点的完全二叉树中,从上到下、从左到右开始编号(i=1开始),i=1表示根节点,i>1时,i//2 表示其双亲节点;若2i<=n 则左孩子为2i,否则i为叶子节点(无左孩子); 若2i+1 <=n 则 i 的右孩子为2i+1 ,否则无右孩子;
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_python_07

  • 满二叉树
  • 每个非叶子节点都有两个分支;
  • 所有叶子节点都在同一层;
  • 即深度为k的二叉树,有python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_二叉树_08个节点的二叉树
  • 完全二叉树
  • 满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树;
  • 完全二叉树从上到下,从左到右依次对节点编号,与满二叉树编号对应;
  • 除最后一层外,以上所有层都是满的,最后一层叶子节点要么满、要么集中在最左边;所有叶子节点在最后一层或者倒数第二层。
  • 完全二叉树可以使用数组存储,根节点索引为python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_09,左子节点为python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_二叉树_10,右子节点为python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_数据结构_11
  • 最后一个非叶子的节点为python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_12,n为数组长度;
  • 二叉树表示

二叉树存储

顺序存储

顺序存储只适合完全二叉树,如,它是一个存在数组中的完全二叉树。

链式存储

二叉树中,一个节点存储数据、左孩子指针、右孩子指针

二叉树的遍历

中序遍历

  • 遍历左子树,根节点,遍历右子树;
  • 左子树中再递归地解决左子节点、左子树的根节点、右子节点;

递归实现

class TreeNode(object):
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

# 左子树
g = TreeNode("g")
h = TreeNode("h")
d = TreeNode('d', left=g, right=h)
b = TreeNode("b", left=d)
# 右子树
i = TreeNode("i")
e = TreeNode('e', right=i)
f = TreeNode("f")
c = TreeNode("c", left=e, right=f)

# 根节点
a = TreeNode("a", left=b, right=c)

#		b
#	d		 c
# g	   h   e   f
#			i

# 中序遍历
# 左子树、root、右子树
def visit_tree(root):
    if root is None: # java中为null
        return

    # 访问左子树
    visit_tree(root.left)
    # 访问根节点的值
    print(root.data)
    # 访问右子树
    visit_tree(root.right)


if __name__ == '__main__':
    visit_tree(a)

循环迭代
借助栈,每个root根节点压栈;处理时弹栈;

if __name__ == '__main__':
    #
    stack = []
    root = a
    while root or stack:
        while root:
            # 根节点入栈
            stack.append(root)
            root = root.left
        root = stack.pop()
        print(root.data)
        root = root.right

中序遍历:g,d,h,b,a,e,i,c,f

java循环

python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_二叉树_13


 

前序遍历

  • 先访问根节点,再先序遍历左子树、最后先序遍历右子树;
  • a,b,d,g,h,c,e,i,f

递归实现

# 先序遍历
def first_travel(root):
    if root is None:
        return
    print(root.data)
    first_travel(root.left)
    first_travel(root.right)

循环迭代

def stack_first_travel(root):
    if root is None:
        return
    stack = []

    # 先序处理
    while root or stack:
        while root:
            # 先序遍历根
            print(root.data, end=",")
            # 右子树 入栈
            if root.right:
                stack.append(root.right)
            # 先序遍历左子树
            root = root.left

        # 右子树出栈
        if stack:
            root = stack.pop()

 

后序遍历

  • 左子树、右子树、根节点
    递归实现
def last_travel(root):
    if root is None:
        return
    last_travel(root.left)
    last_travel(root.right)
    print(root.data)

练习

分别输出一下二叉树的先序遍历中序遍历后序遍历

python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子树_14


 

 

python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_15


 

二叉树的恢复

  • 任意一棵二叉树的先序遍历、中序遍历都是唯一的;
  • 已知节点的先序序列和中序序列,则可以唯一确定这棵二叉树;
  • 由先序序列,逐一获取每个根节点或子树的根节点;
  • 在中序序列中,用每个根节点,划分左右子树;
  • 再取一个根节点,递归地划分左右子树。
    例如:
    一棵二叉树的先序序列 abcdefghi; 中序序列bcaedghfi;恢复该二叉树。

    先、中可以恢复;
    后、中可以恢复;
    编码实现恢复;
  • 先序+中序 唯一恢复二叉树
# 递归恢复二叉树
def restore_btree(mid_seq): # 中序遍历序列或者子集
    global first_seq # 先序遍历全局
    if not mid_seq:  # 中序遍历的分治部分
        return None
    elif len(mid_seq) == 1:
        return TreeNode(mid_seq[0])
    while first_seq:
        first_data = first_seq.pop(0)
        if first_data in mid_seq:
            root = TreeNode(first_data)
            idx = mid_seq.index(first_data)
            mid_left = mid_seq[0 : idx]
            mid_right = mid_seq[idx + 1 : ]
            root.left = restore_btree(mid_left) # 根据中序左子集创建 左子树
            root.right = restore_btree(mid_right) # 根据中序右子集创建 右子树
            return root


if __name__ == '__main__':
    first_seq = list("abcdefghi")
    mid_seq = list("bcaedghfi")
    root = restore_btree(mid_seq)

    first_travel(root)
  • 先序遍历统计二叉树叶子节点数;
def count_leaf(root):
    global num
    if root is None:
        return num
    elif root.left is None and root.right is None:
        num += 1
        return num

    if root.left:
        count_leaf(root.left)
    if root.right:
        count_leaf(root.right)

    return num


num = 0
count_leaf(root)
print("total leafs:", num)

 

构建链式二叉树

根据列表数据,构建链式二叉树;
[“a”, “b”, “d”, None, “f”, None, None, None, “c”, “e”, None, None, None]

class Node:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right


def create_bt(): # 创建二叉树
    global alist
    # 先序 递归 创建二叉树
    if alist:
    	data = alist.pop(0)
    	if data is None:
        	return root  # 空树
    else:
    	return None
    
    # 先创建根节点
    root = Node(data)
    # 创建左子树
    root.left = create_bt()
    # 最后创建右子树
    root.right = create_bt()

    return root


# 先序遍历
def first_travel(root):
    if root is None:
        return
    print(root.data)
    first_travel(root.left)
    first_travel(root.right)


if __name__ == '__main__':
    alist = ["a", "b", "d", None, "f", None, None, None, "c", "e", None, None, None]

    root = create_bt()
    first_travel(root)

创建的二叉树如图:

python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_16


先序遍历:a b d f c e

中序遍历:d,f,b,a,e,c

后序遍历:f,d,b,e,c,a

 

二叉排序树

Binary Search Tree,也叫二叉搜索树;没有重复值;

  • 左子节点的值 小于根节点,根节点值小于右节点;
  • 左子树中同样满足;
  • 右子树中同样满足;
  • 二叉搜索树 查找的时间复杂度为python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_数据结构_17
  • 假设树为完全二叉树,且查找python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子树_18次才找到目标,则相当于查的树深度为python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子树_18,则树节点数n满足python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_python_20python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_二叉树_21
  • 通过取以2为底的对数得到python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子树_22
  • 所以时间复杂度python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_23;也可以每次除2的方式理解,python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_24,即到达叶子节点;
  • 当二叉搜索树为倾斜树时,最坏时间复杂度为python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_python_25
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_二叉树_26

  • 所以二叉搜索树的性能与树高有关;
# 定义二叉排序树的节点
class TreeNode:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
 
# 构建二叉排序树
def insert_node(root, val):
    if root is None:
        return TreeNode(val)
    if val > root.val:
        root.right = insert_node(root.right, val)
    else:
        root.left = insert_node(root.left, val)
    return root
 
# 中序遍历二叉排序树(升序)
def in_order_traversal(root):
    if root is None:
        return []
    res = []
    res.extend(in_order_traversal(root.left))
    res.append(root.val)
    res.extend(in_order_traversal(root.right))
    return res
 
# 示例:构建一个二叉排序树
root = None
nums = [5, 2, 7, 1, 3, 6, 8, 4, 9]
for num in nums:
    root = insert_node(root, num)
 
# 输出二叉排序树的中序遍历结果
print(in_order_traversal(root))

 

平衡树

为了保证二叉排序树的中序遍历有序性,会导致树的不平衡;
为了提高查询性能,需要避免极度不平衡的树(倾斜树);

  • 理想平衡,左右子树的高度一样,需要耗费时间调整平衡;
  • 适度平衡,左右子树的高度大致一样;
  • AVL平衡二叉(查找)树,是一种特殊的二叉排序树;左右子树的高度差绝对值python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子树_27;左右子树也是平衡二叉树;
  • 平衡因子,左子树的高度减去右子树的高度;
  • 当插入数据导致不平衡时,只需从下向上依次调整最小的不平衡子树;
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_数据结构_28

  • 调整平衡方法,LL型、RR型、LR型、RL型;
  • LL型,当一个节点的平衡因子不在(-1,0, 1)时,即出现了不平衡,向其左子树方向(高度大的一侧)走两次,以中间节点为轴,以走过的边为左右子树的指针,顺时针旋转,断开的节点连接到初始的树根节点。

  • LR型,向高度大的左子树走一步L,向导致不平衡的右子树走一步R,(L边先断开)以LR中间的节点逆时针旋转(断开的节点连接初始的根),旋转后的树根挂载到L边;然后再LL调整。(LR->逆时针->LL->顺时针
  • RL型,(RL->顺时针->RR-逆时针
  • 平衡二叉树插入

    如下完成平衡二叉树的插入,思路:从下往上调整最小不平衡子树

    将最小不平衡子树调整为:
  • 平衡二叉树删除
    1.如果平衡二叉树为空,则返回;
    2.如果T.data==x,查找成功,若T节点有一个孩子为空,删除T节点后,其孩子代替其位置;若T有两个孩子,则删除T后,令T的直接前驱(直接后驱)代替它,并删除其直接前驱(直接后驱);直接前驱-左子树的最右叶子节点;直接后驱-右子树的最左叶子节点
    3.如果T.data<x 或者>x 则到对应的右/左子树中删除;
    4.调整平衡;
    删除案例1:直接删除该节点,调整平衡;

    最后树变为:


    删除案例2

    删除后右子树的结果:
  • 树堆Treap,特殊的二叉排序树;
  • 红黑树(理解),二叉排序树中左右子树高度差不超2倍;
  • 根黑,叶子(Null)黑,内部节点为黑或者红色;红父必黑孩子;任意节点到所有叶子的路径上黑节点数相同;
  • 构建红黑树练习
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_python_29

  • 思路:根为黑,父节点为黑,直接插入;父节(p)点为红,且父节点的兄弟(u)为红,即双红修正–将p&u变黑,p&u的父节点(g)变红,并将g看作是新插入的节点,继续向上处理;若p红u黑,查看g->x路径执行LL/RR/LR/RL旋转,新根变黑,两个孩子变红;
    1.插入12,为黑色根;
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_30

  • 2.插入16,根黑,则直接插入(红节点);
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_python_31

  • 3.根黑,直接插入2(红色);
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_32

  • 4.输入30,p&u 为双红,双红修正
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_33

  • 5.输入28,非双红要旋转,新根变黑,双子变红;
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_34

  • 6.插入20, 双红修正,p&u变黑,g变红,并将g作为一个新插入节点,继续向上处理;父节点为黑,则直接插入g;
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子树_35

  • 7.插入60、29,父节点为黑,直接插入;
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子树_36

  • 8.插入85,双红修正;
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_python_37


  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子树_38

  • 最终结果:
  • python数据结构与算法分析第3版在线 数据结构与算法pythonpdf_子节点_39