剑指offer系列之树(p55-58)

34 篇文章 0 订阅
15 篇文章 0 订阅

55. 二叉树的下一个结点

题目描述

知识点:二叉树
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

思路

考虑如下几种情况:

  • 空树直接返回NULL
  • 若当前结点有右孩子,那么下一个结点必然为右孩子最左结点,所谓最左结点就是沿着右孩子的左指针一直向下遍历直到到达最下面一个结点;

若当前孩子没有右结点,则只能向上遍历。

  • 首先判断当前结点的父节点是否为空,若为空则直接返回NULL;否则判断当前结点是否是其父结点的左孩子,如果是则直接返回父结点;如果不是则把父节点赋给当前结点。重复执行这一步。
# -*- coding:utf-8 -*-
# class TreeLinkNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#         self.next = None
class Solution:
    def GetNext(self, pNode):
        # write code here
        if pNode == None:
            return pNode
        # 右结点不为空,则必定时右结点的最左的孩子
        if pNode.right:
            pNode = pNode.right
            while pNode.left:
                pNode = pNode.left
            return pNode
        # 右结点为空,往父节点遍历
        while pNode.next:
            parent = pNode.next
            # pNode是父节点的左孩子
            if parent.left == pNode:
                return parent
            pNode = pNode.next
        return None

56. 对称的二叉树

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

思路

思路一:递归法

如果一个二叉树是对称的,那么它的左右子树必然也是对称的。
下面需要对左右子树进行比较:

  • 如果左右子树都为空,则直接返回True
  • 如果仅有一个为空,则直接返回False
  • 判断左右子树根结点的值是否相等,如果相等继续比较left.left & right.right 以及 left.right & right.left .
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def isSymmetrical(self, pRoot):
        # write code here
        if pRoot == None:
            return True
        return self.comRoot(pRoot.left, pRoot.right)
    
    def comRoot(self, left, right):
        # 左右结点都为空
        if left == None and right == None:
            return True
        # 左右结点仅有一个为空
        elif left == None or right == None:
            return False
        # 左右结点都不为空
        else:
            return left.val == right.val and self.comRoot(left.left, right.right) and self.comRoot(left.right, right.left)
思路二:DFS

使用栈来存储成对的结点,出栈也是成对的出。所谓成对,其实就是必须满足相等条件的两个结点。深度遍历的思想也体现在了栈中。
出栈时:

  • 如果两个结点都为空,则继续进行;
  • 如果仅有一个结点为空,则直接返回False
  • 如果两个都不为空且值不相等,直接返回False.

入栈时也是成对的入栈:

确定入栈顺序,left.left,right.right,left.right,right.left

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def isSymmetrical(self, pRoot):
        # write code here
        stack = []
        if not pRoot:
            return True
        stack.append(pRoot.left)
        stack.append(pRoot.right)
        while len(stack) > 0:
            left = stack.pop(-1)
            right = stack.pop(-1)
            if left == None and right == None:
                continue
            if left == None or right == None:
                return False
            if left.val != right.val:
                return False
            stack.append(left.left)
            stack.append(right.right)
            stack.append(left.right)
            stack.append(right.left)
        return True
思路三:BFS

思路三与思路二基本相同,唯一区别在于使用了队列存储,这也是体现了BFS的思想。类似于层次遍历。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def isSymmetrical(self, pRoot):
        # write code here
        queue = []
        if not pRoot:
            return True
        queue.append(pRoot.left)
        queue.append(pRoot.right)
        while len(queue) > 0:
            left = queue.pop(0)
            right = queue.pop(0)
            if left == None and right == None:
                continue
            if left == None or right == None:
                return False
            if left.val != right.val:
                return False
            queue.append(left.left)
            queue.append(right.right)
            queue.append(left.right)
            queue.append(right.left)
        return True

57. 按之字形打印二叉树

题目描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

思路一:层次遍历

