1. 程式人生 > 實用技巧 >LeetCode --- 不同的二叉搜尋樹 解題思路與疑惑

LeetCode --- 不同的二叉搜尋樹 解題思路與疑惑

題目:

提示: 0 <= n <= 8

BST:

二叉搜尋樹(Binary Search Tree,BST),又稱為二叉排序樹(Binary Sort Tree,BST),具有以下性質:

  1. 若左子樹不為空,則左子樹上所有節點的值均小於等於根節點值。
  2. 若右子樹不為空,則右子樹上所有節點的值均大於等於根節點值。
  3. 左、右子樹也分別是BST。

BST資料結構:

下面二叉搜尋樹的資料結構:

class TreeNode:
	def __init__(self, val=0):
		self.val = val
		self.left = None
		self.right = None

BST基本操作的程式設計(很多操作此程式用不到):

class OperationTree:
	# 二叉搜尋樹插入操作
	def insert(self, root, val): 
		if root == None:
			root = TreeNode(val)
		elif val < root.val:
			root.left = self.insert(root.left, val)
		elif val > root.val:
			root.right = self.insert(root.right, val)
		return root
	# 查詢二叉搜尋樹是否含有特定數字
	def query(self, root, val):
		if root == None:
			return False
		elif root.val == val:
			return True
		elif val < root.val:
			return self.query(root.left, val)
		elif val > root.val:
			return self.query(root.right, val)
	# 尋找BST最小值
	def findMin(self, root):
		if root.left:
			return self.findMin(root.left)
		else:
			return root
	# 尋找BST最大值
	def findMax(self, root):
		if root.right:
			return self.findMax(root.right)
		else:
			return root
	# 刪除 BST的某個節點
	def delNode(self, root, val):
		if root == None: #被刪除的節點不存在,不進行任何操作返回
			#print("被刪除的節點:%d不存在!"%val)
			return
		# 左子樹遞迴刪除目標節點
		if val < root.val:
			root.left = self.delNode(root.left, val)
		# 右子樹遞迴刪除目標節點
		elif val > root.val:
			root.right = self.delNode(root.right, val)
		else:
			# 既有左子樹,又有右子樹,則需找到右子樹中的最小值節點
			if root.left and root.right:
				temp = self.findMin(root.right)
				root.val = temp.val
				root.right = self.delNode(root.right, temp.val)
			# 左右子樹都為空
			elif root.right == None and root.left == None:
				root = None
			# 只有左子樹
			elif root.right == None:
				root = root.left
			# 只有右子樹
			elif root.left == None:
				root = root.right
		return root
	# 中序遍歷、列印二叉搜尋樹:列印的是一個有序數列
	def printTree(self, root):
		if root == None:
			return
		self.printTree(root.left)
		print(root.val, end = ' ')
		self.printTree(root.right)

從題目可以看到列印方式是層次優先的遍歷列印。下面是按層次遍歷列印二叉樹(包含了部分null節點的輸出)函式:

# 層次優先遍歷列印二叉樹
def LevelTree(self, root):
	result_list = []
	# 如果根節點為空,則返回空列表
	if root is None:
		return
	# 模擬一個佇列儲存節點
	stack = []
	# 首先將根節點入隊
	stack.append(root)
	# 列表為空時,迴圈終止
	while len(stack) != 0:
		length = len(stack)
		for i in range(length):
			# 判斷是否只剩下None
			if set(stack) == {None}:
				stack = []
				break
			# 將同層節點依次出隊
			top = stack.pop(0)
			if top is None:
				result_list.append('null')
				# print('null', end = ' ')
				continue
			if top.left	is None:
				stack.append(None)
			elif top.left is not None:
				# 非空左孩子入隊
				stack.append(top.left)
			if top.right is None:
				stack.append(None)
			elif top.right is not None:
				# 非空右孩子入隊
				stack.append(top.right)
			result_list.append(top.val)
			# print(top.val, end = ' ')
	return result_list # 二叉樹水平遍歷的結構,儲存在列表中返回

可以寫個簡單的指令碼測試能否輸出正確的樹結構:

        List = [1,2,3]
	root = None
	op = OperationTree()
	for val in List:
		root = op.insert(root, val)
	print("按層次列印二叉搜尋樹:", end = '  ')
	print(op.levelOrder(root))

主程式分析

後面寫的是主程式邏輯結構,思路是根據輸入的n值生成 n! 種[1,2,3,...,n]的不同的排序存入陣列,然後對陣列內每一個元素構建二叉樹並用LevelTree函式返回其結構,判斷這個結構是否已經存在過,不存在就擴充套件,存在就跳過(過濾重複輸出的結構)。
這句話的意思可以這樣理解:
n=3的情況下,[2,1,3]、[2,3,1]是二叉搜尋樹兩個不同的生成順序,但是生成的BST一樣的:

在n較大的時候,這種差別體現的尤其明顯。因此需要過濾判斷。
完整程式碼:

#coding=utf-8
import itertools

def permutation(li):
    return list( itertools.permutations(li) )

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class OperationTree:
	# 二叉搜尋樹插入操作
	def insert(self, root, val): 
		if root == None:
			root = TreeNode(val)
		elif val < root.val:
			root.left = self.insert(root.left, val)
		elif val > root.val:
			root.right = self.insert(root.right, val)
		return root

	# 層次優先遍歷列印二叉樹
	def LevelTree(self, root):
		result_list = []
		# 如果根節點為空,則返回空列表
		if root is None:
			return
		# 模擬一個佇列儲存節點
		stack = []
		# 首先將根節點入隊
		stack.append(root)
		# 列表為空時,迴圈終止
		while len(stack) != 0:
			length = len(stack)
			for i in range(length):
				# 判斷是否只剩下None
				if set(stack) == {None}:
					stack = []
					break
				# 將同層節點依次出隊
				top = stack.pop(0)
				if top is None:
					result_list.append('null')
					# print('null', end = ' ')
					continue
				if top.left	is None:
					stack.append(None)
				elif top.left is not None:
					# 非空左孩子入隊
					stack.append(top.left)
				if top.right is None:
					stack.append(None)
				elif top.right is not None:
					# 非空右孩子入隊
					stack.append(top.right)
				result_list.append(top.val)
				# print(top.val, end = ' ')
		return result_list

class Solution:
    def generateTrees(self, n):
        op = OperationTree()
        src_List = [i for i in range(1,n+1)]
        tree_list = permutation(src_List) # n!個元素(輸入結構)
        tree_print_list = []  # 輸出結構
        for tree in tree_list:
            root = None
            for val in tree:
                root = op.insert(root, val)
            tree_print = op.LevelTree(root)
            if tree_print not in tree_print_list:
                tree_print_list.append(tree_print)
            del root
        return tree_print_list

if __name__ == "__main__":
    n = int(input())
    X = Solution()
    print(X.generateTrees(n))

測試n=3:

測試n=4:

看起來應該是對的,可是提交leetcode遇到了麻煩:

輸出為什麼是空的而stdout是理想的輸出。。。
其次'null'與null有區別嗎。。。
最最最難受的是提交檢驗失敗了。