剑指offer小结第二波

二叉树专题系列

1. 镜像类

题目描述:

操作给定的二叉树,将其变换为源二叉树的镜像。

Ying的解法:

二叉树的组成是根节点和左右指针,因此解决二叉树问题,一般要先讨论根节点的是否为空,决定了树的存在性,往往也是需要作为一种特殊情况需要单独拿出来考虑;然后再对普通情况进行把握。

class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        # write code here
        root1 = root
        if root is None:
            return root
        else:
            root.left,root.right = root.right,root.left
        if root.left is not None:
            self.Mirror(root.left)
        if root.right is not None:
            self.Mirror(root.right)
        return root1

题目描述:

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

Ying的解法:

这道题和上道题看上去没有什么本质区别,对称二叉树和它的镜像二叉树是相同的,所以我一开始的想法是构建该树的镜像二叉树,然后通过任意一种遍历方式比较两棵树是否相同,但是具体操作发现,对于完全二叉树没有问题,但是无法通过所有的样例。即使同时考虑通过两种遍历方式(先序中序或者中序后序),也无法通过所有样例。考虑每个节点的值相同的情况。
因此,考虑换一种解题方法,运用递归的思想,发现,判断是否对称,一个二叉树需要满足左右子树关于中心线左右互换,即根节点的左孩子等于右孩子,左孩子的左孩子等于右孩子的右孩子,左孩子的右孩子等于右孩子的左孩子......
由此,得到如下代码:

class Solution:
    def isSame(self,left,right):
        #取非不能用!,而是not
        if not left or not right:
            if not left and not right:
                return True
            return False
        if left.val != right.val:
            return False
        r1 = self.isSame(left.left,right.right)
        r2 = self.isSame(left.right,right.left)
        if r1 and r2:
            return True
    
    def isSymmetrical(self, pRoot):
        # write code here
        if not pRoot:
            return True
        return self.isSame(pRoot.left,pRoot.right)

2. 层次遍历类

题目描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

Ying的解法:

这道题很简单,很基本的二叉树层次遍历问题。注意这道题要求的是打印节点,所以在提交函数中不需要写return(写也不错),而应该是print;此外,在遍历当前节点时,需要将其左右节点存储在一个队列中,队列的实现,python有天然的优势,通过列表结构的pop(0)方法实现,转化为只要列表不空,就打印列表元素的问题,以下是代码实现。

class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # write code here
        if root==None:
            return None
        tempQue = []
        node = root
        tempQue.append(node)
        while tempQue:
            node = tempQue.pop(0)
            print(node.val)
            if node.left:
                tempQue.append(node.left)
            if node.right:
                tempQue.append(node.right)

题目描述

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

Ying的解法:

与上一题相比,本题的题目要求仅多了一个每一层输出一行,我最开始的想法是每输出一层打印一个换行,但是题目要求是输出,要求返回二维数组,而不是打印。每一行相当于二维数组中的一维数组元素,同样的问题,如何区分一层结束?首先开辟一个空的二维数组和两个一维数组,一个存节点,一个存值,考虑加入标记'#'表示一行输出完毕,当遇到'#'时,一维节点数组弹出'#'的同时,将值列表添加到二位列表中,再清空,同时节点列表插入'#';区分当遇到最后一个'#'时,停止操作,返回二维列表。实现代码如下:

class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        # write code here
        if not pRoot:
            return []
        tempQue = []
        # 存放二维数组中的每个list元素
        vaList = []
        # 存放最后结果的二维数组
        resultList = []
        node = pRoot
        tempQue.append(node)
        tempQue.append('#')
        while tempQue:
            if tempQue[0]!='#':
                node = tempQue.pop(0)
                vaList.append(node.val)
                if node.left:
                    tempQue.append(node.left)
                if node.right:
                    tempQue.append(node.right)
            elif tempQue[0]=='#' and len(tempQue)!=1:
                tempQue.pop(0)
                resultList.append(vaList)
                vaList = []
                tempQue.append('#')
            elif tempQue[0]=='#' and len(tempQue)==1:
                tempQue.pop(0)
                resultList.append(vaList)
                return resultList

题目描述

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

Ying的解法:

这道题在上个题目的基础上要求奇数层从左到右打印,偶数行从右到左打印,我的第一想法是在上一题的代码基础上,用栈实现,想要寻求不同行之间一致的操作规律,结果把自己绕晕了。以下这种做法比较巧妙,通过维护两个列表,分别存储相邻两层之间的节点,当前节点的左孩子右孩子添加到另一个列表中,不停出栈,直到当前列表为空,再遍历另一个列表重复之前操作,只不过之前是先左后右,这次是先右后左。在此遍历过程中,两个列表始终有一个不空,直到都为空,返回二位列表。

