数据结构与算法分类练习--树 图

生产力专家说,突破是通过“非线性”来思考的,就是计算树中最重要的非线性数据结构之一。树结构确实是数据组织中的一个突破,因为它们允许我们实现比使用线性数据结构(如基于数组的列表或链表)快很多的算法。树的主要的用途是用来提高查找效率,如二叉排序树、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 #.

  1. First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
  2. Second node is labeled as 1. Connect node 1 to node 2.
  3. 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])

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值