python实现二叉树

python实现二叉树

二叉树的性质:

  • 树高 root 到 其他所有节点的最大距离 h
  • 最大节点数 2的h次方
  • 第i层中至多有2的i次方个节点
  • 高度为h 至多有 2的h-1次方-1个节点
抽象数据类型:
ADT BinTree:
    BinTree(self,data,left,right)  构造,创建一个新的二叉树
    is_empty(self) 空检测
    num_nodes(self) 节点总数
    data(self)
    left(self)
    right(self)
    set_left(self,btree)
    set_rigth(self,btree)
    traversal(self)
    forall(self,op)
class BinTNode(object):
    def __init__(self, dat, left=None, right=None):
        self.data = dat
        self.left = left
        self.right = right


def count_BinTNode(t):
    """统计节点数"""
    if t is None:
        return 0
    else:
        return 1 + count_BinTNode(t.left) + count_BinTNode(t.right)


def sum_BtinTNode(t):
    """计算所有数据的和"""
    if t is None:
        return 0
    else:
        return t.data + sum_BtinTNode(t.left) + sum_BtinTNode(t.right)


def preoder(t, proc):
    """深度先根遍历(递归)"""
    if t is None:
        return
    proc(t.data)
    preoder(t.left)
    preoder(t.right)

# 打印二叉树的节点
def print_BinTNodes(t):
    if t is None:
        print("^", end='')
        return
    print("(" + str(t.data), end='')
    print_BinTNodes(t.left)
    print_BinTNodes(t.right)
    print(')', end='')


# 这里用到了队列,用队列实现广度遍历
from 栈和队列.makequeue import SQueue


def levelorder(t, proc):
    qu = SQueue()
    qu.enqueue(t)
    while not qu.is_empty():
        n = qu.dequeue()
        if n is None:
            continue
        qu.enqueue(n.left)
        qu.enqueue(n.right)
        proc(n.data)

class BinTree(object):
    """二叉树类"""

    def __init__(self):
        self._root = None

    def is_empty(self):
        return self._root is None

    def leftchild(self):
        return self._root.left

    def rightchild(self):
        return self._root.right

    def set_root(self, rootnode):
        self._root = rootnode

    def set_left(self, leftchild):
        self._root.left = leftchild

    def set_right(self, rightchild):
        self._root.right = rightchild

非递归的先根遍历要点:

  • 深度遍历
  • 遇到节点,访问节点,下一步沿着节点的left下行
  • 节点的right没有被访问,但是需要记录,入栈
  • 遇到空树时回溯,取出栈中保存的right,然后想一颗完整的二叉树一样遍历它
  • 遇到空树 意味着 一颗子树遍历完成
  • 如果这课子树是左子树,对应的右子树应该为栈顶元素
  • 如果这棵树是右子树,说明以他为右子树的大树已经完成遍历,下一步应该处理更上层的右子树

循环控制的条件:

  • 假设变量t一直取值为当前遍历子树的root,栈中保存的是前面记录的没有遍历的右子树.这样,只要当前树非空(当前选择的节点树需要被遍历)或者
    栈非空(还有子树没有被遍历),那么循环就应该继续
    循环体:
  • 先处理当前节点的数据,然后沿着树的左分支下行,一路上将面遇到的右节点压入到栈(这个部分 也需要一个循环),内部循环直到遇到空树的时候回溯,
    从栈中弹出一个元素(最近的右分支),进行新的不遍历
# 这里用到了栈结构
from 栈和队列.makestack import SStack_from_list


def preorder_nonrec(t, proc):
    s = SStack_from_list()
    # 循环条件一  当前树非空,或者栈中还有右子树
    while t is not None or not s.is_empty():
        while t is not None:  # 内部循环,确定沿左下行[只要当前节点非空就处理当前子树树的元素]
            proc(t.data)
            s.push(t.right)  # 遇到右子树压入栈
            t = t.left  # 处理完root 处理left<一路向左>
        t = s.pop()  # 当当前节点为空[左到头了] 从栈中拿出最近的一个右节点,继续处理


