python实现Leetcode链表题型全解(反转,合并,删除,环,公共节点,复制,相加,重复,回文,重排)更新ing

1.反转链表

输入一个链表,反转链表后,输出新链表的表头
主要的思想是用两个指针,其中newHead指向的是反转成功的链表的头部,currentHead指向的是还没有反转的链表的头部:
在这里插入图片描述
初始状态是newHead指向null,currentHead指向的是第一个元素,一直往后遍历直到newHead指向最后一个元素为止:在这里插入图片描述
下面展示的是其中某个时间点的指向细节:
在这里插入图片描述
注意:考虑空链表和链表里只有一个元素的情况。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        # 空链表{}
        if not pHead :
            return None 
        #只有一个元素表{1}
        if not pHead.next:
            return pHead
        #currenthead就是从第一个元素往后移动的原链表元素
        currenthead,newhead=pHead,None
        while currenthead:
        #以第一次循环为例子:{1,2,3,4,5}
        #(1)temp指向2
            temp=currenthead.next
        #(2)currenthead的next指向newhead,1指向none
            currenthead.next=newhead
        #(3)newhead现在指向到第一个元素1
            newhead=currenthead
        #(4)currenthead指向原来存储好的2的位置
            currenthead=temp
        return newhead
            

详细过程循环里图解:
在这里插入图片描述

2.合并2个有序链表

将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的,且合并后新链表依然有序。

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

思路1:
新建表头new,用一个指针(初始为new)往后走进行判断链表,判断大小确定当前下一个元素是什么.直到2个链表有1个为空,剩下不空的链表接在后面.

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

#
# 
# @param l1 ListNode类 
# @param l2 ListNode类 
# @return ListNode类
#
class Solution:
    def mergeTwoLists(self , l1 , l2 ):
    #若有一个为空的链表,则直接返回另一个有序链表
        if not l1:return l2
        if not l2:return l1
        new=ListNode(-1)
        l=new
        while l1 and l2:
            if l1.val>l2.val:
                l.next,l2=l2,l2.next
            else:
                l.next,l1=l1,l1.next
            l=l.next
        l.next=l1 if l1 else l2
        return new.next
        

在这里插入图片描述
思路2:
或者不新建表头,使用递归,将指针指向当前小的val的链表(假如l1.val<l2.val),再递归合并(l1.netx,l2).

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def mergeTwoLists(self , l1 , l2 ):
        # write code here
        if not l1:return l2
        if not l2:return l1
    #递归
        if l1.val<=l2.val:
            l=l1
            l.next=self.mergeTwoLists(l1.next,l2)         
        else:
            l=l2
            l.next=self.mergeTwoLists(l1, l2.next)
       return l
          

3. 删除链表节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

要考虑删除头节点情况

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        if head.val==val: return head.next
        pre,cur=head,head.next
        while cur and cur.val!=val:
            pre,cur=cur,cur.next
        if cur: pre.next=cur.next
        return head
            

4-1.删除链表的倒数第n个节点

给定一个链表,删除链表的倒数第n个节点并返回链表的头指针.

输入{1,2,3,4,5,6},2
返回{1,2,3,4,6}

快慢指针均指向头节点:快指针先走k步,然后两个指针一起走直到快指针走到最后一个节点,此时慢指针位于要删除的节点的前一个节点,直接slow.next=sloe.next.next删除即可,返回头指针head.
在这里插入图片描述
且注意,考虑删除头结点(len - n == 0)的特例,直接返回head->next

分如下两种情况:
1 n=0(链表为空)或n=1(链表只有一个元素),直接返回None
2 常规情况,两个游标,一个游标first_cur先走n步,然后两个游标first_cur/last_cur一起移动。需要注意当n=链表的长度时,即需要删除头节点时的情况。

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

#
# 
# @param head ListNode类 
# @param n int整型 
# @return ListNode类
#
class Solution:
    def removeNthFromEnd(self , head , n ):
        #0 or 1
        cur = head.next if head else head
        if not cur:
            return None
        slow,fast=head,head
        for _ in range(n):
            fast=fast.next
        if not fast:return head.next
        while fast.next:
            slow,fast=slow.next,fast.next
        slow.next=slow.next.next
        return head
            

