劍指offer(Python版)——23-28
23.二叉搜尋樹的後序遍歷
題目:輸入一個整數陣列,判斷該陣列是不是某二叉搜尋樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的陣列的任意兩個數字都互不相同。
解題思路:遇到二叉樹,首先應該想的是遞迴來解決,因為它的結構太對稱了。二叉搜尋樹,左子樹的節點值都小於根節點值,根節點值都小於右子樹的節點值。後序遍歷的過程是左子樹,右子樹,根節點。二叉搜尋樹的後序遍歷得到的序列就滿足這幾個特性。第一,最後一個值是根節點的值,其次,從序列的第一個值開始,遇到的第一個大於根節點值之前的值都是左子樹的,剩下的到根節點之前都是右子樹的。這樣就把整個二叉樹的框架搭建起來了,分清了左子樹、右子樹,根節點。在每個子樹裡採取同樣的操作遞迴執行,就可以將左右子樹構建為二叉樹。遞迴中,主要的區別就是底層的判斷設計,這裡是判斷是否是後序遍歷,則在最底層時,只剩一個節點,直接返回真。
程式碼如下:
# -*- coding:utf-8 -*- class Solution: def VerifySquenceOfBST(self, sequence): # write code here if not sequence: return False if len(sequence) == 1: return True i = 0 # 尋找比根節點大的第一個值 while (sequence[i] < sequence[len(sequence)-1]): i += 1 r_1 = sequence[i] j = i # 判斷左子樹所有節點是否比根節點小 while (j < len(sequence)-1): if sequence[j] < sequence[-1]: return False else: j += 1 # 設定標誌來記錄左右子樹是否是後序遍歷 left = True right = True # 遞迴的判斷左右子樹 if len(sequence[0:i]) > 0: left = self.VerifySquenceOfBST(sequence[0:i]) if len(sequence[i:len(sequence)-1]) > 0: right = self.VerifySquenceOfBST(sequence[i:len(sequence)-1]) return left and right
24.二叉樹中和為某一值的路徑
題目:輸入一顆二叉樹的跟節點和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。路徑定義為從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的list中,陣列長度大的陣列靠前)
解題思路:二叉樹,先考慮遞迴解決,同樣可以拆分分成左右子樹遞迴去做,找出左子樹中滿足值減去根節點值得所有路徑,右子樹也是同樣,這樣將根節點和左右子樹所有滿足條件的序列組合,就是最終的所有路徑。所有路徑都放在list中,所以遞迴的底層是判斷是否滿足是葉節點並且值和傳遞進來的相等。
程式碼如下:
# -*- coding:utf-8 -*- # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: # 返回二維列表,內部每個列表表示找到的路徑 def FindPath(self, root, expectNumber): # write code here # 按遞迴的方法,主要講遞迴最底層的判斷條件寫清楚就好了 if not root : return [] if root and not root.left and not root.right and root.val == expectNumber: return [[root.val]] result = [] # 遞迴的判斷左右子樹 re_left = self.FindPath(root.left,expectNumber-root.val) re_right = self.FindPath(root.right,expectNumber-root.val) # 將左右子樹中的所有路徑與根節點合併為最終路徑 for i in re_left + re_right: result.append([root.val]+i) return result
25.複雜連結串列的複製
題目:輸入一個複雜連結串列(每個節點中有節點值,以及兩個指標,一個指向下一個節點,另一個特殊指標指向任意一個節點),返回結果為複製後複雜連結串列的head。(注意,輸出結果中請不要返回引數中的節點引用,否則判題程式會直接返回空)
解題思路:三段式解法,第一步現將連結串列每個節點在原連結串列中複製一個節點,這樣連結串列就變成了原來的二倍,但是擴充的節點只有next指標,隨機指標為空;第二步,將連結串列中原來節點的隨機指標進行復制,新連結串列節點的隨機指標等於原連結串列節點隨機指向節點的next指標。第三步,連結串列拆分成兩個連結串列,使用兩個指標交替改變節點指向,最後得到拆分連結串列。複製next指標和隨機指標的結構很類似,拆分連結串列時注意兩個指標交替進行即可
程式碼如下:
# -*- coding:utf-8 -*-
# class RandomListNode:
# def __init__(self, x):
# self.label = x
# self.next = None
# self.random = None
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
# write code here
self.CloneNode(pHead)
self.ConnectRandomNode(pHead)
return self.ReconnctNode(pHead)
def CloneNode(self,pHead):
# 將連結串列進行復制
pNode = pHead
while (pNode):
pCloneNode = RandomListNode(pNode.label)
pCloneNode.next = pNode.next
pNode.next = pCloneNode
pNode = pCloneNode.next
def ConnectRandomNode(self,pHead):
# 複製節點隨機指標
pNode = pHead
while (pNode):
pClone = pNode.next
if pNode.random :
pClone.random = pNode.random.next
pNode = pClone.next
def ReconnctNode(self,pHead):
# 拆分連結串列
pNode = pHead
pCloneHead = None
pCloneNode = None
if pNode:
pCloneHead = pCloneNode = pNode.next
pNode.next = pCloneNode.next
pNode = pNode.next
while (pNode):
pCloneNode.next = pNode.next
pCloneNode = pCloneNode.next
pNode.next = pCloneNode.next
pNode = pNode.next
return pCloneHead
26.二叉搜尋樹與雙向連結串列
題目:輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。要求不能建立任何新的結點,只能調整樹中結點指標的指向。
解題思路:二叉樹,先考慮遞迴去解決。將二叉搜尋樹轉變成雙向連結串列只要將二叉搜尋樹的節點指標變成中序遍歷的序列指向即可。還是先在左子樹,根節點,右子樹上考慮。中序的序列中,根節點前面的節點是左子樹中最大的,根節點後面的節點是右子樹中最小的節點。掌握了這個特性,可以先將左子樹遞迴,然後取出左子樹列表的最大的,也是最右端的節點,修改指標。右子樹類似操作。最後將整個雙向列表的頭結點返回(最左邊的節點)
程式碼如下:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Convert(self, pRootOfTree):
# 採用遞迴的方法,去解決左右子樹
# 根節點和左子樹最大的節點相連,和右子樹最小的節點相連
if not pRootOfTree:
return pRootOfTree
if not pRootOfTree.left and not pRootOfTree.right:
return pRootOfTree
# 遞迴處理左子樹,con_left是轉化成連結串列後的左子樹部分
self.Convert(pRootOfTree.left)
conv_left = pRootOfTree.left
# 尋找左子樹最大的節點與根節點相連
if conv_left:
while(conv_left.right):
conv_left = conv_left.right
conv_left.right,pRootOfTree.left = pRootOfTree,conv_left
# 遞迴處理右子樹
self.Convert(pRootOfTree.right)
conv_right = pRootOfTree.right
if conv_right:
while(conv_right.left):
conv_right = conv_right.left
conv_right.left,pRootOfTree.right = pRootOfTree,conv_right
# 找到調整後的頭結點
while(pRootOfTree.left):
pRootOfTree = pRootOfTree.left
return pRootOfTree
27. 字串的排列
題目:輸入一個字串,按字典序打印出該字串中字元的所有排列。例如輸入字串abc,則打印出由字元a,b,c所能排列出來的所有字串abc,acb,bac,bca,cab和cba。(輸入描述:輸入一個字串,長度不超過9(可能有字元重複),字元只包括大小寫字母。)
解題思路:遞迴的思路,序列分成兩部分,第一個字元是一部分,之後的所有字元是一部分。我只要求出第二部分所有的排列加上第一個部分就是以第一部分開頭的所有序列。將第一部分和後面每個字元調換位置,再採用同樣的處理方式就可以得到其他字元開頭的所有排列。
程式碼如下:
# -*- coding:utf-8 -*-
class Solution:
def Permutation(self, ss):
# write code here
if not ss:
return []
if len(ss) == 1:
return ss
result = []
list_ss = list(ss)
for i in list_ss:
temp = list_ss[:]
temp.remove(i)
for j in self.Permutation(''.join(temp)):
result.append(i+j)
return sorted(list(set(result)))