算法(13)-leetcode-explore-learn-数据结构-链表小结

leetcode-explore-learn-数据结构-链表5

  • 1.小结
  • 2.例题
    • 2.1合并两个有序链表
      • 思路1:迭代
      • 思路2:递归
    • 2.2 两数相加
    • 2.3 扁平化多级双向链表
    • 2.4 复制带随机指针的链表
    • 2.5 旋转链表

本系列博文为leetcode-explore-learn子栏目学习笔记,如有不详之处,请参考leetcode官网:https://leetcode-cn.com/explore/learn/card/linked-list/

所有例题的编程语言为python

1.小结

单链表双链表的相同点:
1.无法在常量时间内随意访问数据
2.能够在o(1)时间内,在给定节点之后完成新节点的添加
3.能够在o(1)的时间内,删除链表的第一节点

区别:删除给定节点
单链表无法由给定节点获取前一个节点,因此在删除给定节点之前必须花费o(n)的时间来找出前一个节点
双链表中,可以使用“prev”字段获取前一个节点,因此能在o(1)的时间内=删除给定节点。

链表的主要优势:删除添加节点十分方便

2.例题

2.1合并两个有序链表

leetcode 21
将两个升序链表合并为一个新的升序链表并返回。

思路1:迭代

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        if l1==None or l2==None:
            return l1 if l1 else l2
        res_head=ListNode(0)
        res_curr_node=res_head
        while(l1 and l2):
            if l1.val<=l2.val:
                l1_next_node=l1.next
                l1.next=None
                res_curr_node.next=l1
                l1=l1_next_node
                res_curr_node=res_curr_node.next
            else:
                l2_next_node=l2.next
                l2.next=None
                res_curr_node.next=l2
                l2=l2_next_node
                res_curr_node=res_curr_node.next
        if l1:
            res_curr_node.next=l1
        if l2:
            res_curr_node.next=l2
        return res_head.next    

精简写法:


class Solution(object):
    def mergeTwoLists(self, l1, l2):
        if l1==None or l2==None:
            return l1 if l1 else l2
        res_head=ListNode(0)
        res_curr_node=res_head
        while(l1 and l2):
            if l1.val<=l2.val:
                res_curr_node.next=l1
                l1=l1.next
            else:
                res_curr_node.next=l2
                l2=l2.next
            res_curr_node=res_curr_node.next
        res_curr_node.next=l1 if l1 else l2   
        return res_head.next

思路2:递归

递归最重要的是递归出口


class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        if l1==None:
            return l2
        elif l2==None:
            return l1
        elif l1.val<=l2.val:
            l1.next=self.mergeTwoLists(l1.next,l2)
            return l1       # 由顶向下时,已经决定了第一个节点是谁,递归得到最底端时,直接将长链表的剩余部分链接回已经排序好的链表中。
        else:
            l2.next=self.mergeTwoLists(l1,l2.next)
            return l2

2.2 两数相加

给出两个 非空的链表 来表示两个 非负整数。其中,他们各自的位数是按照 逆序 方式存储的,并且每个节点只能存储一位 数字。

如果,我们将这两个数相加起来,返回一个新的链表表示它们的和

可以假设除了数字0之外,两个数字都不会以0开头

思路:逆序存储简化了问题,直接遍历两个链表的对位元素,相加后维护一个进位标志flag。
冗余

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        flag=0
        res_head=ListNode(0)
        res_cur_node=res_head
        while(l1 and l2):
            sum_num=l1.val+l2.val+flag
            flag=sum_num//10
            res_cur_node.next=ListNode(sum_num%10)
            res_cur_node=res_cur_node.next
            l1=l1.next
            l2=l2.next
        while(l1):
            #print(l1.val)
            sum_num=l1.val+flag
            flag=sum_num//10
            res_cur_node.next=ListNode(sum_num%10)
            res_cur_node=res_cur_node.next
            l1=l1.next
        while(l2):
            #print(l2.val)
            sum_num=l2.val+flag
            flag=sum_num//10
            res_cur_node.next=ListNode(sum_num%10)
            res_cur_node=res_cur_node.next
            l2=l2.next
        if flag:
            res_cur_node.next=ListNode(flag)
        return res_head.next

精简

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        carry=0
        res_head=ListNode(0)
        res_cur_node=res_head
        
        while(l1 or l2 or carry):
            if l1:
                carry+=l1.val
                l1=l1.next
            if l2:
                carry+=l2.val
                l2=l2.next
            carry,val=divmod(carry,10)
            res_cur_node.next=ListNode(val)
            res_cur_node=res_cur_node.next
        return res_head.next

2.3 扁平化多级双向链表

多级双向链表中,除了指向下一个节点和前一个节点的指针外,它还有一个子链表指针,可能指单独的双向链表。这些子链表也可能有一个或多个自己的子项。一次类推,生成多级数据结构 。

给出位于链表第一级的头节点,请扁平化链表,使所有的节点出现子啊单级双链表中。

