树一般有两种解题方式:
递归:确定递归条件和终止条件
迭代:利用队列或栈这两种数据结构实现,一般层次遍历用队列实现,前序遍历、中序遍历、后序遍历用栈实现
稍微复杂的问题般都会结合DFS 和回溯
二叉树
特殊二叉树包括:
完全⼆叉树(Complete Binary Tree):每⼀层都是紧凑靠左排列,计算节点数的时间复杂度是$O(logN*logN)$
满⼆叉树(Perfect Binary Tree):每层都是是满的,节点数 = 2^树高 - 1,计算节点数的时间复杂度是$O(logN)$
Full Binary Tree:⼆叉树的所有节点要么没有孩⼦节点,要么有两个孩⼦节点
二叉树的实现 以下代码实现了二叉树的初始化及增加节点函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Node (object ): """节点类""" def __init__ (self, val=-1 , left=None , right=None ): self.val = val self.left = left self.right = right class Tree (object ): """树类""" def __init__ (self ): self.root = Node() self.myQueue = [] def add (self, val ): """为树添加节点""" node = Node(val) if self.root.val == -1 : self.root = node self.myQueue.append(self.root) else : treeNode = self.myQueue[0 ] if treeNode.left == None : treeNode.left = node self.myQueue.append(treeNode.left) else : treeNode.right = node self.myQueue.append(treeNode.right) self.myQueue.pop(0 )
二叉树的遍历 递归法 以下代码实现了二叉树的前序(跟左右)、中序(左跟右)、后序(左右跟)遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def front_recursive (self, root ): """利用递归实现树的先序遍历""" if root is None : return print (root.val, end=' ' ) self.front_recursive(root.left) self.front_recursive(root.right) def middle_recursive (self, root ): """利用递归实现树的中序遍历""" if root is None : return self.middle_recursive(root.left) print (root.val, end=' ' ) self.middle_recursive(root.right) def later_recursive (self, root ): """利用递归实现树的后序遍历""" if root is None : return self.later_recursive(root.left) self.later_recursive(root.right) print (root.val, end=' ' )
迭代法 以下代码实现了二叉树的前序、中序、后序、层次遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 def front_stack (self, root ): """利用堆栈实现树的先序遍历""" if root is None : return myStack = [] node = root while node or myStack: while node: print (node.val, end=' ' ) myStack.append(node) node = node.left node = myStack.pop() node = node.right def middle_stack (self, root ): """利用堆栈实现树的中序遍历""" if root is None : return myStack = [] node = root while node or myStack: while node: myStack.append(node) node = node.left node = myStack.pop() print (node.val, end=' ' ) node = node.right def later_stack (self, root ): """利用堆栈实现树的后序遍历""" if root is None : return myStack1 = [] myStack2 = [] node = root myStack1.append(node) while myStack1: node = myStack1.pop() if node.left: myStack1.append(node.left) if node.right: myStack1.append(node.right) myStack2.append(node) while myStack2: print (myStack2.pop().val, end=' ' ) def level_queue (self, root ): """利用队列实现树的层次遍历""" l2 = [] cur_layer = [root] while cur_layer and root: l1, next_layer = [], [] for node in cur_layer: l1.append(node.val) if node.left: next_layer.append(node.left) if node.right: next_layer.append(node.right) cur_layer = next_layer l2.append(l1) return l2
Morris遍历 二叉树的递归和迭代遍历都需要借助栈,空间复杂度为$O(h)$,其中$h$指二叉树的最大高度。而Morris遍历的时间复杂度为$O(n)$,空间复杂度为$O(1)$。其遍历规则如下:
设当前遍历的节点为cur,若cur无左子树,则cur = cur.right
若cur有左子树,找到cur左子树的最右节点记为mostRight
若mostRight的右子树为空,则令mostRight.right = cur, cur = cur.left
若此时mostRight.right = cur
,则令mostRight.right = None, cur = cur.right
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def morris (self, root, order ): """实现Morris遍历""" if root is None : return cur = root while cur: if cur.left is None : print (cur.val, end=' ' ) cur = cur.right else : mostRight = cur.left while mostRight.right is not None and mostRight.right != cur: mostRight = mostRight.right if mostRight.right is None : mostRight.right = cur if order == 'front' : print (cur.val, end=' ' ) cur = cur.left elif mostRight.right == cur: if order == 'middle' : print (cur.val, end=' ' ) mostRight.right = None cur = cur.right
Morris遍历的后序遍历有点麻烦,具体可以看参考文献
测试及结果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 if __name__ == '__main__' : """主函数""" vals = range (10 ) tree = Tree() for val in vals: tree.add(val) print ('队列实现层次遍历:' ) print (tree.level_queue(tree.root)) print ('\n\n递归实现先序遍历:' ) tree.front_recursive(tree.root) print ('\n递归实现中序遍历:' ) tree.middle_recursive(tree.root) print ('\n递归实现后序遍历:' ) tree.later_recursive(tree.root) print ('\n\n堆栈实现先序遍历:' ) tree.front_stack(tree.root) print ('\n堆栈实现中序遍历:' ) tree.middle_stack(tree.root) print ('\n堆栈实现后序遍历:' ) tree.later_stack(tree.root) print ('\nMorris先序遍历:' ) tree.morris(tree.root, 'front' ) print ('\nMorris中序遍历:' ) tree.morris(tree.root, 'middle' )
二叉搜索树
简介:⼆叉搜索树(Binary Search Tree,BST)定义:⼀个⼆叉树中,任意节点的值要⼤于等于左⼦树所有节点的值,且要⼩于等于右边⼦树的所有节点的值。
性质:
每个节点中的值必须大于或等于其左子树中的任何值
每个节点中的值必须小于或等于其右子树中的任何值
二叉搜索树中序遍历 时,结果依此递增
N叉树 树的每个节点可以有两个以上的子节点,称为m阶的多叉树或m叉树
节点实现
1 2 3 4 class Node : def __init__ (self, val=None , children=None ): self.val = val self.children = children
前缀树(字典树) Trie,前缀树或字典树,是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。前缀树的节点可以是任意一个字符集中的字符,如对于都是小写字母的字符串,字符集就是’a’-‘z’;对于都是数字的字符串,字符集就是’0’-‘9’;对于二进制字符串,字符集就是0和1。前缀树有相当多的应用情景,例如自动补完和拼写检查。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 class Trie : """ 实现一个字符集为小写英文字母的前缀树 """ def __init__ (self ): """ Initialize data structure. """ self.children = [None ] * 26 self.isEnd = False def searchPrefix (self, prefix ): """ return None if prefix not in trie else the end of the prefix """ node = self for ch in prefix: ch = ord (ch) - ord ('a' ) if not node.children[ch]: return None node = node.children[ch] return node def insert (self, word: str ) -> None : """ Inserts a word into the trie. """ node = self for ch in word: ch = ord (ch) - ord ('a' ) if not node.children[ch]: node.children[ch] = Trie() node = node.children[ch] node.isEnd = True def search (self, word: str ) -> bool : """ Returns if the word is in the trie. """ node = self.searchPrefix(word) return node is not None and node.isEnd def startsWith (self, prefix: str ) -> bool : """ Returns if there is any word in the trie that starts with the given prefix. """ return self.searchPrefix(prefix) is not None if __name__ == '__main__' : trie = Trie() trie.insert("apple" ) print (trie.search("apple" )) print (trie.search("app" )) print (trie.startsWith("app" )) trie.insert("app" ) print (trie.search("app" ))
复杂度分析:
时间复杂度: 初始化为$O(1)$,其余操作为$O(|S|)$,$|S|$是每次插入或查询的字符串长度
空间复杂度: $O(|T|·\Sigma)$,其中$|T|$为所有插入字符串的长度之和,$\Sigma$为字符集的大小,代码中$\Sigma=26$
其他特殊结构
力扣指南 参考资料