1. 程式人生 > >python實現二叉樹的相關演算法(持續更新)

python實現二叉樹的相關演算法(持續更新)

定義樹結構

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

前序遍歷

def pre_iter(node):
    if not node:
        return
    print(node.val)
    pre_iter(node.left)
    pre_iter(node.right)

中序遍歷

def mid_iter(node):
    if not
node: return mid_iter(node.left) print(node.val) mid_iter(node.right)

後序遍歷

def post_iter(node):
    if not node:
        return
    post_iter(node.left)
    post_iter(node.right)
    print(node.val)

程式碼雖簡單,但是個人認為樹結構是學習遞迴,乃至學習函數語言程式設計的好例子.宜細細品味,挖個函數語言程式設計的坑,希望以後自己可以寫出一篇完整的文章.

class Solution:
    # 返回構造的TreeNode根節點
    def reConstructBinaryTree(self, pre, tin):
        if len(tin) == 0:
            return None
        else:
            #先序序列當中第一個值必定為該層父節點
            root = TreeNode(pre[0])
            #root已經包含了返回的節點值
            slt = tin.index(pre[0])
            root.left = self.reConstructBinaryTree(pre[1
:1+slt],tin[:slt]) root.right = self.reConstructBinaryTree(pre[1+slt:],tin[slt+1:]) return root
class Solution(object):
    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """
        if not inorder and not postorder:
            return None
        else:
            val = postorder.pop()
            root = TreeNode(val)
            i = inorder.index(val)
            root.left = self.buildTree(inorder[:i],postorder[:i])
            root.right = self.buildTree(inorder[i+1:],postorder[i:])
            return root

二叉樹的深度優先遍歷(DFS)

def dfs(self, root):
    stk = []#以棧結構實現
    if not root:
        return root
    else:
        stk.append(root)
        while stk:
            node = stk.pop()
            print(node.val)#這裡可執行一切使用當前結點的方法
            #右子結點先入棧
            if node.right:
                stk.append(node.right)
            #左子結點後入棧
            if node.left:
                stk.append(node.left)

二叉樹的廣度優先遍歷(BFS)

def bfs(self, root):
    q = []#以佇列結構實現
    if not root:
        return root
    else:
        q.append(root)
        while q:
            #以list結構實現棧與佇列結構知識pop()與pop(0)的區別
            node = q.pop(0)
            print(node.val)#這裡可執行一切使用當前結點的方法
            #左子結點先入棧
            if node.left:
                q.append(node.left)
            #右子結點後入棧
            if node.right:
                q.append(node.right)

在迭代實現遍歷時,深度優先以棧結構實現,廣度優先以佇列結構實現.

附:二叉樹的深度優先遍歷遞迴實現

實際上樹的前序後序中序遍歷都是深度優先的具體情況,以上面前序遍歷的程式碼即可作為樹的深度優先遍歷的遞迴實現.

def dfs_rcv(node):
    if not node:
        return
    print(node.val)
    dfs_rcv(node.left)
    dfs_rcv(node.right)

二叉樹的映象(對稱反轉一個二叉樹)

def Mirror(root):
    if not root:
        return
    else:
        root.left, root.right = Mirror(root.right), Mirror(root.left)
        #錯誤寫法
        #root.left = Mirror(root.right)
        #root.right = Mirror(root.left)
        return root

二叉樹的深度

    def depth(root):
        if not root:
            return 0
        else:
            nleft = depth(root.left)
            nright = depth(root.right)
            return max(nleft, nright) + 1

判斷是否平衡二叉樹

class Solution:
    def getDepth(self, root):
        if not root:
            return 0
        else:
            left = self.getDepth(root.left)
            right = self.getDepth(root.right)
            return max(left,right) + 1

    def IsBalanced_Solution(self, pRoot):
        # write code here
        if not pRoot:
            return True
        else:
            left = self.getDepth(pRoot.left)
            right = self.getDepth(pRoot.right)
            if abs(left-right) <= 1:
                return True
            else:
                return False

檢驗兩棵樹是否相同(LeetCode:SameTree)

        if not p and not q:
            return True
        else:
            if not p or not q:
                return False
            else:
                return (p.val == q.val) and self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)

注意: 給出case當中,空樹也為True.左右子樹為空,也為True.

class Solution:
    def issame(self,p1,p2):
        if not p1 and not p2:
            return True
        else:
            if p1 and p2:
                return (p1.val == p2.val) and self.issame(p1.left,p2.right) and self.issame(p1.right,p2.left)
            else:
                return False

    def isSymmetrical(self, pRoot):
        # write code here
        if not pRoot:
            return True
        else:
            if pRoot.left and pRoot.right:
                return self.issame(pRoot.left, pRoot.right)
            else:
                return True

輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。要求不能建立任何新的結點,只能調整樹中結點指標的指向。

class Solution:
    def Convert(self, pRootOfTree):
        # 僅針對測試用例為空根節點
        if not pRootOfTree:
            return pRootOfTree
        # 葉子結點返回結點
        if not pRootOfTree.left and not pRootOfTree.right:
            return pRootOfTree
        else:
        #針對所有非葉節點
            if pRootOfTree.left:
                lch = self.Convert(pRootOfTree.left)
            #因為下一層的return值為最左端結點
            #因此作為根節點的連線點,左子樹構造的連結串列需遍歷到最右端
                while lch.right:
                    lch = lch.right
                pRootOfTree.left = lch
                lch.right = pRootOfTree
            if pRootOfTree.right:
                rch = self.Convert(pRootOfTree.right)
            #下一層的return值為最左端結點
            #因此對於根節點來說,直接連線即可             
                pRootOfTree.right = rch
                rch.left = pRootOfTree
            head = pRootOfTree
            #每層構造完連結串列,返回最左端,也就是頭結點
            while head.left:
                head = head.left
            return head

1) 分析每一層的任務:構造一個 “[左子樹構造的連結串列]<–>根節點<–>[右子樹構造的連結串列]”類似這樣結構的連結串列
2) 遞迴問題的性質:問題的返回值要求決定了每次遞迴的返回值.
在這個問題當中,要求返回構造後的雙向連結串列的頭結點,即1)當中[左子樹構造的連結串列]的頭結點.
如果是隻要求返回根節點在連結串列中的位置,問題會簡單一些,程式碼會更易讀一點.
3) 確定遞迴終止條件:
如果是[沒有左子結點 or 沒有右子節點]作為終止條件,會導致不能訪問到所有節點.因此應該選擇[沒有左子結點 and 沒有右子節點]作為終止條件.
4) 到這裡solution已經得出大體結構,編碼試錯.發現提示訪問了Nonetype的right或者left,加入if pRootOfTree.right 和 if pRootOfTree.left: 解決.

Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

Note:

You may only use constant extra space.
Recursive approach is fine, implicit stack space does not count as extra space for this problem.
Example:

Given the following binary tree,

     1
   /  \
  2    3
 / \    \
4   5    7

After calling your function, the tree should look like:

    1 -> NULL
   /  \
  2 -> 3 -> NULL
 / \    \
4-> 5 -> 7 -> NULL
# Definition for binary tree with next pointer.
# class TreeLinkNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#         self.next = None

class Solution:
    # @param root, a tree link node
    # @return nothing
    def connect(self, root):
        if not root:
            return root
        else:
            layer = [root]
            while layer:
                next_layer = []
                p = head = layer.pop(0)
                if p.left:
                    next_layer.append(p.left)
                if p.right:
                    next_layer.append(p.right)
                while layer:
                    node = layer.pop(0)
                    if node.left:
                        next_layer.append(node.left)
                    if node.right:
                        next_layer.append(node.right)
                    p.next = node
                    p = p.next
                layer = next_layer

廣度優先遍歷的一種形式.

給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指標。

# -*- 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 not pNode:
            return pNode
        else:
            if pNode.right:
                pNode = pNode.right
                while pNode.left:
                    pNode = pNode.left
                return pNode
            else:
                p = pNode
                while p.next:
                    pp = p.next
                    if pp.left == p:
                        return pp
                    p = pp
                return p.next

思路:
1) 對於給定結點pNode, 分為if pNode.right else 有無右子節點兩種情況;
2) pNode有右結點 則中序遍歷的下一個結點必定在右子樹當中,具體位置是右子樹的最左結點;
3) pNode無右結點 則往父結點尋找 從底往上 當找到父節點為祖父結點的左結點時 該祖父結點則為返回結點值;
4) 若找不到3) 中的結構,則說明當前結點pNode 為中序遍歷順序的最右端.

112. Path Sum
查詢是否存在從根到葉的路徑path, path上的結點的值相加為目標值sum. 存在返回True 不存在返回False

class Solution:
    def hasPathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: bool
        """
        if not root:
            return False
        else:
            if root.left and root.right:
                return self.hasPathSum(root.left,sum-root.val) or self.hasPathSum(root.right,sum-root.val)
            elif root.left:
                return self.hasPathSum(root.left,sum-root.val)
            elif root.right:
                return self.hasPathSum(root.right,sum-root.val)
            else:
                return bool(root.val == sum)

其實是因為對於輸入為([],0),即空樹和0的特例, LeetCode要求返回False.如果該特例可以返回True,有以下更簡潔的寫法:

class Solution:
    def hasPathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: bool
        """
        if not root:
            return bool(sum==0)
        else:
            return self.hasPathSum(root.left,sum-root.val) or self.hasPathSum(root.right,sum-root.val)

Leetcode 113. Path Sum II
查詢是否存在從根到葉的路徑path, path上的結點的值相加為目標值sum. 以二維陣列形式返回所有符合的路徑

class Solution:
    def pathSum(self, root, sumk):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: List[List[int]]
        """
        res = []
        if not root:
            return res
        stk = [(root,[root.val])]
        while stk:
            node,path = stk.pop()
            if node.left or node.right:
                if node.left:
                    lp = path + [node.left.val]
                    nxt = (node.left,lp)
                    stk.append(nxt)
                if node.right:
                    rp = path + [node.right.val]
                    nxt = (node.right,rp)
                    stk.append(nxt)
            else:
                if sum(path) == sumk:
                    res.append(path)
        return res