Python之二叉树

本文回顾了队列和堆的基本概念,重点讲解了二叉树的定义、类型(满二叉树、完全二叉树、平衡二叉树)以及四种遍历方式。在Python中,介绍了使用二叉树解决LeetCode上的问题,如验证二叉搜索树和层次遍历等。

一、复习

1.1、队列与堆

队列的操作比较讨巧,查找最大值或最小值的效率也较高,堆用于查最大值也是效率很高

主要应掌握出队入队,以及出堆,Python中的deque模块与heapq模块能够分别建立队列和堆

二、二叉树

2.1、定义

  • 一棵深度为k,且有2^k-1个节点的二叉树,称为满二叉树。这种树的特点是每一层上的节点数都是最大节点数。而在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树。具有n个节点的完全二叉树的深度为floor(log2n)+1。深度为k的完全二叉树,至少有2k-1个叶子节点,至多有2k-1个节点。

  • 二叉树是递归定义的,其结点有左右子树之分,逻辑上二叉树有五种基本形态:
    (1)空二叉树——如图(a);
    (2)只有一个根结点的二叉树——如图(b);
    (3)只有左子树——如图©;
    (4)只有右子树——如图(d);
    (5)完全二叉树——如图(e)。

在这里插入图片描述

(1)完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
(2)满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
(3)平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

2.2、遍历

  1. 前序遍历:按照根节点->左子树->右子树的顺序访问二叉树
  2. 中序遍历:按照左子树->根节点->右子树的顺序访问
  3. 后序遍历:按照左子树->右子树->根节点的顺序访问
  4. 层次遍历:按照每一层,从上到下,从左到右顺序访问

2.3、Python中应用

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class Node(object):

    def __init__(self,val):
        self.val = val
        self.left = None
        self.right = None

class Tree(object):

    def __init__(self):
        self.root = None

    def add(self,val):
        node=Node(val)
        if self.root is None:
            self.root = node
        else:
            q = [self.root]

            while True:
                pop_node = q.pop(0)
                if pop_node.left is None:
                    pop_node.left = node
                    return
                elif pop_node.right is None:
                    pop_node.right = node
                    return
                else:
                    q.append(pop_node.left)
                    q.append(pop_node.right)

    def preorder(self,root):  #前序遍历
        if root is None:
            return []
        result_0 = [root.val]
        leftval_0 = self.preorder(root.left)
        rightval_0 = self.preorder(root.right)
        return result_0 + leftval_0 + rightval_0

    def inorder(self,root):  # 中序遍历
        if root is None:
            return []
        result_1 = [root.val]
        leftval_1 = self.inorder(root.left)
        rightval_1 = self.inorder(root.right)
        return leftval_1 + result_1 + rightval_1

    def postorder(self,root): # 后序遍历
        if root is None:
            return []
        result_2 = [root.val]
        leftval_2 = self.postorder(root.left)
        rightval_2 = self.postorder(root.right)
        return leftval_2 + rightval_2 + result_2

    def traverse(self):  #普通的层次遍历 只是简单按顺序把结点列出来,没有分层
        if self.root is None:
            return []
        q = [self.root]
        i = 0
        while True:
        	q = [self.root]
        	res = [self.root.item]
        	while q != []:
            	pop_node = q.pop(0)
            	if pop_node.child1 is not None:
                	q.append(pop_node.child1)
                	res.append(pop_node.child1.item)

            	if pop_node.child2 is not None:
                	q.append(pop_node.child2)
                	res.append(pop_node.child2.item)
        	return res


t = Tree()

for m in range(10):
    t.add(m)

print t.traverse()
print t.preorder(t.root)
print t.inorder(t.root)
print t.postorder(t.root)

2.3.1、leetcode

