劍指offer(python)--樹

重建二叉樹

題目描述
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。

思路:
  在二叉樹的前序遍歷序列中,第一個數字總是樹的根結點的值。但在中序遍歷序列中,根結點的值在序列的中間,左子樹的結點的值位於根結點的值的左邊,而右子樹的結點的值位於根結點的值的右邊。因此我們需要掃描中序遍歷序列,才能找到根結點的值。

如下圖所示,前序遍歷序列的第一個數字1就是根結點的值。掃描中序遍歷序列,就能確定根結點的值的位置。根據中序遍歷特點,在根結點的值1前面的3個數字都是左子樹結點的值,位於1後面的數字都是右子樹結點的值。

  同樣,在前序遍歷的序列中,根結點後面的3個數字就是3個左子樹結點的值,再後面的所有數字都是右子樹結點的值。這樣我們就在前序遍歷和中序遍歷兩個序列中,分別找到了左右子樹對應的子序列。

既然我們已經分別找到了左、右子樹的前序遍歷序列和中序遍歷序列,我們可以用同樣的方法分別去構建左右子樹。也就是說,接下來的事情可以用遞歸的方法去完成

問題:如何表示出來?https://blog.csdn.net/weixin_38339143/article/details/79897670

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
        
class Solution:
    # 返回構造的TreeNode根節點
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if len(pre)==0 or len(tin)==0:
            return None
        root_data = TreeNode(pre[0])#將根定義成節點的形式
        i=tin.index(pre[0])#i=tin.index(root_data.val)#尋找位置將左右子樹分開 #返回根節點的索引
        root_data.left = self.reConstructBinaryTree(pre[1:1+i],tin[:i])
        root_data.right = self.reConstructBinaryTree(pre[1+i:],tin[i+1:])
        return root_data

# 實例化
if __name__=='__main__':
    pre=[1,2,4,7,3,5,6,8]
    tin=[4,7,2,1,5,3,8,6]
    demo = Solution()
    print(demo.reConstructBinaryTree(pre,tin))

二叉樹的下一個節點

題目描述
給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。
對於這道題,要求找到中序遍歷的下一個節點。

首先,對於中序遍歷而言,自然是先左、再根、最後右。
中序是DNH BIEJ A FKVCLGM
在這裏插入圖片描述

分析二叉樹的下一個節點,一共有以下情況:
1.二叉樹爲空,則返回空;
2.節點右孩子存在,則設置一個指針從該節點的右孩子出發,一直沿着指向左子結點的指針找到的葉子節點即爲下一個節點F;(eg:D,B,E,A,C,G)
2、沒有右子樹的,也可以分成兩類,a)是父節點左孩子(eg:N,I,L) ,那麼父節點就是下一個節點 ; b)是父節點的右孩子(eg:H,J,K,M)找他的父節點的父節點的父節點…直到當前結點是其父節點的左孩子位置。如果沒有eg:M,那麼他就是尾節點

# -*- 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):
        if pNode.right:#有右子樹
            p=pNode.right
            while p.left:
                p=p.left
            return p
        while pNode.next:#無右子樹,則找第一個當前節點是父節點左孩子的節點
            if(pNode.next.left==pNode):
                return pNode.next
            pNode = pNode.next#沿着父節點向上遍歷
        return  #到了根節點仍沒找到,則返回空

方法2:

思路,如果這道題是求中序遍歷,肯定很簡單。所以我們先寫一箇中序遍歷的算法。關鍵是從根節點開始遍歷,所以第一步還是找到某個節點的根節點,方法是一直使用next判斷即可。
再將從根節點中序遍歷的結果保存到一個數組中,直接找pNode所在索引的下一個即可。當然要考慮這個節點是不是最後一個,如果是最後一個,直接返回None。

鏈接:https://www.nowcoder.com/questionTerminal/9023a0c988684a53960365b889ceaf5e?f=discussion
來源:牛客網

class Solution:
    def GetNext(self, pNode):
        # write code here
        dummy = pNode
        while dummy.next:
            dummy = dummy.next
        self.result = []
        self.midTraversal(dummy)
        return self.result[self.result.index(pNode) + 1] if self.result.index(pNode) != len(self.result) - 1 else None
 
    def midTraversal(self, root):
        if not root: return
        self.midTraversal(root.left)
        self.result.append(root)
        self.midTraversal(root.right)