4-2. 输出该链表中倒数第k个结点

注意:如果是返回从第k个节点开始的链表
比如

输入{1,2,3,4,5,6},2
返回{5,6}

则不需要slow.next=sloe.next.next,且一直快指针跑到为空,慢指针跑到倒数第k个节点处
返回去slow即可
如下题:
输入一个链表,输出该链表中倒数第k个结点。

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param pHead ListNode类 
# @param k int整型 
# @return ListNode类
#
class Solution:
    def FindKthToTail(self , pHead , k ):
        # write code here
#         if not pHead:return pHead
        slow,fast=pHead,pHead
        for _ in range(k):
        #注意:下面一句判断解决链表为{}情况和{1,2,3},k=50情况,均应该返回空链表
            if not fast: return fast
            fast=fast.next
        while fast:
            slow,fast=slow.next,fast.next
        return slow

5-1.链表是否有环

判断给定的链表中是否有环。如果有环则返回true,否则返回false。
你能给出空间复杂度的解法

根据快慢指针是否相遇判断
快指针每次两步,慢指针每次一步,有环则必相遇。
终止条件:null,单个节点,两个结点,都不可能有环

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

# @return bool布尔型
class Solution:
    def hasCycle(self , head ):
        if not head:
            return False
        fast,slow=head,head
        while fast and fast.next:
            fast,slow=fast.next.next,slow.next
            if fast==slow:
                return True
        return False      

5-2.若链表有环找入环点

对于一个给定的链表,返回环的入口节点,如果没有环,返回null
拓展:你能给出不利用额外空间的解法么?

解法:一个快指针,两个慢指针。
1.快慢指针同时出发,如果快指针到达null,说明链表没有环
2.如果快慢指针相遇了,说明链表有环。
(以上和5-1一样)
3.在快慢指针相遇的时候,让第二个慢指针从头结点出发,
4.当两个慢指针相遇的时候,相遇的位置就是环的起点。

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

# @param head ListNode类 
# @return ListNode类

class Solution:
    def detectCycle(self , head ):
        if not head:
            return head
        slow,fast=head,head
        while fast and fast.next:
            fast,slow=fast.next.next,slow.next
            if fast==slow:
                slow2=head
                while slow!=slow2:
                    slow,slow2=slow.next,slow2.next
                return slow
        return None

6.两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。
在这里插入图片描述
解题思路:
我们使用两个指针 node1node2 分别指向两个链表 headAheadB 的头结点,然后同时分别逐结点遍历,当 node1 到达链表 headA 的末尾时,重新定位到链表 headB 的头结点;当 node2 到达链表 headB 的末尾时,重新定位到链表 headA 的头结点。
这样,当它们相遇时,所指向的结点就是第一个公共结点
在这里插入图片描述

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

#
# 
# @param pHead1 ListNode类 
# @param pHead2 ListNode类 
# @return ListNode类
#
class Solution:
    def FindFirstCommonNode(self , pHead1 , pHead2 ):
        node1,node2=pHead1,pHead2
        while node1!=node2:
            node1=node1.next if node1 else pHead2
            node2=node2.next if node2 else pHead1
        return node1

7.复制复杂链表(含random)

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
在这里插入图片描述
解题思路: 哈希表
考虑构建 原链表节点 和 新链表对应节点 的键值对映射关系,再遍历构建新链表各节点的 next 和 random 引用指向即可。
参考ppt理解:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/solution/jian-zhi-offer-35-fu-za-lian-biao-de-fu-zhi-ha-xi-/
在这里插入图片描述

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head: return
        dic = {}
        # 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
        cur = head
        while cur:
            dic[cur] = Node(cur.val)
            cur = cur.next
        cur = head
        # 4. 构建新节点的 next 和 random 指向
        while cur:
            dic[cur].next = dic.get(cur.next)
            dic[cur].random = dic.get(cur.random)
            cur = cur.next
        # 5. 返回新链表的头节点
        return dic[head]

8.两个链表相加

假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。

链表元素正序

情况1:链表元素正序存放

输入
[9,3,7],[6,3]
返回值
{1,0,0,0}

