【数据结构】06-二叉树

本文介绍了优先队列的概念和基于线性表的实现,强调了完全二叉树的结构在优先队列中的作用。讨论了堆的性质,包括堆序和如何保持堆的结构。并详细讲解了堆在优先队列中的应用,包括插入元素的上筛操作和弹出元素的下筛操作,以及堆排序的原理。
摘要由CSDN通过智能技术生成
from queue import SQueue as queue
from list_stack import Stack as stack
import time
class BinTNode():
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

def count_BinTNode(t):
    if t is None:
        return 0
    else:
        total = 1 + count_BinTNode(t.left) + count_BinTNode(t.right)
    return total

total = 0
def count_BinTNode2(t):
    if t is None:  # 边界条件
        return
    else:
        global total
        total += 1
        count_BinTNode2(t.left)
        count_BinTNode2(t.right)

def sum_BinTNode(t):
    if t is None:
        return 0
    else:
        return t.data + sum_BinTNode(t.letf) + sum_BinTNode(t.right)

def pre_order(t, op):
    if t is None:
        return
    op(t.data, end=' ')
    pre_order(t.left, op)
    pre_order(t.right, op)
    return

def mid_order(t, op):
    if t is None:
        return
    mid_order(t.left, op)  # 处理左子树
    op(t.data, end=' ')  # 处理自己
    mid_order(t.right, op)  # 处理右子树
    return

def last_order(t, op):
    if t is None:
        return
    last_order(t.left, op)
    last_order(t.right, op)
    op(t.data, end=' ')
    return

def level_order(t, op):
    if t is None:
        return
    q = queue()
    q.enqueue(t)
    while not q.is_empty():
        temp = q.dequeue()
        op(temp.data, end=' ')
        if temp.left is not None:
            q.enqueue(temp.left)
        if temp.right is not None:
            q.enqueue(temp.right)
    return

def preorder_elements(t):
    s= stack()
    s.push(t)
    while not s.is_empty():
        t = s.pop()
        while t is not None:
            yield t.data
            s.push(t.right)
            t = t.left

def preorder_nonrec(t, op):
    s = stack()
    s.push(t)  # 开头
    while not s.is_empty():  # 非空栈
        t = s.pop()
        while t is not None:
            op(t.data, end=' ')
            s.push(t.right)  # 先进后出
            t = t.left

def midorder_nonrec(t, op):
    """见左捣底"""
    """回退访右"""
    s = stack()
    while t is not None or not s.is_empty():
        while t is not None:
            s.push(t)
            t = t.left

        node = s.pop()
        op(node.data, end=' ')

        if node.right is not None:
            t = node.right
        else:
            t = None

def postorder_nonrec(t, op):
    """见左捣底(末端可右)"""
    """先右后根"""
    s = stack()

    while t is not None or not s.is_empty():  # 第一个条件只在第一次循环必要
        while t is not None:  # 从本节点到偏左子孙节点入栈(如果某节点左节点为空,只能入栈右节点)
            s.push(t)  # 0-1-3
            t = t.left if t.left is not None else t.right

        t = s.pop()  # 处理栈顶节点
        op(t.data, end=' ')

        if not s.is_empty() and s.top().left == t:  # 不判空最后一步top会出错
            t = s.top().right  # 已经处理过左节点,转到右节点
        else:
            t = None  # 退出循环,下次弹出父节点

root = BinTNode(0, BinTNode(1, BinTNode(3, BinTNode(7), BinTNode(8)),
                BinTNode(4, BinTNode(9), None)), BinTNode(2, BinTNode(5),
                BinTNode(6)))

print('深度优先:', end='')
level_order(root, print)
print('')
print('先序遍历:', end='')
pre_order(root, print)
print('')
print('先序遍历:', end='')
preorder_nonrec(root, print)
print('')
print('中序遍历:', end='')
mid_order(root, print)
print('')
print('中序遍历:', end='')
midorder_nonrec(root, print)
print('')
print('后序遍历:', end='')
last_order(root, print)
print('')
print('后序遍历:', end='')
postorder_nonrec(root, print)
print('')

输出:
深度优先:0 1 2 3 4 5 6 7 8 9 
先序遍历:0 1 3 7 8 4 9 2 5 6 
先序遍历:0 1 3 7 8 4 9 2 5 6 
中序遍历:7 3 8 1 9 4 0 5 2 6 
中序遍历:7 3 8 1 9 4 0 5 2 6 
后序遍历:7 8 3 9 4 1 5 6 2 0 
后序遍历:7 8 3 9 4 1 5 6 2 0 

