数据结构与算法【Python实现】(六)二叉树、二叉搜索树

一、树

 树是一种可以递归定义的数据结构。树是由n个节点组成的结合:

如果n=0,是一颗空树;如果n>0,那么一定存在一个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。

树的实例:模拟文件系统

class Node:
    def __init__(self, name, type="dir"):
        self.name = name
        self.type = type  #"dir" or "file"
        self.children = []
        self.parent = None
    def __repr__(self):
        return self.name

class FileSystemTree:
    def __init__(self):
        self.root = Node("/")  #根目录
        self.now = self.root  #当前文件夹

    def mkdir(self, name):
        #name 以 / 结尾
        if name[-1] != "/":
            name += "/"
        node = Node(name)
        self.now.children.append(node)
        node.parent = self.now

    def ls(self):  #展示当前目录下的所有目录
        return self.now.children

    def cd(self, name):  #转换目录
        if name[-1] != "/":
            name += "/"
        if name == "../":
            self.now =self.now.parent
            return
        for child in self.now.children:
            if child.name == name:
                self.now = child
                return
        else:
            raise ValueError("invaild dir")


tree = FileSystemTree()
tree.mkdir("var/")
tree.mkdir("bin/")
tree.mkdir("user/")

tree.cd("user/")
tree.mkdir("Python")
tree.cd("../")
print(tree.ls())

二、二叉树

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

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

1、二叉树的遍历

DLR前序遍历:EACBDGF

LDR中序遍历:ABCDEGF

LRD后序遍历:BDCAFGE

层次遍历:EAGCFBD(用队列写)

给定一个序列的两种遍历方式,就可以确定这棵树

 前序遍历

#前序遍历
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=",")

层次遍历

#层次遍历
from collections import deque

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)

三、二叉搜索树

二叉搜索树是一棵二叉树且满足性质:设x是二叉树的一个节点,如果y是x左子树的一个节点,那么y.key <= x.key;如果y是x右子树的一个节点,那么y.key >= x.key

二叉搜索树的操作:查询、插入、删除

1、二叉树的插入

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

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

    #递归写法
    def insert(self,node,val):
        if not node:  #如果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:  #如果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 #如果等于直接返回

二叉搜索树的中序遍历一定是按升序排好序的。

2、二叉树的查询

# 递归写法
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:  #等于val  找到
        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

3、二叉树的删除

(1)如果要删除的节点是叶子节点:直接删除

(2)如果要删除的节点只有一个孩子:将此节点的父亲与孩子连接,然后删除该节点

(3)如果要删除的节点有两个孩子:将其右子树的最小节点(往左走到头)(该节点最多有一个右孩子)删除,并替换当前节点。/ 或寻找其左子树的最大节点(往右走到头)。

# 情况1 node是叶子节点 直接删掉
def __remove_node_1(self,node):
    if not node.parent:  #如果父节点为空 只有根节点的父节点为空,即如果是根节点
        self.root = None
    if node == node.parent.lchild:  #如果node是父亲的左孩子
        node.parent.lchild = None
        node.parent = None
    else:  #node是父亲的右孩子
        node.parent.rchild = None
        node.parent = None


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

# 情况2.2 node只有一个右孩子
def __remove_node_22(self, 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)
        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  #把找到的节点数据替换给要删除的node节点
            #再删除被替换的min_node节点
            if min_node.rchild:  #如果只有有右孩子
                self.__remove_node_22(min_node)
            else: #如果是叶子节点
                self.__remove_node_1(min_node)

四、二叉搜索树的效率

平均情况下,二叉搜索树进行搜索的时间复杂度为O(lgn)

最坏情况下,二叉搜索树可能非常偏斜,如图,变成了一个列表

解决方案:随机化插入 / AVL树

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值