思路:
因为相加应该从个位开始,同时考虑进位问题
因为两个链表和最后相加后的链表都要求是正序.因此用到三次反转.
首先对于两个链表进行一次翻转,即[7,3,9],[3,6]进行相加
当两个列表均空的时候停止相加.
初始化进位jw=0,新链表表头newhead,cur指向新链表表头.
循环里:
res=jw值
哪个链表非空:res+=head1.val,同时链表往下走一步
cur.next指向新节点,节点的值为res%10
更新进位jw=res//10
同时指针往前一步

当两个链表均为空的时候跳出循环,此时还得看可能还有进位,则如果此时仍有进位的值,cur.next指向val为该进位值的新节点.

注意,此时新的链表头节点值为-1,因此从newhead.next开始,反转后即为相加后链表的正序.

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

#
# 
# @param head1 ListNode类 
# @param head2 ListNode类 
# @return ListNode类
#
class Solution:
    def reverse(self,head):
            pre,cur=None,head
            while cur:
                temp=cur.next
                cur.next=pre 
                pre=cur
                cur=temp
            return pre
    def addInList(self , head1 , head2 ):
         
        head1,head2=self.reverse(head1),self.reverse(head2)
        newhead=ListNode(-1)
        cur=newhead
        jw=0
        while head1 or head2:
            res=jw
            if head1:
                res+=head1.val
                head1=head1.next
            if head2:
                res+=head2.val
                head2=head2.next
            cur.next=ListNode(res%10)
            jw=res//10
            cur=cur.next
        if jw>0:
            cur.next=ListNode(jw)
        return self.reverse(newhead.next)
            
            

注意:也可以不用反转链表,可以用三个栈,head1,head2分别入栈,相加后的元素也入栈,再按照出栈顺序构造新的链表.

链表元素倒序

情况2:链表元素倒序存放

