LeetcodeMedium-【面試題36. 二叉搜索樹與雙向鏈表】-中序遍歷

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的循環雙向鏈表。要求不能創建任何新的節點,只能調整樹中節點指針的指向。

爲了讓您更好地理解問題,以下面的二叉搜索樹爲例:
在這裏插入圖片描述
我們希望將這個二叉搜索樹轉化爲雙向循環鏈表。鏈表中的每個節點都有一個前驅和後繼指針。對於雙向循環鏈表,第一個節點的前驅是最後一個節點,最後一個節點的後繼是第一個節點。

下圖展示了上面的二叉搜索樹轉化成的鏈表。“head” 表示指向鏈表中有最小元素的節點。
在這裏插入圖片描述
特別地,我們希望可以就地完成轉換操作。當轉化完成以後,樹中節點的左指針需要指向前驅,樹中節點的右指針需要指向後繼。還需要返回鏈表中的第一個節點的指針。

注意:本題與主站 426 題相同:https://leetcode-cn.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/

注意:此題對比原題有改動。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

思路1:先序遍歷

直觀的可以發現如果不需要鏈表的化,z中序遍歷就是排好序的樹了,那麼本題的要求轉爲雙向鏈表,也就是要求在先序遍歷的同時修改left、right指針,從而完成雙向鏈表。結合二叉搜索樹的性質可以知道,每個節點都大於左子樹,小於右子樹。故可以知道:
每個結點的需要修改的操作爲:
1.left 指向小於自己的最大節點,即左子樹中的最右節點;
2.right指向大於自己的最小節點,即右子樹中最左節點。

根據這兩條其實就可以做題了,需要注意的是遍歷函數的設計,應爲針對左子樹和右子樹需要的目標不同,但是制定遞歸時,並不知道當前遍歷的節點是左子樹還是右子樹,於是我設計的是返回四個參數:分別是左子樹的最小值和最大值,右子樹的最小值和最大值。這樣在調用的地方根據需求取對應的返回值就行了。
還有一點,需要記錄頭節點和尾節點,最後需要把頭尾相連。
在這裏插入圖片描述

"""
# Definition for a Node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        if root == None:
            return None
        self.tail = self.head = None 
        # 先序遍歷
        def mid(root): 
            if root == None:
                return None
            rt_l1, rt_r1, rt_l2, rt_r2, rt2_l1, rt2_r1, rt2_l2, rt2_r2, tail = None, None, None, None, None, None, None, None, None
            if root.left:
                rt_l1, rt_r1, rt_l2, rt_r2, _ = mid(root.left)
                # 此節點的left應該指向它的左子樹的最右節點,即比它小的最大值                
                root.left = rt_r2
                rt_r2.right = root
            # 記錄頭節點
            if self.head == None:
                self.head = root
            # print(root.val)
            if root.right:
                rt2_l1, rt2_r1, rt2_l2, rt2_r2, tail  = mid(root.right)
                # 此節點的right應該指向它的右子樹的最左節點,即比它大的最小值
                root.right = rt2_l1
                rt2_l1.left = root
            # 需要返回左子樹的最右節點,即比自己小的最大值;需要返回右子樹的最左節點,即比自己大的最小值; 需要返回尾節點即右子樹最右值,即最大值
            return rt_l1 if rt_l1 else root,rt_r2 if rt_r2 else root, rt2_l1 if rt2_l1 else root, rt2_r2 if rt2_r2 else root,  tail if tail else root
        
        _, _,  _, _, self.tail = mid(root)
        # 首位節點相連
        self.head.left = self.tail
        self.tail.right = self.head
        return self.head
    # [-76,null,-6,null,39,null,94]
    # [27,-99,55,null,-34,null,58,null,-8,null,59,null,8,null,68]

思路2:先序,簡潔寫法

中序遍歷
在這裏插入圖片描述
根據中序輸出的結果我們知道就是排序好的順序,也就是說每次輸出的節點它應該鏈接的節點就是上一個輸出的節點,所以只需要用個一個變量記錄下上一個輸出的節點就行了,然後每次遍歷到新輸出時,只需要將它與記錄的前一個節點進行關聯。這個真的簡潔啊!!!

"""
# Definition for a Node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        if root == None:
            return None
        def dfs(root):
            if root == None:
                return
            dfs(root.left) # 遞歸左子樹
            # print(root.val) 
            if self.head:
                root.left = self.pre # 指向上一個輸出的節點
                self.pre.right = root 
            else:
                self.head = root
            self.pre = root # 記錄當前輸出的節點
            dfs(root.right) # 遞歸右子樹
        self.pre = None
        self.head = None
        dfs(root)
        self.head.left = self.pre
        self.pre.right = self.head
        return self.head
    # [-76,null,-6,null,39,null,94]
    # [27,-99,55,null,-34,null,58,null,-8,null,59,null,8,null,68]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章