第八课 数据结构-树与二搜索叉树

python数据结构与算法基础 第八课

tags:

  • python
  • 路飞学院

categories:

  • python
  • 数据结构
  • 二叉树
  • 二叉搜索树

第一节 树结构

1. 树的定义

  1. 树是一种数据结构,比如:目录结构。
  2. 树是一种可以递归定义的数据结构
  3. 树的定义:树是由n个节点组成的集合
    • 如果n=0,那这是一棵空树;
    • 如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是-棵树。

2. 树的相关概念

  1. 根节点、叶子节点
  2. 树的深度(高度)
  3. 节点的度。节点的分差数比如:F的度是三
  4. 树的度。所有节点的分差数最大的数叫数的度。
  5. 孩子节点/父节点
  6. 子树
    在这里插入图片描述

第二节 二叉树的介绍

1. 知识回顾-二叉树顺序储存方式

  1. 二叉树:度不超过2的树
  2. 每个节点最多有两个孩子节点
  3. 两个孩子节点被区分为左孩子节点和右孩子节点
  4. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
  5. 完全二叉树:叶子节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。(最后一层若有节点,必从左边开始排。图b,7号节点有值,六号节点没右孩子则不是完全二叉树)
  6. 二叉树的储存方式:
    • 链式存储方式(后面会讲到)
    • 顺序存储方式(用列表储存)
    • 在此堆排序中用顺序储存方式
  7. 二叉树(顺序储存方式)中最常见的操作:
    • 父亲i找孩子:左孩子2i+1, 右孩子2i +2
    • 孩子p找父亲:左右孩子(p-1)//2
      在这里插入图片描述

2.二叉树的链式储存方式

  1. 二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接。
  2. 二叉树的定义如下:
    在这里插入图片描述
class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None # 左孩子
        self.rchild = None  # 右孩子


a = BiTreeNode("A")
b = BiTreeNode("B")
c = BiTreeNode("C")
d = BiTreeNode("D")
e = BiTreeNode("E")
f = BiTreeNode("F")
g = BiTreeNode("G")

e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f

root = e
print(root.lchild.rchild.data)

root = e
print(root.lchild.rchild.data)

第三节 二叉树的遍历

1. 二叉树的遍历方式

  1. 前序遍历: EACBDGF
  2. 中序遍历: ABCDEGF
  3. 后序遍历: BDCAFGE
  4. 层次遍历: EAGCFBD (用队列实现,不光适用于二叉树。多叉树也可以)
  5. 可以通过上述前中,后中还原出树的结构。但是通过前后不能唯一还原出二叉树。(有兴趣可以中找资料了解一下)
from collections import deque


class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None # 左孩子
        self.rchild = None  # 右孩子


a = BiTreeNode("A")
b = BiTreeNode("B")
c = BiTreeNode("C")
d = BiTreeNode("D")
e = BiTreeNode("E")
f = BiTreeNode("F")
g = BiTreeNode("G")

e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f

root = e

# 前序遍历
def pre_order(root):
    if root:
        print(root.data, end=",")
        pre_order(root.lchild)
        pre_order(root.rchild)


# 中序遍历
def in_order(root):
    if root:
        in_order(root.lchild)
        print(root.data, end=",")
        in_order(root.rchild)


# 后序遍历
def post_order(root):
    if root:
        post_order(root.lchild)
        post_order(root.rchild)
        print(root.data, end=",")


# 层次遍历
def level_order(root):
    queue = deque()
    queue.append(root)
    while len(queue) > 0: # 只要队列不为空,一直访问
        node = queue.popleft()
        print(node.data, end=",")
        if node.lchild:
            queue.append(node.lchild)
        if node.rchild:
            queue.append(node.rchild)


pre_order(root)
print("")
in_order(root)
print("")
post_order(root)
print("")
level_order(root)

第四节 二叉树的实际应用-二叉搜索树

1. 二叉搜索树介绍

  1. 二叉搜索树是一颗二叉树且满足性质:设x是二叉树的一个节点。如果y是x左子树的
    一个节点,那么y.key≤x.key; 如果y是x右子树的一个节点,那么y.key ≥x.key。
  2. 二叉搜索树的操作:查询、插入、删除。
  3. 下面递归写法都要带node参数,因为node是用来递归的。

2. 二叉搜索树的插入

class BiTreeNode:
    # 二叉搜索树节点对象
    def __init__(self, data):
        self.data = data
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子
        self.parent = None  # 父节点