如输入:(7 -> 1 -> 6) ,(5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912

此时不需要reverse操作,其他的与情况1代码一致即可

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

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        newhead=ListNode(-1)
        cur,jw=newhead,0
        while l1 or l2:
            res=jw
            if l1:
                res+=l1.val
                l1=l1.next
            if l2:
                res+=l2.val
                l2=l2.next
            cur.next=ListNode(res%10)
            jw=res//10
            cur=cur.next
        if jw>0:
            cur.next=ListNode(jw)
        return newhead.next

9.删除链表的重复元素

未排序链表

情况1:移除未排序链表中的重复节点。保留最开始出现的节点

输入:[1, 2, 3, 3, 2, 1]
 输出:[1, 2, 3]

思路:
新建一个节点cur指向头节点,利用一个set().
当cur.next非空时遍历(从头节点到最后一个节点):
若cur.next的值不在visited里,则加入,同时cur往前走一步
否则,删除cur.next的节点.
最后输出head

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

#
# 
# @param head ListNode类 
# @return ListNode类
#
class Solution:
    def deleteDuplicates(self , head ):
        if not head or not head.next:
            return head
        node=ListNode(-1) 
        node.next=head
        visited=set()
        while node.next:
            if node.next.val not in visited:
                visited.add(node.next.val)
                node=node.next
            else:
                node.next=node.next.next
        return head

排序链表

情况2:移除排序链表中的重复节点。保留最开始出现的节点
删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次.
例如:

输入
{1,1,2}
返回
{1,2}

思路:
直接cur指向头指针
在cur.next非空时遍历,因为已经排序,如果重复,只可能相邻的值重复,因此若cur的值和cur.next值相等,则删除cur.next,否则,cur正常往后走.
最后返回head

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

#
# 
# @param head ListNode类 
# @return ListNode类
#
class Solution:
    def deleteDuplicates(self , head ):
        # write code here
        if not head or not head.next:return head
        cur=head
        while cur.next:
            if cur.next.val==cur.val:
                cur.next=cur.next.next
            else:
                cur=cur.next
        return head

排序链表,且不把重复元素保留一次,全部删除

给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
例如:

输入
{1,2,2}
返回值
{1}

思路:
建立两个一样的链表new和pre,选择遍历head,当head.val==head,next.val时,进入while循环,head往前走,循环结束,依然head往前走一步,因为只要是重复的都不要,然后pre选择新的next,但不更新pre,因为此时的head尽管以及跳过一段重复了,但可能也是下一个重复数字的开始,若还没更新pre,head已经到最后一个数字了,那么刚好pre.next就是head。否则head和pre同时更新。

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

#
# 
# @param head ListNode类 
# @return ListNode类
#
class Solution:
    def deleteDuplicates(self , head ):
        new=ListNode(-1)
        new.next=head
        pre=new
        while head and head.next:
            if head.val==head.next.val:
                while head and head.next and head.val==head.next.val:
                    head=head.next
                head=head.next
                pre.next=head
            else:
                pre,head=pre.next,head.next
        return new.next

在这里插入图片描述

10. 判断链表是否回文

这个在leetcode上可以AC但是在牛客网上总超时QAQ
那理解方法为主吧
题目:
编写一个函数,检查输入的链表是否是回文的。

输入: 1->2
输出: false 
示例 2:

输入: 1->2->2->1
输出: true

思路
如果空列表,直接True
(1)找到链表中心位置:
设置fast,slow初始化=head,即均指向第一个节点:
fast每走2步,slow走1步
当fast为空或fast.next为空,则跳出.

  • 当链表长度为奇数的时候
    跳出循环时候,slow指向最中间的数字(不是后半部分链表的开始,不过不影响后面比对)
    在这里插入图片描述

  • 当链表长度为偶数的时候
    如图再往下一步即fast为空,此时slow指向后半段链表的第一个节点(图中未画出)
    在这里插入图片描述

(2)反转后半部分链表,前后两部分链表进行比较
类似反转链表的写法:此时fast指向前半段链表的头节点,reverse为后半段链表反转后的头节点.
在这里插入图片描述
当二者都不为空时,比较对应节点值,如果不一样,立刻返回False
否则,fast,reverse均向前走[注意:当链表长度为奇数的时候,此时reverse链表比fast链表长,但是不要紧,fast链表走完即跳出,不会到那一步比较]
跳出返回True

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        # l=[]
        # while head:
        #     l.append(head.val)
        #     head=head.next
        # return l[:]==l[::-1]
        if not head :return True
        fast,slow=head,head
        #找到中心点
        while fast and fast.next:
            fast=fast.next.next
            slow=slow.next
        #反转后半部分
        fast=head
        reverse=None
        while slow:
            temp=slow.next
            slow.next=reverse
            reverse=slow
            slow=temp
        #比较两部分是否相等
        while fast and reverse:
            if fast.val!=reverse.val:
                return False
            fast,reverse=fast.next,reverse.next
        return True      

注释部分代码是将链表push进列表,根据python列表的比较,看翻转后和原来是否一致比较.list[:]==list[::-1]

11.重排链表

题目描述:
在这里插入图片描述要求使用原地算法,不能改变节点内部的值,需要对实际的节点进行交换。
例如:
对于给定的单链表{10,20,30,40},将其重新排序为{10,40,20,30}.

思路:
(1)利用快慢指针找到中间节点,链表分为两部分
(2)对后半部分的链表进行反转操作
(3)交叉取节点合并成新链表

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

#
# 
# @param head ListNode类 
# @return void
#
class Solution:
    def reorderList(self , head ):
    #当空链表或者只有1个或2个节点时候不用重排
        if not head or not head.next or not head.next.next:
            return head
        # 1.找到中间节点,链表分两个部分
        slow,fast=head,head
        while fast.next and fast.next.next:
            fast,slow=fast.next.next,slow.next
        right=slow.next
        slow.next=None
        #2.反转后半部分链表
        pre=None
        while right:
            temp=right.next
            right.next=pre
            pre=right
            right=temp
        left=head
        right=pre
        #3合并两部分链表,left和pre(right)
        while left and right:
            next1,next2=left.next, right.next
            left.next= right
            left=next1
            right.next=left
            right=next2
        return left
            

注意分开两个链表时的一个操作:图解一下!

right=slow.next
slow.next=None

在这里插入图片描述

--------2021.03----updating-------------------

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值