對稱的二叉樹

請實現一個函數,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的鏡像是同樣的,定義其爲對稱的。

/*思路:首先根節點以及其左右子樹,左子樹的左子樹和右子樹的右子樹相同

  • 左子樹的右子樹和右子樹的左子樹相同即可,採用遞歸比較左右節點,然後對左右節點的左右分支進一步遞歸比較
  • 非遞歸也可,採用棧或隊列存取各級子樹根節點
遞歸方法
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def isSymmetrical(self, pRoot):
        if pRoot is None:
            return True
        return self.Traversal(pRoot.left,pRoot.right)
    def Traversal(self,left, right):
        if left is None and right is None:
            return True
        elif left and right and left.val ==right.val:
            return self.Traversal(left.left,right.right) and self.Traversal(left.right,right.left)
        else:
            return False

按照之字形順序打印二叉樹

請實現一個函數按照之字形打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印,第三行按照從左到右的順序打印,其他行以此類推。

可以用一個隊列,一個棧實現 也可用兩個棧實現,我用兩個棧實現,思路簡單一點。
在這裏插入圖片描述

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Print(self, pRoot):
        if pRoot is  None:
            return []
        queue=[]
        queue.append(pRoot)#把根節點存儲在隊列
        result=[]#存儲樹有多少層,1,2,3,4之類
        while len(queue)!=0:
            res=[] #存儲每層節點
            nextStack=[] #存儲左右子樹節點
            for i in queue:
                res.append(i.val)
                if i.left:
                    nextStack.append(i.left)
                if i.right:
                    nextStack.append(i.right)
            queue=nextStack
            result.append(res) ##這裏就是數組裏面套數組
        returnResult=[]
        for i, v in enumerate(result):
            if i%2==0:
                returnResult.append(v)
            else:
                returnResult.append(v[::-1])#這裏每一個裏面是一個數組
                #result=[[1],[2,3],[4,5,6,7]]
        return returnResult
            

把二叉樹打印成多行

題目描述
從上到下按層打印二叉樹,同一層結點從左至右輸出。每一層輸出一行。
思路: 二叉樹的層次遍歷 先計算出根節點的左右子樹,藉助一個隊列,再循環遞歸
思路還是很清晰的,使用兩個隊列一個存放節點,一個存放值。先將根節點加入到隊列中,然後遍歷隊列中的元素,遍歷過程中,訪問該元素的左右節點,再將左右子節點加入到隊列中來

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Print(self, pRoot):
        if pRoot is  None:
            return []
        queue=[]
        queue.append(pRoot)#把根節點存儲在隊列
        result=[]#存儲樹有多少層,1,2,3,4之類
        while len(queue)!=0:
            res=[] #存儲每層節點
            nextStack=[] #存儲左右子樹節點
            for i in queue:
                res.append(i.val)
                if i.left:
                    nextStack.append(i.left)
                if i.right:
                    nextStack.append(i.right)
            queue=nextStack
            result.append(res) #這裏得到是每一層,和每一層對應的節點
        return result

          

序列化二叉樹

題目描述
請實現兩個函數,分別用來序列化和反序列化二叉樹

對於序列化:使用前序遍歷,遞歸的將二叉樹的值轉化爲字符,並且在每次二叉樹的結點 不爲空時,在轉化val所得的字符之後添加一個’ ,'作爲分割。對於空節點則以 ‘#’ 代替。
2. 對於反序列化:按照前序順序,遞歸的使用字符串中的字符創建一個二叉樹(特別注意: 在遞歸時,遞歸函數的參數一定要是char ** ,這樣才能保證每次遞歸後指向字符串的指針會 隨着遞歸的進行而移動!!!)

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.flag = -1
         
    def Serialize(self, root):
        # write code here
        if not root:
            return '#,'
        return str(root.val)+','+self.Serialize(root.left)+self.Serialize(root.right)
         
    def Deserialize(self, s):
        # write code here
        self.flag += 1
        l = s.split(',')
         
        if self.flag >= len(s):
            return None
        root = None
         
        if l[self.flag] != '#':
            root = TreeNode(int(l[self.flag]))
            root.left = self.Deserialize(s)
            root.right = self.Deserialize(s)
        return root

判斷結果

輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。

思路:

  1. 二叉樹的後序遍歷 先左節點再是右節點,最後是根節點
    2.二叉搜索樹是對一個有序數組進行二分查找形成的搜索樹,它指一棵空樹或者具有下列性質的二叉樹:
    若任意節點的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值; 若任意節點的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值;
    任意節點的左、右子樹也分別爲二叉查找樹;

以下圖二叉樹爲例,我們都知道對二叉搜索樹進行中序遍歷可以還原爲一個有序數組,而圖中二叉樹後序遍歷結果爲[3,2,4,6,8,7,5]。接下來分析一下這個數組的規律。首先按照後序遍歷的性質,數組的最後一個元素即是整個二叉樹的根結點。另外按照搜索二叉樹的前兩條性質,左子樹均小於根,右子樹均大於根。左右子樹部分同樣符合整個規律,所以我們可以遞歸實現數組的判斷。
在這裏插入圖片描述

# -*- coding:utf-8 -*-
class Solution:
     def VerifySquenceOfBST(self, sequence):
        # write code here
        if len(sequence) == 0:
            return False
        else:
            root = sequence[-1]
            del sequence[-1]
            lefttree = []
            righttree =[]
            # 左子樹和右子樹分界
            splitindex = -1
            for i in range(len(sequence)):
                # 值小於根結點的歸爲左子樹
                if sequence[i] < root:
                    lefttree.append(sequence[i])
                    splitindex = i
                else:
                    break
            for i in range(splitindex+1, len(sequence)):
                # 若右子樹部分有小於根結點的值,說明不是二叉搜索樹
                if sequence[i] > root:
                    righttree.append(sequence[i])
                else:
                    return False
            if len(lefttree) <= 1:
                left = True
            else:
                # 遞歸判斷左子樹
                left = self.VerifySquenceOfBST(lefttree)
            if len(righttree) <= 1:
                right = True
            else:
                right = self.VerifySquenceOfBST(righttree)
            return left and right

二叉搜索樹的第k個節點

給定一棵二叉搜索樹,請找出其中的第k小的結點。例如, (5,3,7,2,4,6,8) 中,按結點數值大小順序第三小結點的值爲4。
思路分析:
進行深度遍歷–然後存到列表裏,

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回對應節點TreeNode
    def KthNode(self, pRoot, k):
        self.res=[]
        self.dfs(pRoot)
        if 0<k<=len(self.res):
            return self.res[k-1] 
        else:
            return None
    def dfs(self,root):
        if not root:return
        self.dfs(root.left)
        self.res.append(root)
        self.dfs(root.right)

樹的子結構

輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)
**> 解題思路

正確思路 遍歷二叉樹A,定位B的根節點在A中的可能位置——> 定位後,驗證B是不是A當前位置的子結構。**
本題的思路在於子結構的判斷,首先需要判斷兩棵樹的根節點是否相同,若是不同則遞歸調用其左子樹和B樹,若仍不同則遞歸調用其右子樹和B樹,若都不同則返回false。
在這裏插入圖片描述
原文:https://blog.csdn.net/u010005281/article/details/79460325 --完整測試代碼

1.先在A中找和B的根節點相同的結點

2.找到之後遍歷對應位置的其他結點,直到B中結點遍歷完,都相同時,則B是A的子樹

3.對應位置的結點不相同時,退出繼續在A中尋找和B的根節點相同的結點,重複步驟,直到有任何一棵二叉樹爲空退出

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        if pRoot1==None or pRoot2==None:
            return False
        result=False
        if pRoot1.val==pRoot2.val:
            result=self.isSubtree(pRoot1,pRoot2)
        if result==False:
            result=self.HasSubtree(pRoot1.left,pRoot2)|self.HasSubtree(pRoot1.right,pRoot2)
        return result
    def isSubtree(self,root1,root2):
        #該函數判斷在找到和子樹根節點相同的節點之後,判斷其餘節點是否相同
        if root2==None:
            return True
        if root1==None:
            return False
        if root1.val==root2.val:
            return self.isSubtree(root1.left,root2.left)& self.isSubtree(root1.right,root2.right)
        return False
        

是否是平衡二叉樹

題目描述
輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。

思路:平衡二叉樹的定義是任何節點的左右子樹高度差都不超過1的二叉樹。

按照定義,很容易得出獲得左右子樹高度,然後判斷。遞歸寫法。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, pRoot):
        if not pRoot: return True
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        if abs(left - right) > 1:
            return False
        return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)

    def TreeDepth(self, pRoot):
        if not pRoot: return 0
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        return max(left, right) + 1

對比:求二叉樹的深度(Python代碼)

def TreeDeep(self, pRoot):
    if not pRoot:
        return 0
    left = self.TreeDeep(pRoot.left)
    right = self.TreeDeep(pRoot.right)
    return left+1 if left>right else right+1

二叉樹深度和平衡二叉樹

題目描述
輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。

求樹的深度,可以從層次遍歷出發考慮 層次遍歷可以使用隊列完成,也可以使用遞歸完成,所以有兩種方法

方法一:使用隊列
class Solution:
    # 層次遍歷
    def levelOrder(self, root):
        # write your code here
        # 存儲最後層次遍歷的結果
        res = []
        # 層數
        count = 0
        # 如果根節點爲空,則返回空列表
        if root is None:
            return count
        # 模擬一個隊列儲存節點
        q = []
        # 首先將根節點入隊
        q.append(root)
        # 列表爲空時,循環終止
        while len(q) != 0:
            # 使用列表存儲同層節點
            tmp = []
            # 記錄同層節點的個數
            length = len(q)
            for i in range(length):
                # 將同層節點依次出隊
                r = q.pop(0)
                if r.left is not None:
                    # 非空左孩子入隊
                    q.append(r.left)
                if r.right is not None:
                    # 非空右孩子入隊
                    q.append(r.right)
                tmp.append(r.val)
            if tmp:
                count += 1  # 統計層數
            res.append(tmp)
        return count
 
    def TreeDepth(self, pRoot):
        # write code here
        # 使用層次遍歷
        # 當樹爲空直接返回0
        if pRoot is None:
            return 0
        count = self.levelOrder(pRoot)
        return count
方法二:使用遞歸方法
class Solution:
    def TreeDepth(self, pRoot):
        # write code here
        # 使用層次遍歷
        # 當樹爲空直接返回0
        if pRoot is None:
            return 0
        # 方法2:使用遞歸
        # 如果該樹只有一個結點,它的深度爲1.如果根節點只有左子樹沒有右子樹,
        # 那麼樹的深度爲左子樹的深度加1;同樣,如果只有右子樹沒有左子樹,
        # 那麼樹的深度爲右子樹的深度加1。如果既有左子樹也有右子樹,
        # 那該樹的深度就是左子樹和右子樹的最大值加1.
        count = max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right)) + 1
        return count

二叉樹和爲某一值的路徑

題目描述
輸入一顆二叉樹的根節點和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的list中,數組長度大的數組靠前)

not x 意思相當於 if x is false, then True, else False

代碼中經常會有變量是否爲None的判斷,有三種主要的寫法:

第一種是if x is None;

第二種是 if not x:;

第三種是if not x is None(這句這樣理解更清晰if not (x is None))

思路1:–遞歸

首先要理解題意,是從根節點往子節點連。

1、如果只有根節點或者找到葉子節點,我們就把其對應的val值返回

2、如果不是葉子節點,我們分別對根節點的左子樹、右子樹進行遞歸,直到找到葉子結點。然後遍歷把葉子結點和父節點對應的val組成的序列返回上一層;如果沒找到路徑,其實也返回了序列,只不過是[]

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def FindPath(self, root,target_number):
        result =[]
        if not root:
            return result
        if not root.left and not root.right and root.val == target_number:
            return [[root.val]]
        else:
            left = self.FindPath(root.left,target_number - root.val)
            right = self.FindPath(root.right,target_number - root.val)
            for item in left+right:
                result.append([root.val]+item)
            return result

思路2:和思路1類似的想法,但是提出了一些python的注意事項

這是一道DFS題目,也可以看做是先序遍歷的題目 ,在二叉樹中,dfs就相當於先序遍歷
首先,採用一種“減法”思想,當檢查一棵樹從根到葉子節點形成的路徑的和是否爲target時,先將當前根節點的值 root.val
加入path, 然後檢查它的左子樹(若非空),看從左子樹的根到葉子節點形成的路徑的和是否爲 target - root.val (遞歸),
然後同樣的道理去遞歸檢查右子樹(若非空),這便是大致的思路。 但這道題麻煩的一點是,它要求記錄下所有符合標準的路徑,這便用到了dfs的特性。
但又來了一件麻煩事,先序遍歷便是先左後右。檢查完左子樹後,會對path就行修改,再去查找右子樹,如何將path恢復到之前未進行左子樹檢查的狀態?

一開始自己的做法是,每到分叉路口,到對當前路徑做一份拷貝,用原路徑去繼續進行左子樹的遞歸,拷貝路徑進行右子樹的遞歸,這樣左子樹對右子樹的結果產生影響,但是這樣造成了大量的拷貝,浪費空間
比較好的做法是將path設爲全局的,然後dfs的過程便是先序遍歷的過程,一旦遍歷到葉子結點,便將path最後的節點移除掉,這樣在遞歸一層一層進行的時候將值添加進path,在遞歸返回的過程中將path最末尾的元素一個一個移除。這樣便依靠遞歸的特性完成了路徑的恢復。

例如對於樹 10,5,12,5,7,#,#,#,#,#,#,#,#(層序遍歷),path變化過程爲 10,5,5 》》 10,5 》》
10,5,7 》》10,5 》》10 》》10,12 》》10 》》 null

因爲最終遍歷到葉子結點時,若發現符合規定的路徑,是把path加入到結果集中,因爲java中添加的是引用,而path是不斷變化的,所以我們要新new一個當前路徑的副本出來,防止添加進去的path都是相同的(最終path的狀態)。

當然,這道題也可以用加法的思想,看dfs過程中能否加出target

因爲題意沒有說節點值全爲正數,所以必須遞歸到根節點才能確定這條路徑能否加出target,而不能到中間節點就加到>=target了,就認爲這條路徑不行了,假如這條路徑後序有0或者負數的情況,還是能加出target的。
經驗教訓 深刻理解DFS的用法 深刻理解遞歸是如何恢復path的,從先序遍歷進行考慮,遞歸是如何一步步進行的,如何一步步返回的
最終添加入結果集中,必須添加的是path的副本 遞歸中,全局變量的使用

# -*- coding:utf-8 -*-
class Solution:
    # 返回二維列表,內部每個列表表示找到的路徑
    def FindPath(self, root, expectNumber):
        # write code here
        if not root:
            return []
 
        ret = []
        path = []
        self.Find(root, expectNumber, ret, path)
        # print ret
        # a = [];self.f(a);print a
        return ret
     
    def Find(self, root, target, ret, path):
        if not root:
            return
 
        path.append(root.val)
        isLeaf = (root.left is None and root.right is None)
        if isLeaf and target == root.val:
            ret.append(path[:])  # 這裏這一步要千萬注意啊,
            # 假如是:ret.append(path), 結果是錯的。因爲Python可變對象都是引用傳遞啊。
 
        #print "target:", target, "isLeaf:", isLeaf,
        #print "val:", root.val, "path:", path, "ret:", ret
        #print
 
        if root.left:
            self.Find(root.left, target - root.val, ret, path)
        if root.right:
            self.Find(root.right, target - root.val, ret, path)
 
        path.pop()

二叉樹鏡像

題目描述
操作給定的二叉樹,將其變換爲源二叉樹的鏡像。
輸入描述:
二叉樹的鏡像定義:源二叉樹
(https://img-blog.csdnimg.cn/20190221215648202.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI0NDI5MzMz,size_16,color_FFFFFF,t_70)
思路:
將左子樹和右子樹交換
遞歸思想,先交換根節點的左右子樹的位置,然後向下遞歸,把左右子樹的根節點作爲下次循環的根節點

class Solution:
    # 返回鏡像樹的根節點
    def Mirror(self, root):
        if root==None:
            return 
        if root.left==None and root.right==None:
            return 
       #這三步交換根節點的左右樹,交換的樹,而不單單是節點
        tmp=root.right
        root.right=root.left
        root.left=tmp
        if root.left is not None:
            self.Mirror(root.left)
        if root.right is not None:
            self.Mirror(root.right)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章