class BST:
    def __init__(self, li):
        self.root = None
        if li:
            for i in li:
                self.insert_no_rec(i)

    # 递归写法
    def insert(self, node, val):
        # 这里没有考虑等于的情况,可以统一归到左边或者右边。
        # 也可以给节点加个属性值。count计数它
        if not node:
            node = BiTreeNode(val)
        elif val < node.data:
            node.lchild = self.insert(node.lchild, val)
            node.lchild.parent = node
        elif val > node.data:
            node.rchild = self.insert(node.rchild, val)
            node.rchild.parent = node
        return node

    # 非递归写法
    def insert_no_rec(self, val):
        p = self.root
        # 空树特殊处理
        if not p:
            self.root = BiTreeNode(val)
            return
        while True:
            if val < p.data:
                if p.lchild:
                    p = p.lchild
                else:
                    p.lchild = BiTreeNode(val)
                    p.lchild.parent = p
                    return
            elif val > p.data:
                if p.rchild:
                    p = p.rchild
                else:
                    p.rchild = BiTreeNode(val)
                    p.rchild.parent = p
                    return
            else:
                return

    # 前序遍历
    def pre_order(self, root):
        if root:
            print(root.data, end=",")
            self.pre_order(root.lchild)
            self.pre_order(root.rchild)

    # 中序遍历
    def in_order(self, root):
        if root:
            self.in_order(root.lchild)
            print(root.data, end=",")
            self.in_order(root.rchild)

    # 后序遍历
    def post_order(self, root):
        if self.root:
            self.post_order(root.lchild)
            self.post_order(root.rchild)
            print(root.data, end=",")

    # 层次遍历
    def level_order(self, root):
        from collections import deque
        queue = deque()
        queue.append(root)
        while len(queue) > 0:  # 只要队列不为空,一直访问
            node = queue.popleft()
            print(node.data, end=",")
            if node.lchild:
                queue.append(node.lchild)
            if node.rchild:
                queue.append(node.rchild)


tree = BST([4, 6, 7, 9, 2, 1, 3, 5, 8])
# 中序遍历实际上是对二叉搜索树的排序
tree.in_order(tree.root)

3. 二叉搜索树的查询

# 二叉搜索树的查询-递归写法
    def query(self, node, val):
        if not node:
            return None
        if node.data < val:
            return self.query(node.rchild, val)
        elif node.data > val:
            return self.query(node.lchild, val)
        else:
            return node

    # 二叉搜索树的查询-非递归写法
    def query_no_rec(self, val):
        p = self.root
        while p:
            if p.data < val:
                p = p.rchild
            elif p.data > val:
                p = p.lchild
            else:
                return p
        return None

4. 二叉搜索树的删除

  1. 删除分三种情况
  2. 情况一:删除的节点是叶子节点。直接删除
  3. 情况二:要删除的节点是父节点,而且只有一个孩子。把节点删除,把他的孩子和他的父亲连起来。
  4. 情况三:要删除的节点有两个孩子。那么选左子树的最大元素或右子树的最小元素替换这个删除的节点即可。(我们采用右子树的最小节点)
    def __remove_node_1(self, node):
        # 情况1:node 是叶子节点
        if not node.parent:
            # 根节点
            self.root = None
        if node == node.parent.lchild:
            # 如果node是左孩子
            node.parent.lchild = None
            # node.parent = None
        else: # 右孩子
            node.parent.rchild = None
            # node.parent = None

    def __remove_node_21(self, node):
        # 情况2: node只有一个左孩子
        if not node.parent:
            self.root = node.lchild
            node.lchild.parent = None
        # 它是它父亲的左孩子
        elif node == node.parent.lchild:
            node.parent.lchild = node.lchild
            node.lchild.parent = node.parent
        # 它是它父亲的右孩子
        else:
            node.parent.rchild = node.lchild
            node.lchild.parent = node.parent

    def __remove_node_22(self, node):
        # 情况2: node只有一个右孩子
        if not node.parent:
            self.root = node.rchild
            node.rchild.parent = None
        # 它是它父亲的左孩子
        elif node == node.parent.lchild:
            node.parent.lchild = node.rchild
            node.rchild.parent = node.parent
        # 它是它父亲的右孩子
        else:
            node.parent.rchild = node.rchild
            node.rchild.parent = node.parent

    def delete(self, val):
        if self.root: # 不是空树
            node = self.query_no_rec(val) # 这里query_no_rec返回的是一个对象
            if not node:
                return False
            if not node.lchild and not node.rchild:
                self.__remove_node_1(node)
            elif not node.rchild: # 只有一个左孩子
                self.__remove_node_21(node)
            elif not node.lchild: # 只有一个右孩子
                self.__remove_node_22(node)
            else: # 两个孩子都有
                # 找到右子树的最小值
                min_node = node.rchild
                while min_node.lchild:
                    min_node = min_node.lchild
                node.data = min_node.data
                # 删除min_node
                if min_node.rchild:
                    self.__remove_node_22(min_node)
                else:
                    self.__remove_node_1(min_node)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值