生产力专家说,突破是通过“非线性”来思考的,树就是计算树中最重要的非线性数据结构之一。树结构确实是数据组织中的一个突破,因为它们允许我们实现比使用线性数据结构(如基于数组的列表或链表)快很多的算法。树的主要的用途是用来提高查找效率,如二叉排序树、FP-树。另外可以用来提高编码效率,如哈弗曼树。 树也为数据提供了自然的组织,因此在文件系统、图形用户界面、数据库、网站和其他计算机系统中已经成为无处不在的结构。
树的遍历主要有两种,一种是深度优先遍历,像前序、中序、后序;另一种是广度优先遍历,像层次遍历。在树结构中两者的区别还不是非常明显,但从树扩展到有向图,到无向图的时候,深度优先搜索和广度优先搜索的效率和作用还是有很大不同的。 深度优先一般用递归,广度优先一般用队列。一般情况下能用递归实现的算法大部分也能用堆栈来实现。
图是一组对象通过链接连接的一组对象的图形表示。图上的基本操作有显示图形顶点/边缘,添加一个定点/边缘,创建一个图。可以使用python字典数据类型轻松呈现图。 我们将顶点表示为字典的关键字,顶点之间的连接也称为边界,作为字典中的值。
Flatten Binary Tree to Linked List(树与链表的转换)
Given a binary tree, flatten it to a linked list in-place. For example, Given
1
/ \
2 5
/ \ \
3 4 6
The flattened tree should look like:
1-2-3-4-5-6
这道题要求把二叉树展开成链表,根据展开后链表的顺序可以看出是使用先序遍历,只要是数的遍历就有递归和非递归的两种方法来求解。首先来看递归的方法,先利用DFS的策略找到最左子节点,然后回到其父节点,把其父节点和右子节点断开,将原左子结点连到父节点的右子节点上,然后再把原右子节点连到新右子节点的右子节点上,然后再回到上一父节点做相同操作。
变化过程如下:
1
/ \
2 5
\ \
3 6
\
4
=>1-2-3-4-5-6
class Solution:
def flatten(self, root):
"""
:type root: TreeNode
:rtype: void Do not return anything, modify root in-place instead.
"""
if root:
if root.left:
self.flatten(root.left)
if root.right:
self.flatten(root.right)
tmp = root.right
root.right = root.left
root.left = None
while root.right:
root = root.right
root.right = tmp
else:
return
非迭代的方法是从根节点开始出发,先检测其左子结点是否存在,如存在则将根节点和其右子节点断开,将左子结点及其后面所有结构一起连到原右子节点的位置,把原右子节点连到元左子结点最后面的右子节点之后。
class Solution:
def flatten(self, root):
while root:
if root.left:
tmp = root.left
while (tmp.right):
tmp = tmp.right
tmp.right = root.right
root.right = root.left
root.left = None
root = root.right
Lowest Common Ancestor of a Binary Tree(最近的公共祖先)
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
Given the following binary tree: root = [3,5,1,6,2,0,8,null,null,7,4]
Example :
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 Output: 3
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 Output: 5
首先判断root是否为公共祖先,不是的话,则递归到左右节点。如果不是二叉树。
如果是二叉查找树,从根节点开始比较,如果,如果当前节点比这两个节点都大,则最低的公共父节点一定在左子树中,那么遍历左子树节点。如果当前节点比两个节点小,那么遍历右子节点。这样,找到的第一个值在二者之间的节点就是公共的最低的祖先。
如果不是二叉树,找出这两个节点的路径,并存在链表中,则这两个链表的交点就是最低公共祖先。
class Solution:
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if root in (None, p, q): return root
left, right = (self.lowestCommonAncestor(kid, p, q)
for kid in (root.left, root.right))
return root if left and right else left or right
Binary Tree Right Side View(树的层次遍历)
Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.For example:
Given the following binary tree, You should return [1, 3, 4].
遍历每层的节点时,先把当前层最后一个节点值存到结果view中,然后把下一层的节点都存入到列表level里。
def rightSideView(self, root):
view = []
if root:
level = [root]
while level:
view += level[-1].val,
level = [kid for node in level for kid in (node.left, node.right) if kid]
return view
Populating Next Right Pointers in Each Node II(树的改造)
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.
难点在于不是满二叉树就没办法直接连接,可以用一个链表记录下一层的节点。
class Solution:
def connect(self, root):
if not root:
return
dummy = TreeLinkNode()
# cur是下一层的节点的链表
cur = dummy
while root: #root是本层节点
if root.left:
cur.next = root.left
cur = cur.next
if root.right:
cur.next = root.right
cur = cur.next
# 向后遍历本层节点
root = root.next
# 本层遍历完毕
if not root:
# 下移一层
root = dummy.next
# 还原下一层
dummy.next = None
cur = dummy
Clone Graph(图的复制)
Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.
Nodes are labeled uniquely.
We use # as a separator for each node, and , as a separator for node label and each neighbor of the node.
As an example, consider the serialized graph {0,1,2#1,2#2,2}.
The graph has a total of three nodes, and therefore contains three parts as separated by #.
- First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
- Second node is labeled as 1. Connect node 1 to node 2.
- Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.
Visually, the graph looks like the following:
这道题考察对图的遍历和利用HashMap拷贝的方法。 对图的遍历就是两个经典的方法DFS和BFS。BFS经常用Queue实现,DFS经常用递归实现(可改为栈实现)。拷贝方法是用用HashMap,key存原始值,value存copy的值,用DFS,BFS方法遍历帮助拷贝neighbors的值。
# BFS
def cloneGraph(self, node):
if not node:
return
nodeCopy = UndirectedGraphNode(node.label)
dic = {node: nodeCopy}
queue = collections.deque([node])
while queue:
node = queue.popleft()
for neighbor in node.neighbors:
if neighbor not in dic: # neighbor is not visited
neighborCopy = UndirectedGraphNode(neighbor.label)
dic[neighbor] = neighborCopy
dic[node].neighbors.append(neighborCopy)
queue.append(neighbor)
else:
dic[node].neighbors.append(dic[neighbor])
return nodeCopy
# DFS iteratively
def cloneGraph1(self, node):
if not node:
return
nodeCopy = UndirectedGraphNode(node.label)
dic = {node: nodeCopy}
stack = [node]
while stack:
node = stack.pop()
for neighbor in node.neighbors:
if neighbor not in dic:
neighborCopy = UndirectedGraphNode(neighbor.label)
dic[neighbor] = neighborCopy
dic[node].neighbors.append(neighborCopy)
stack.append(neighbor)
else:
dic[node].neighbors.append(dic[neighbor])
return nodeCopy
# DFS recursively
def cloneGraph2(self, node):
if not node:
return
nodeCopy = UndirectedGraphNode(node.label)
dic = {node: nodeCopy}
self.dfs(node, dic)
return nodeCopy
def dfs(self, node, dic):
for neighbor in node.neighbors:
if neighbor not in dic:
neighborCopy = UndirectedGraphNode(neighbor.label)
dic[neighbor] = neighborCopy
dic[node].neighbors.append(neighborCopy)
self.dfs(neighbor, dic)
else:
dic[node].neighbors.append(dic[neighbor])