6.3 优先队列

6.3.1 概念

在这里插入图片描述
问题:完全二叉树:为什么要求在左呢?:
得益于完全二叉树的结构,使得父子节点具有确定的关系,便于和线性表一一对应。
在这里插入图片描述

6.3.2 基于线性表的实现

方案一:插入时就排序,这样入队O(N),出队O(1);
方案二:出队时排序,入队O(1),出队O(N)。当然方案二可以配合栈将最优先的元素放到栈里,这样每次从栈中访问。
基于线性表实现的考虑
1、首先确定要插入的位置,进行下标合法性检查,然后调用insert插入;
2、初始化参数拷贝,避免共享;
3、位置确定过程:初始化为由大到小,从最后一个元素开始逐个检查。

6.3.3 树形结构和堆

堆与性质:
结构上,堆由完全二叉树实现,但是数据的存储要满足特殊的堆序:根结点的优先级必须不小于子结点的优先级。所以优先级最高的就是根结点。另外,不同路径上的元素不交叉考虑。这样就构造出了大顶堆,与之对应的是小顶堆。
在这里插入图片描述
堆的四个性质:
1、在堆的最后面增加元素,得到的仍然是完全二叉树,但不一定满足堆序。
2、弹出顶端元素,形成两个子堆
3、在两个子堆上增加根结点构成完全二叉树,但不一定满足堆序。
4、去掉最后一个元素,仍然是堆。
堆与优先队列
在堆中可以用O(1)的时间访问最优先元素,但是要解决两个问题:
1、插入新元素后堆的重构
2、弹出元素后,子堆的融合

6.3.4 优先队列的堆实现

插入元素与上筛(i-1//2):
先将元素插入到堆最后一个位置,然后依次和父结点比较,直到优先级较小。
弹出元素与下筛:
弹出元素后,将最后一个元素移到根结点,然后逐个和子结点比较,直到优先级较高。
一行两种操作的时间复杂度不会超过log n。

6.3.5 堆排序

# 基于堆实现优先队列
class PrioQueueError(ValueError):
    pass

class PrioQue():
    def __init__(self, elist):
        """设置小优先级高"""
        self._elements = list(elist)
        self._len = len(self._elements)
        if elist:
            self.buildheap()

    def buildheap(self):
        end = self._len
        for i in range(end//2-1, -1, -1):  # 中间的-1取不到,是左闭右开区间
            self.sift_down(self._elements[i], i, end)
        print('初始队列:', self._elements)

    def is_empty(self):
        return self._len == 0

    def enqueue(self, data):
        # 为第一次交换做准备
        self._elements.append(None)
        self._len += 1
        self.sift_up(data, self._len-1)
        return

    def sift_up(self, data, last_index):
        """要插入的元素和队列最后一个位置的索引"""
        """位置合法 + 数值合适"""
        base_pos, parent_pos = last_index, (last_index - 1) // 2
        while parent_pos >= 0 and data < self._elements[parent_pos]:
            self._elements[base_pos] = self._elements[parent_pos]
            base_pos, parent_pos = parent_pos, (parent_pos - 1) // 2
        self._elements[base_pos] = data
        return

    def dequeue(self):
        if self.is_empty():
            raise PrioQueueError
        first = self._elements[0]
        last = self._elements.pop()
        self._len -= 1
        if self._len > 0:
            self.sift_down(last, 0, self._len)
        return first

    def sift_down(self, data, begin, end):
        """begin可以视作根节点,从该处三角下筛"""
        """下筛的时候就不考虑原根节点了"""
        base_pos, target = begin, 2 * begin + 1
        while target < end:  # 确定最小的→交换
            if target + 1 < end and self._elements[target] > self._elements[1 + target]:
                target += 1
            if data < self._elements[target]:
                break  # 是三值中最小的,位置对
            else:
                self._elements[base_pos] = self._elements[target]
            base_pos, target = target, target * 2 + 1
        self._elements[base_pos] = data
        return

    def order(self):
        temp = [0]*self._len
        for i in range(self._len):
            temp[i] = self.dequeue()
        return temp

    def content(self):
        print(self._elements)

pq = PrioQue([5, 6, 9, 2, 4, 7])
pq.enqueue(10)
print("队列加10:  ", end='')
pq.content()
pq.dequeue()
print("dequeue后: ", end='')
pq.content()
print("排序后:    ", end='')
print(pq.order())


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值