两道链表展开的题目

概述

114. 二叉树展开为链表

430. 扁平化多级双向链表

114 这道题目要求我们把一颗二叉树展开成链表
430 这道题目要求我们把复杂的链表展开

114题解已经发布在这篇博客,这里不再赘述了

下面写430的题解

先序递归

仔细观察这个链表结构,发现我们按照先序递归的方式来访问这个复杂链表,刚好就能得到展开后的结果,那么可以写出代码:

class Solution:
    def flatten(self, head: 'Node') -> 'Node':
        if not head:
            return head
            
        self.ans = Node(None,None,None,None)
        self.cur = self.ans
        self.helper(head)

        # 处理头结点的指针,按要求返回
        newhead = self.ans.next  
        newhead.prev = None

        return newhead 

    def helper(self, head):
        if not head:
            return 
        # 按照cur,child,next的顺序遍历结点

        # 处理next指针,必须先保存然后递归,不然会在结束子链表展开时进入循环
        next = head.next

        # 处理当前指针
        self.cur.next = head
        head.prev = self.cur

        # 处理孩子指针
        child = head.child 
        head.child = None

        # 更新当前结点
        self.cur = head

        # 访问child结点、next结点 
        self.helper(child)
        self.helper(next)

上面的几个地方
首先我们用self.ans来作为临时头节点方便操作
然后self.cur用来指向每次递归遍历访问的结点
在递归中,我们要把next指针和child指针摘下来,然后处理self.cur和head的关系,再对刚刚保存的next和child进行调用
最后返回的时候,要把返回节点的前一个结点置空,这样才能通过样例

在这里插入图片描述

fancy递归

当然读者可能觉得上面的递归不够fancy,那么我们可以更改一下代码,让它看起来高端一点
由于接下来的递归很fancy,所以我把它叫做fancy递归

首先我们检查这个数据结构:prev、next、child三个指针
然后题目要求我们把多级链表展开成单级链表,我们检查一下链表顺序可以发现按照以下规则递归处理即可:

  1. 空结点 返回
  2. 处理当前节点
  3. 处理孩子链表
  4. 处理后继链表

关键是处理完孩子链表后,我们要把孩子链表拼接在后继结点前面
这也好办,只需要处理孩子链表的时候,同时返回孩子链表的首尾链表结点即可
又因为处理孩子、后继链表两个代码一致,所以我们可以复用一段处理的代码,这段代码的功能是:把一条多级链表展开,同时返回展开后的尾结点

那么可以写出代码

class Solution:
    def flatten(self, head: 'Node') -> 'Node':
        if not head:
            return head
        dh = Node(None,None,None,None)
        cur = dh
        
        self.helper(cur,head)

        dh.next.prev = None
        return dh.next


    # 给定cur和head,展开以head为首的链表,并且返回展开后的尾指针
    def helper(self,cur,head):
        if not head:
            return cur # 注意返回的是cur
        
        cur.next = head
        head.prev = cur

        next = head.next
        child = head.child
        head.child = None

        tail = self.helper(head,child)
        return self.helper(tail,next)

上面的代码看着就很fancy,写起来也很fancy,特别是tail那一行,如果没经过思考是想不到怎么取到最后一个指针的(因为每次传入的都是head和head的下级(孩子或后继)指针,最后临界的时候,head一定是空,那么就会返回cur,而cur表示的就是前一个节点,从全局上来说就是最后一个结点)

需要注意的是最好先保存head的next和child再传入,不然会出现一些循环错误

迭代

有先序递归就有先序迭代,先序迭代一定会用到栈,我们复习一下用栈来怎么做二叉树的先序遍历:

  1. 弹出栈顶元素,直接访问
  2. 如果右孩子不为空,入栈
  3. 如果左孩子不为空,入栈

放到这里就是:

  1. 弹出栈顶元素,直接访问
  2. 如果后继节点不为空,入栈
  3. 如果孩子不为空,入栈

和普通的迭代有点不同的是这道题要处理一下指针关系

class Solution:
    def flatten(self, head: 'Node') -> 'Node':
        if not head:
            return head
        stack = [head]
        dh = Node(None,None,None,None) 
        pre = dh
        
        while stack:
            node = stack.pop()
            pre.next = node
            node.prev = pre
            pre = node
            
            if node.next:
                stack.append(node.next)
            if node.child:
                stack.append(node.child)
                node.child = None
            
            
        dh.next.prev = None

        return dh.next

在这里插入图片描述
搞定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值