class Solution:
    def Print(self, pRoot):
        # write code here
        if not pRoot:
            return []
        #两个列表存放节点元素
        list1 = []
        list2 = []
        # 存放二维数组中的每个list元素,节点的值
        vaList = []
        # 存放最后结果的二维数组
        resultList = []
        node = pRoot
        list1.append(node)
        while list1 or list2:
            while list1:
                node = list1.pop()
                vaList.append(node.val)
                if node.left:
                    list2.append(node.left)
                if node.right:
                    list2.append(node.right)
                if not list1:
                    resultList.append(vaList)
                    vaList = []
            while list2:
                node = list2.pop()
                vaList.append(node.val)
                if node.right:
                    list1.append(node.right)
                if node.left:
                    list1.append(node.left)
                if not list2:
                    resultList.append(vaList)
                    vaList = []
        return resultList

3. 深度问题类

题目描述:二叉树中和为某一值的路径

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

Ying的思路:

因为要输出的是一个二维列表,而且列表中元素的长度要逆序输出。即对于所有和为给定值的路径,先返回深度大的路径。这个代码自己没有写出来,是借鉴书上和他人做法的。比较巧妙的是对于不同的路径,始终维持的是一个一维列表,当还能继续搜索时(左右子节点不全为空),则继续搜索,但搜索完之后要返回上一步,寻找其他满足情况的路径。二叉树的问题,经常通过列举,把题意理解清楚之后,举例子走一遍,很有帮助。以下是代码:

import copy
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def getSum(self, array):
        sum = 0
        for i in array:
            sum += i
        return sum
    
    def Find(self, root, expectNumber, onePath, pathArray):
        if not root:
            return pathArray
        onePath.append(root.val)
        if self.getSum(onePath) == expectNumber and not root.left and not root.right:
            pathArray.append(copy.deepcopy(onePath))
        elif self.getSum(onePath) != expectNumber:
            if root.left:
                self.Find(root.left, expectNumber, onePath, pathArray)
                onePath.pop(-1)
            if root.right:
                self.Find(root.right, expectNumber, onePath, pathArray)
                onePath.pop(-1)
            # 感觉这样写有问题,如果已经到叶子节点,该路径值不满足怎么办
            # 其实是没有问题的,不满足条件的路径不做任何操作就可以了
            # 二维列表中的列表是动态更新的,维护的是一个,而不是每个以为列表都需要重新开辟,所以这里用的是深拷贝
    def FindPath(self, root, expectNumber):
        onePath = []
        pathArray = []
        self.Find(root, expectNumber, onePath, pathArray)
        return pathArray

题目描述:二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

Ying的思路:

二叉树的深度是由它从根节点开始到叶子节点的所有路径中最长的一条决定的。叶子节点的深度为1,空树的深度是0,其他二叉树的深度游它左右子树长度的最大值决定。左右子树分别有各自的左右子树,它的深度同样等于它的左右子树深度的最大值。以此递归下去,就可以求得二叉树的深度。代码如下所示:

class Solution:
    def TreeDepth(self, pRoot):
        # write code here
        if pRoot is None:
            return 0
        else:
            return max(self.TreeDepth(pRoot.left),self.TreeDepth(pRoot.right)) + 1 

题目描述:平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

Ying的思路:

平衡二叉树是指左右子树深度相差不超过1的二叉树。因此,直接的想法便是得到做右子树的深度,判断差的绝对值是否不大于1。接下来,就是如何计算左右子树的深度。这里需要列举几种情况进行讨论当左右子节点都存在时,和上个题一样,比较左右子树最大深度加1即可,但是若左子树或右子树不存在,那么比较过程的递归函数中node.left和node.right是不存在的,会报错,因此,这两种情况要分开逃理论一下,至此,得到如下代码:

class Solution:
    def getHight(self,node):
        if not node.left and not node.right:
            return 0
        if node.left or node.right:
            if node.left and node.right:
                return max(self.getHight(node.left),self.getHight(node.right))+1
            if node.left:
                return self.getHight(node.left)+1
            if node.right:
                return self.getHight(node.right)+1
    def IsBalanced_Solution(self, pRoot):
        # write code here
        if not pRoot:
            return True
        hLeft = 0
        hRight = 0
        if pRoot.left or pRoot.right:
            if pRoot.left:
                hLeft = self.getHight(pRoot.left)
            if pRoot.right:
                hRight = self.getHight(pRoot.right)
        if abs(hLeft-hRight)<=1:
            return True
        return False

==未完待续

作者原创,欢迎交流,如需转载请先联系作者。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容