直觉:采用递归的方式求解,可每个节点该如何处理呢:
新建一个res_head,res_head下一个节点应该为curr_node.child,curr_node.next,

官方思路递归:扁平化处理可以看作对二叉树进行先序遍历,child作为二叉树中的指向座子树的left指针,next可以看作二叉树中的right指针。
难点:深度优先遍历到了末尾该如何让扁平化操作?
flatten_dfs(prev, curr)接收两个指针作为函数参数,并返回扁平化链表中的尾部指针。curr指向需要扁平化的子列表,prev指向curr元素的前一个元素。
在dfs函数中首先要建立prev和curr的双向链接
然后对左子树进行操作dfs(curr,cuur.child)其将返回扁平化子列表的尾部元素,再调用dfs(tail,curr.next)对右子树进行操作。注意点:
1.在进行左子树操作时,需要先保存curr.next的信息
2.在扁平化child指针所指向的列表之后,应该删除child指针

"""
# Definition for a Node.
class Node(object):
    def __init__(self, val, prev, next, child):
        self.val = val
        self.prev = prev
        self.next = next
        self.child = child
"""

class Solution(object):
    def flatten(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if head==None:
            return head
        dummy_head=Node(None,None,head,None)
        self.flatten_dfs(dummy_head,head)
        dummy_head.next.prev=None
        return dummy_head.next
        
    def flatten_dfs(self,prev,curr):
        if curr==None:
            return prev
        curr.prev=prev
        prev.next=curr
        tempNext=curr.next
        tail=self.flatten_dfs(curr,curr.child)
        curr.child=None
        return self.flatten_dfs(tail,tempNext)

官方思路迭代:

2.4 复制带随机指针的链表

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点
编程实现这个链表的深度拷贝

难点:新建节点,很简单,随机节点该如何指向?

先遍历一遍形成一个单链表,再遍历一遍原链表找到带随机指针的节点,在单链表中找到对应的节点,难的是对应节点怎么找,因为,指针存的是地址,在新链表中并不知道该指向哪一个?解决方案–维护一张map映射表,每个节点对应的新节点,因此便可以找到对应的节点。

"""
# Definition for a Node.
class Node:
    def __init__(self, x, next=None, random=None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution(object):

    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if head==None:
            return None
        visitedHash={}
        res_head=Node(0)
        res_cur_node=res_head
        cur_node=head
        while(cur_node):
            node=Node(cur_node.val,None,None)
            res_cur_node.next=node
            visitedHash[cur_node]=node
            res_cur_node=res_cur_node.next
            cur_node=cur_node.next
        cur_node=head
        res_cur_node=res_head.next
        while(cur_node):
            if cur_node.random:
                node=visitedHash[cur_node.random]
                res_cur_node.random=node
            cur_node=cur_node.next
            res_cur_node=res_cur_node.next
        return res_head.next

参考官网给题解:递归
带随机指针的链表可以看作一张图,要做的是遍历整张图,并拷贝它。拷贝的意思是每当遇到一个新的未访问过的节点,需创造新的节点。在回溯的过程中记录访问过的节点,否则因为随机指针存在,会导致死循环。

"""
# Definition for a Node.
class Node:
    def __init__(self, x, next=None, random=None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution(object):
    def __init__(self):
        self.visitedHash={}
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if head==None:
            return None
        if head in self.visitedHash:
            return self.visitedHash[head]
        node=Node(head.val,None,None)
        self.visitedHash[head]=node
        node.next=self.copyRandomList(head.next)
        node.random=self.copyRandomList(head.random)
        
        return node

2.5 旋转链表

给定一个链表,将链表的每个节点向右移动k个位置,其中k是非负数。

思路1:和旋转数组一样,每次右移一位,移动K次即可

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def rotateRight(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if head==None or head.next==None:
            return head
        l=0
        cur_node=head
        while(cur_node):
            l+=1
            cur_node=cur_node.next
        k=k%l
        def rotate_one_step(head):
            pre_pre_node=None
            pre_node=head
            cur_node=head.next
            while(cur_node):
                pre_pre_node=pre_node
                pre_node=cur_node
                cur_node=cur_node.next
            pre_pre_node.next=None
            pre_node.next=head
            return pre_node
        while(k>0):
            head=rotate_one_step(head)
            k-=1
        return head         

思路2: 移动k次,就是将倒数的k个节点相对顺序不变的移动到链表的头部。所以从链表的head开始,往后找到L-k个节点,将后半部分移动到链表的前半部分。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def rotateRight(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if head==None or head.next==None:
            return head
        l=0
        cur_node=head
        while(cur_node):
            l+=1
            cur_node=cur_node.next
        k=k%l
        # 首尾相连
        pre_node=None
        cur_node=head
        while(cur_node):
            pre_node=cur_node
            cur_node=cur_node.next
        pre_node.next=head
        # 寻找切割点
        cur_node=head
        for _ in range(1,l-k): #[0,l-k-1]
            cur_node=cur_node.next
        # 确定的新的头部
        new_head=cur_node.next
        cur_node.next=None
        return new_head

你可能感兴趣的:(算法)