98. Validate Binary Search Tree
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        
        list = self.inorder(root)
        
        for i in range(len(list)-1):   # 直接利用中序遍历得到的序列进行比对(因为该题二叉树的中序遍历序列一定为递增数列)
            if list[i]>=list[i+1]:
                return False
        return True
        
        
    def inorder(self,root):  # 中序遍历
        if root is None:
            return []
        result_1 = [root.val]
        leftval_1 = self.inorder(root.left)
        rightval_1 = self.inorder(root.right)
        return leftval_1 + result_1 + rightval_1
class Solution(object):
    def __init__(self):
        self.flag = True
    
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        self.judge(root,None,None)
        return self.flag
        
        
    def judge(self,root,min,max):   #这个很牛逼,我并不能看得很懂哈哈哈,应该是从中序遍历推广来的
        if root is None:
            return
        if (min != None and root.val<=min.val) or (max != None and root.val >= max.val):
            self.flag = False
            return
            
        self.judge(root.left,min,root)     # 对于左子树而言,最大值是左子树的根节点root
        self.judge(root.right,root,max)  #对于右子树而言,最小值是右子树的根节点root
class Solution(object):
    
    def __init__(self):
        import sys
        self.last = -sys.maxint  #python中最小的数(本来是最大,加了个负号就变成了最小)
    
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        
        if root is None:
            return True
        
        if self.isValidBST(root.left) == False:    # 一边进行中序遍历,一边进行比较(中序遍历是递增数列),所以要设置全局变量,每遍历得到一个值便比较一次
            return False
        if root.val <= self.last:
            return False
        self.last = root.val
        if self.isValidBST(root.right) == False:
            return False
        return True
class Solution(object):

    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        stack = []
        p = root
        pre = None
        
        while p!=None or stack!=[]:  #迭代的思路,用栈压入左子树,然后取出判断
            while p!=None :              # 据大佬说跟中序遍历的思路是共通的
                stack.append(p)
                p = p.left
                
            t = stack.pop()
        
            if pre!=None and t.val<=pre.val:    # 利用中序遍历序列的递增关系,一直中序遍历一直判断
                return False
            pre = t
            p = t.right
        
        return True
  • 结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

102. Binary Tree Level Order Traversal
class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        
        q = [(root,0)]   # 右边存的是层数,根节点是第0层
        res = [[]]    # 初始化res
        
        if root == None :
            return []
        
        k = 0   #   k为层数
        
        
        while q:  #一边不断按顺序将结点输入队列q,然后再从队头将各个结点出队
            
            t = q.pop(0)
            res[t[1]].append(t[0].val)  # 按层数将对应值输入到res列表中
            
            if t[0].left :
                q.append((t[0].left,t[1]+1))     #  输入队列q时,记得把层数+1
                
            if t[0].right :
                q.append((t[0].right,t[1]+1))    #  输入队列q时,记得把层数+1
                
            if t[0].left or t[0].right :    # 有子节点,说明有下一层,层数要加一,同时res也要插入新的层
                if k == t[1]:     #如果标示的层数与从队列取出的结点所指示的层数相同,则说明确实有下一层
                    res.append([])
                    k += 1
        
        return res
  • 结果
    在这里插入图片描述
107. Binary Tree Level Order Traversal II
class Solution(object):
    def levelOrderBottom(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        
        q = [(root,-1)]   #与上题不同,这里根节点是-1,标示res中的末尾
        res = [[]]
        
        if root == None :
            return []
        
        k = -1   # 层数的反序
        
        
        while q:  #与上题不同,这里不断在res的头部不断插入新的层数,因为是反序,所以不再从res的尾部插入新层
            
            t = q.pop(0)
            res[t[1]].append(t[0].val)
            
            
            if t[0].left :
                q.append((t[0].left,t[1]-1))  # 与上题相反,这里-1
                
            if t[0].right :
                q.append((t[0].right,t[1]-1))   # 与上题相反,这里-1
                
            if t[0].left or t[0].right :
                if k == t[1]:
                    res.insert(0,[])     # res的头部插入新层
                    k -= 1    
                    
        return res
  • 结果

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值