# 二叉树的迭代器  {先序遍历} 1.右节点入栈, 2.输出根节点 3.切换为左节点 4.左节点完毕 5.pop右节点 6.继续遍历
def preorder_elements(t):
    s = SStack_from_list()
    while t is not None or not s.is_empty():
        while t is not None:
            s.push(t.right)
            yield t.data
            t = t.left
        t = s.pop()


# 二叉树迭代器  {中序遍历}  1.左节点入栈 2.pop左节点 3.根切换为右节点 4.继续遍历
def midporder_element(t):
    s = SStack_from_list()
    while t is not None or not s.is_empty():  # 外层循环控制遍历的继续
        while t is not None:  # 内层循环控制一路向左
            s.push(t)  # 将当前节点入栈
            t = t.left  # 访问左节点  中根 左根右,所以需要先访问左节点 所以将所有的左节点都入栈
        if not s.is_empty():  # 当左节点入栈完毕,开始从栈中将最近的左节点弹出
            a = s.pop()
            yield a.data  # 输出最近做节点的数据
            t = a.right  # 然后将根切换问右节点,继续遍历


最难的后序遍历

  • 变量t在执行过程中要满足:
    • 栈中节点序列的左边是二叉树的已遍历的部分,右边是尚未遍历的部分
    • 如果t非空,其父节点就是栈顶节点
    • t 空时,栈顶就是应访问的节点

根据被访问的节点时其父节点的左子节点还是右子节点,来决定下一步应该怎么做

  • 如果是左子节点 : 就切换到右兄弟
  • 如果是右子节点 : 处理根节点,并强制退栈

内层循环的目标是找到下一个应该被访问到的节点,循环中用到一个条件表达式,要求在有左子树时持续左下行,没有左子树的
时候向右切换.该循环的结束说明栈顶点没有左右子树,应该被访问.如果外层循环的一次迭代不进入内层循环,说明栈顶点的左右子树都已经便利完毕,应该访问
栈顶点.
要点:

  • 内层循环为了找当前子树的最左最下节点,将其入栈后种终止
  • 如果被访问的节点是其父节点的左节点,那么就需要遍历它的右节点
  • 如果被处理的节点是其中父节点的右节点,设置t=None退栈,迫使外层循环的下次迭代访问更上层的节点
def postorder_nonrec(t, proc):
    s = SStack_from_list()
    while t is not None or not s.is_empty():
        while t is not None:
            s.push(t)  # 只要当前节点非空,就将当前节点压人栈
            t = t.left if t.left is not None else t.right  # 如果当前节点的左节点非空就衣裤向左,否则切换成为右节点
        t = s.pop()  # 将所有节点压入栈后,开始出栈
        proc(t.data)  # 不进入内层循环,需要访问栈顶节点
        if not s.is_empty() and s.top().left == t:
            # 栈非空,并且栈顶节点的左节点时当前节点,那么说明栈顶节点的右节点没有被遍历,需要切换为右节点
            t = s.top().right
        else:
            # 栈空了,或者没有右节点需要被遍历,那么就强制退栈
            t = None


# 生成器模式后续遍历
def posorder_elements(t):
    s = SStack_from_list()
    while t is not None or not s.is_empty():
        while t is not None:
            s.push(t)
            t = t.left if t.left is not None else t.right
        t = s.pop()
        yield t.data
        if not s.is_empty() and s.top().left == t:
            t = s.top().right
        else:
            t = None


if __name__ == '__main__':
    t = BinTNode(1, BinTNode(2, BinTNode(5), BinTNode(6)), BinTNode(3))
    # print_BinTNodes(t)
    # levelorder(t, print)
    # preorder_nonrec(t, lambda x: print(x, end=' '))
    res = preorder_elements(t)
    print(list(res))
    res = midporder_element(t)
    print(list(res))
    res = posorder_elements(t)
    print(list(res))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值