普通的层次遍历算法,只不过对于偶数层的遍历结果先做一个反转操作再加入到结果中。该方法时间复杂度为 O ( n ) O(n) O(n)[反转操作和for循环的复杂度都是 O ( l e n ( q u e q u e ) ) O(len(queque)) O(len(queque))],空间复杂度为 O ( w ) , w O(w), w O(w),w为树的宽度。但是对于海量数据来说,多做一个反转操作并不是一个好的选择。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Print(self, pRoot):
        # write code here
        res = []
        if pRoot == None:
            return res
        queue = [pRoot]
        while len(queue) > 0:
            line = []
            for i in range(len(queue)):
                tmp = queue.pop(0)
                line.append(tmp.val)
                if tmp.left:
                    queue.append(tmp.left)
                if tmp.right:
                    queue.append(tmp.right)
            if row % 2 == 0:
                line = line[::-1]
            res.append(line)
        return res
思路二:基于双栈的层次遍历

还是层次遍历的方法,不过与传统的层次遍历仅仅使用一个队列不同,这次需要借助两个栈,一个栈用于存储奇数层的节点,另一个用于存储偶数层的节点。奇数栈入栈顺序为先右后左,出栈时就为从左到右了;偶数栈入栈顺序为先左后右,出栈时为先右后左。该方法时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( w ) , w O(w), w O(w),w为树的宽度。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Print(self, pRoot):
        # write code here
        res = []
        if pRoot == None:
            return res
        # s_odd 存储奇数行结点,s_even存储偶数行节点
        s_odd, s_even = [pRoot], []
        while len(s_odd) > 0 or len(s_even) > 0:
            # 处理奇数层
            line = []
            while len(s_odd) > 0:
                for i in range(len(s_odd)):
                    tmp = s_odd.pop(-1)
                    line.append(tmp.val)
                    # 偶数层入栈时先左后右,出栈时先右后左
                    if tmp.left:
                        s_even.append(tmp.left)
                    if tmp.right:
                        s_even.append(tmp.right)
            if len(line) > 0:
                res.append(line)
            # 处理偶数层
            line = []
            while len(s_even) > 0:
                for i in range(len(s_even)):
                    tmp = s_even.pop(-1)
                    line.append(tmp.val)
                    # 奇数层入栈先右后左,出栈时先左后右
                    if tmp.right:
                        s_odd.append(tmp.right)
                    if tmp.left:
                        s_odd.append(tmp.left)
            if len(line) > 0:
                res.append(line)
        return res      

58. 把二叉树打印成多行

题目描述

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

思路

思路一:层次遍历

用一个队列存储每次即将要遍历的那一层节点,每次遍历之前,队列中所存储的元素个数刚好是这一层节点的总数。时间复杂度位 O ( n ) O(n) O(n),空间复杂度为主要体现在队列的使用上面,最大不会超过 O ( n ) O(n) O(n),与二叉树的宽度直接相关。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        # write code here
        res = []
        if pRoot == None:
            return res
        queue = [pRoot]
        while len(queue) > 0:
            line = []
            for i in range(len(queue)):
                tmp = queue.pop(0)
                line.append(tmp.val)
                if tmp.left != None:
                    queue.append(tmp.left)
                if tmp.right != None:
                    queue.append(tmp.right)
            res.append(line)
        return res       
思路二:递归法

该方法来源于牛客网该题的讨论。函数主体其实是一个先序遍历,有几个细节需要注意,一个是利用传入深度来控制分层信息;二是只有当传入的节点的深度小于存储结果的列表长度小时才开辟新的空间,这是为了防止重复开辟新空间,只有当前层第一个被遍历到的节点才需要开辟空间。该方法时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( h ) O(h) O(h), h h h为树的深度.

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        # write code here
        res = []
        
        def Depth(pRoot, depth, res):
            if not pRoot:
                return 
            if depth > len(res):
                res.append([])
            res[depth - 1].append(pRoot.val)
            
            Depth(pRoot.left, depth + 1, res)
            Depth(pRoot.right, depth + 1, res)
            
        Depth(pRoot, 1, res)
        return res      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值