day 2:数据结果之顺序表和链表

1.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

  • 示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

思路:

  • 想法

    我们可以如下递归地定义在两个链表里的 merge 操作(忽略边界情况,比如空链表等):

{  list1  [ 0 ] +  merge (list1  [ 1 : ] ,  list2  )  list1  [ 0 ] <  list  2 [ 0 ]  list2  [ 0 ] +  merge (list1, list  2 [ 1 : ] )  otherwise  \left\{\begin{array}{ll}{ \text { list1 }[0]+\text { merge (list1 }[1:], \text { list2 })} & {\text { list1 }[0]<\text { list } 2[0]} \\ { \text { list2 }[0]+\text { merge (list1, list } 2[1:])} & {\text { otherwise }}\end{array}\right. { list1 [0]+ merge (list1 [1:], list2 ) list2 [0]+ merge (list1, list 2[1:]) list1 [0]< list 2[0] otherwise 

  • 也就是说,两个链表头部较小的一个与剩下元素的 merge 操作结果合并。

  • 算法
    我们直接将以上递归过程建模,首先考虑边界情况。
    特殊的,如果 l1 或者 l2 一开始就是 null ,那么没有任何操作需要合并,所以我们只需要返回非空链表。否则,我们要判断 l1 和 l2 哪一个的头 元素更小,然后递归地决定下一个添加到结果里的值。如果两个链表都是空的,那么过程终止,所以递归过程最终一定会终止。


在leecode中使用递归,因为涉及到类和方法,所以需要用到self作为调用示例,可参考:https://www.jianshu.com/p/814d65a9c404

def mergeTwoLists(self, l1, l2):
    if (l1 is None)and(l2 is None):
        print('两个链表均为空链表')
    elif l1 is None:
        return l2
    elif l2 is 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

用时:40ms,内存:11.8mb

至于如何创建链表,还需要花点时间来实现,后面再补吧

2. 删除链表的倒数第N个节点

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

方法一:两次遍历算法
思路
我们注意到这个问题可以容易地简化成另一个问题:删除从列表开头数起的第 (L−n+1)个结点,其中 L 是列表的长度。只要我们找到列表的长度 L,这个问题就很容易解决。
算法
首先我们将添加一个哑结点作为辅助,该结点位于列表头部。哑结点用来简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部。在第一次遍历中,我们找出列表的长度 L。然后设置一个指向哑结点的指针,并移动它遍历列表,直至它到达第 (L−n)个结点那里。我们把第 (L−n) 个结点的 next 指针重新链接至第 (L−n+2) 个结点,完成这个算法。

步骤:先遍历一次链表,求出链表的总长度。第二次遍历的时候,根据总长度k的值-n,就算出需要再遍历多少个节点,找到要删除的节点的前一个节点x。然后将x节点的next指针指向下下一个节点就可以删除节点了。

方法二:一次遍历算法
算法
当链表总长度是k时,如果要删除倒数第n个节点(假设n小于k),那么首先要找到第k-n个节点,k-n这个节点就是要删除的节点的前一个节点。
当找到k-n这个节点就好办了,直接将k-n的next指针指向下下一个节点即可。

上述算法可以优化为只使用一次遍历。我们可以使用两个指针而不是一个指针。第一个指针从列表的开头向前移动 n+1 步,而第二个指针将从列表的开头出发。现在,这两个指针被 n 个结点分开。我们通过同时移动两个指针向前来保持这个恒定的间隔,直到第一个指针到达最后一个结点。此时第二个指针将指向从最后一个结点数起的第 n 个结点。我们重新链接第二个指针所引用的结点的 next 指针指向该结点的下下个结点。

#法1:
def removeNthFromEnd1(self,head,n):
    #如果链表为空或者n无意义
    if not head or n<0:
        return head
# 在链表头前面增加一个特殊节点,方便边界处理
    p = ListNode(-1)
    p.next,a,b,k = head,p,p,0
    # 第一次遍历,计算链表总长度
    while a.next:
        a,k = a.next,k+1
    # 如果链表总长度小于n,那就直接返回
    if k<n:
        return head
    # 计算第二次遍历多少个节点
    num = k-n
    # 第二次遍历,找到要删除节点的前一个节点
    while num>0:
        b,num = b.next,num-1
    # 删除节点,并返回
    b.next = b.next.next
    return p.next

#法2:双指针
def removeNthFromEnd2(self,head,n):
    # 在链表头前面增加一个特殊节点,方便边界处理
    p = ListNode(-1)
    p.next,a,b,k = head,p,p,0
    # 第一个循环,b指针先往前走n步
    while n>0 and b:
        b,n = b.next,n-1
    # 假设整个链表长5,n是10,那么第一次遍历完后b就等用于空了
    # 于是后面的判断就不用做了,直接返回
    if not b:
        return head
    # 第二次,b指针走到链表最后,a指针也跟着走
    # 当遍历结束时,a指针就指向要删除的节点的前一个位置
    while b.next:
        a,b = a.next,b.next
    # 删除节点并返回	
    a.next = a.next.next
    return p.next	


    

leecode结果:

执行用时 :24 ms ,内存消耗 :11.9 MB

3. 旋转链表

https://leetcode-cn.com/problems/rotate-list/

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

思路

  • 关键点就是k,如果移动一次就相当于将倒数第一个元素1移到链表开头,如果移动两次就相当于将倒数第二和倒数第一个元素2和1移动到链表开头。
    • 所以这题的关键是要找到倒数第k个节点。
    • 在这之前我们还需要处理一些细节:
    • 当k等于链表总长时,就相当于移动0次,也就是啥也不用做直接返回就行了。如果k>链表总长,比如k=7时,结果仍然是5->4->3->2->1。
    • 所以我们要先对k取模,取模后的k范围应该是:
    • 1<=k<=链表总长-1 (不能等于总长,等于总长就直接返回链表了
    • 找到倒数第k个节点4,然后将链表的尾部指向开头
    • 再将节点4前面一个3的next指向null,一定要断开,否则链表就成环状啦
    • 最后返回新的头结点4就可以了
#使用双指针
def removekrightFromEnd1(self,head,k):
    if not head or k<=0:
        return head
    # 创建一个特殊节点,快指针,慢指针,统计节点个数的cur
    p = ListNode(-1)
    cur,n,low,fast,p.next = head,0,p,p,head
# 统计链表个数n
    while cur:               #如果cur指向null,则跳出循环
        cur,n = cur.next,n+1
    #边界条件1:
    if n==0 or k%n==0:
        return head
    #边界条件2:
         #k可能大于n,需要取模
    a=k%n
    while fast.next and a>0:
        fast,a=fast.next,a-1
# 快指针,慢指针一起移动,找到需要切割的点
    while fast.next:
        low,fast = low.next,fast.next
    # 改变链表的指向关系,注意这里的步骤不要乱了
    # 先让fast节点指向head(也就是p.next)
    # 再是head(也就是p.next)指向low的下一个节点
    # 这两步如果弄反了就会出现节点丢失了
    # 最后不要忘记将low.next置空
    
    fast.next,p.next,low.next=head,low.next,None    #慢节点所在的点就是倒数第K个点的前面
    return p.next

    

leecode 结果

用时24ms,内存:11.7MB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值