leetcode链表题总结(python版)

在总结链表题前,先记录一个误区:

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
tmp = ListNode(0)
res = temp

问题是:tmp和res是代表什么?两个相同链表的头结点?指向两个相同链表的头结点?

解答:tmp和res指向的是存储 哑节点0 的位置,此时tmp和res存储的是同一内存地址,可以理解成tmp和res是两个指针。当对res进行操作时,会在res中存储内容(节点)即为这个内存放内容。而res不断移动,tmp还是指向原来的 哑节点的位置。

但是我们之前用python列表时,情况如下

a = [1, 2]
b = a
b += [3]
print(a) #a = [1, 2]
print(b) #b = [1, 2, 3]

为什么改变b却不能改变a呢?那是因为列表是可变对象,对于可变对象,地址是不可以共享的,也就是a,b指向的地址是不一样的。

总结:在python中,不可变对象是共享的(如节点对象),创建可变对象永远是分配新地址(如列表)。

常见的链表题错误

在quick移两个指针的时候,要这样写,要不quick为None时,quick.next会报错。多使用哑节点。

while quick and quick.next:
    quick = quick.next.next

链表题中,能用一个指针用一个指针,能用两个不用三个,而且都要建立哑节点,并对需要的指针进行初始化。

如删除链表重复的节点的题目。

在链表题中,常用的函数有:

def to_kth_node(head, k):#head为第一个结点,k为第k个结点
    k -= 1
    p = head
    while k > 0 and p:
        p = p.next
        k -= 1
    return p

接下来进入链表的题目,做题顺序:2、19、21、23、24、25、61、83、82、86、92

【leetcode2两数相加】

题目:给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

思路:这道题的关键在于进位问题,即当两个数相加大于10,需要向高位进一位。因此,设置进位变量为flag。

但是这道题要想完全做对的关键在于,l1和l2不一样长,而且要判断while循环完后是否有进位的情况。自己在写完常规思路以后一定要想一下是否满足特殊情况,把特例测试一下。

# 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:
        if l1 is None:
            return l2
        if l2 is None:
            return l1
        # tmp是暂存(temporal)
        tmp = ListNode(0)  # tmp指向哑节点
        # res是重置(reset)
        res = tmp  # res指向哑节点(此时和tmp指向同一位置)
        # flag 进位标志
        flag = 0  # 初始化 (只可能是0或1)
        while l1 or l2:  # l1或l2不为空就持续执行
            tmp_sum = 0  # 链表节点值的和
            if l1:  # 如果l1不为空,把l1的某个节点值的和赋给tmp_sum
                tmp_sum = l1.val  # 把l1的某个节点的值赋给tmp_sum
                l1 = l1.next
            if l2:  # 如果l2不为空,把l2中和l1对应的节点的值加到tmp_sum
                tmp_sum += l2.val
                l2 = l2.next  # 指向下一个节点,为下一次的加和做准备
            tmp_res = ((tmp_sum + flag) % 10)  # 个位数字
            flag = ((tmp_sum + flag) // 10)  # 进位的数
            res.next = ListNode(tmp_res) #将个位数字变为节点
            res = res.next  # res后移
            #!!!设置很巧妙,要注意
            if flag:  # 如果flag不为0,就是对应位置相加后有进位
                res.next = ListNode(1)  # res的下一节点设为1
        res = tmp.next  # 找到头结点,并赋给res
        del tmp  # 删除tmp变量
        return res  # 返回res链表(链表返回只需要返回头节点)    

复杂度分析

时间复杂度:O(max(m,n)),其中m表示l1链表的长度,n表示l2链表的长度

空间复杂度:O(max(m,n)),新链表的长度最多为max(m,n)+1

总结:1、请注意,我们使用哑结点来简化代码。如果没有哑结点,则必须编写额外的条件语句来找到表头,而使用哑节点,可以很快找到表头,因为此时res已经在链表尾部了。2、在写代码的过程中,考虑一下特殊情况,如下表

参考:leetcode题解https://leetcode-cn.com/problems/add-two-numbers/solution/liang-shu-xiang-jia-by-leetcode/

哑节点除了能快速找到新链表表头以外,还能简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部

【Leetcode19删除列表的倒数第N个节点】

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

说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

思路:本质是删除题,属于链表的基本操作。当时想到的方法是用两次遍历,第一次遍历先知道链表一共有多少个节点,第二次遍历找到需要删除节点的前一节点。该题我写了三个版本的代码,累死个人。

代码1:没有用到哑节点,把删除分成三种情况,头节点,中间节点,尾节点,两次遍历

代码2:用到哑节点,把删除分成两种情况,尾节点和其它节点,两次遍历

代码3:用到哑节点两个指针一次遍历:一次遍历的原因是让两个指针之间间隔n个结点,这样让一个指针到尾节点结束,另一个指针即为要删除结点的上一结点。特别注意的是两个指针间隔n个结点,之前一直弄错成n-1了!!!

贴出代码:

代码1:没有用到哑节点,把删除分成三种情况,头节点,中间节点,尾节点,两次遍历

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

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        p = head
        tmp = p
        listnode_n = 0
        while p:
            listnode_n += 1
            p = p.next
        p = tmp
        if n == 1: #删除尾节点的情况
            if listnode_n == 1:
                return None
            else:
                for i in range(listnode_n-2):
                    p = p.next
                p.next = None
                return tmp
        if n == listnode_n: #删除头节点的情况
            return tmp.next
        else:
            diff = listnode_n - n
            if diff > 1:
                for j in range(diff-1):
                    p = p.next
            m = p.next
            p.next = m.next
            return tmp

代码2:用到哑节点,把删除分成两种情况,尾节点和其它节点,两次遍历

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

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode(0)#总结:第一次做分为三种情况,删除头节点,删除中间节点,删除尾节点,后来发现尾节点和中间节点情况一样,只需要考虑头节点和中间节点,但是引入哑节点可以解决头节点的问题。从而不用考虑删除头节点的情况,而且方便返回新链表
        dummy.next = head
        p = head
        length = 0
        while p:
            length += 1
            p = p.next
        q = dummy
        diff = length - n
        while diff:
            q = q.next
            diff -= 1
        q.next = q.next.next
        return dummy.next

 代码3:用到哑节点两个指针一次遍历

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

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:    
        dummy = ListNode(0)#一次遍历的方法(进阶版);建立哑节点,并且将哑节点指向head
        dummy.next = head
        p = dummy
        q = p
        s = n + 1 #假设最后一个节点是null
        while s:
            p = p.next
            s -= 1
        while p:
            q = q.next
            p = p.next
        q.next = q.next.next
        return dummy.next
#总结:第一次做分为三种情况,删除头节点,删除中间节点,删除尾节点,后来发现尾节点和中间节点情况一样,只需要考虑头节点和中间节点,但是引入哑节点可以解决头节点的问题。从而不用考虑删除头节点的情况,而且方便返回新链表

单独写函数to_kth_node(head, k)

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

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:  
        if head is None or n <= 0:
            return None
        dummy = ListNode(0)
        dummy.next = head
        p = dummy
        q = head
        q = self.to_kth_node(q, n+1)
        while q:
            p = p.next
            q = q.next
        p.next = p.next.next
        return dummy.next
    def to_kth_node(self, head, k):
        k -= 1
        post = head
        while k > 0 and post:
            post = post.next
            k -= 1
        return post

复杂度分析

时间复杂度分析:O(n)

空间复杂度分析:O(1)

总结

  1. 链表题一定要用哑节点(便于返回头节点,便于删除操作不考虑头节点的特殊情况
  2. 双指针可以做到只需一次遍历。

【leetcode21合并两个有序链表】

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

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

思路:要明白链表的性质就是,如果返回节点1,后面一串都会返回。这道题根本上一个节点添加的问题。哑节点必备。

代码如下:

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

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode(0)
        q = dummy
        p1 = l1
        p2 = l2
        while p1 and p2:
            if p1.val <= p2.val:
                q.next = p1
                p1 = p1.next
            else:
                q.next = p2
                p2 = p2.next
            q = q.next
        if p1:
            q.next = p1
        if p2:
            q.next = p2
        return dummy.next
    #这个链表一定要明白,我每次都是得到当前节点,但是后面还有一大串节点,注意新链表q的移动[q = q.next]

复杂度分析

时间复杂度分析:O(n),n为链表节点数

空间复杂度分析:O(1)

【leetcode23合并K个有序链表】

题目:合并 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

思路:在之前合并两个排序链表的基础上,需要k-1次两两合并。进阶:进行Log(k)次合并即可。注意在进行log(k)时循环应该满足的条件。

这个循环体挺难写的:

思想:考虑奇偶情况,先把分支图画出来发现奇偶情况相同,然后再对每一次两两合并进行精准设置,在考虑一共要经过多少轮两两合并。

while interval < amount:
    for i in range(0, amount - interval, interval * 2):
        lists[i] = self.mergeTwoLists(lists[i], lists[i + interval])
    interval *= 2

代码如下:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#思路是将K个分解成k-1组两链表合并,时间复杂度是O(kn),n表示节点,k表示链表个数,空间复杂度为O(1)
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        def mergeTwoLists(l1, l2):
            dummy = ListNode(0)
            q = dummy
            p1 = l1
            p2 = l2
            while p1 and p2:
                if p1.val <= p2.val:
                    q.next = p1
                    p1 = p1.next
                else:
                    q.next = p2
                    p2 = p2.next
                q = q.next
            if p1:
                q.next = p1
            if p2:
                q.next = p2
            return dummy.next
        if len(lists) == 0:  #要注意这个lists=0的特殊情况
            return None
        k = len(lists) - 1
        for ki in range(k):
            lists[ki+1] = mergeTwoLists(lists[ki], lists[ki+1])
        return lists[k]

优化后的代码:进行Log(k)次合并即可,非常的省时 

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

class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        amount = len(lists)
        if amount == 0:
            return None
        interval = 1
        while interval < amount:
            for i in range(0, amount - interval, interval * 2):
                lists[i] = self.mergeTwoLists(lists[i], lists[i + interval])
            interval *= 2
        return lists[0] if amount > 0 else lists

    def mergeTwoLists(self, l1, l2):
        dummy = ListNode(0)
        q = dummy
        p1 = l1
        p2 = l2
        while p1 and p2:
            if p1.val <= p2.val:
                q.next = p1
                p1 = p1.next
            else:
                q.next = p2
                p2 = p2.next
            q = q.next
        if p1:
            q.next = p1
        if p2:
            q.next = p2
        return dummy.next




        

总结:做这道题最大的感触就是,(1)要学会分而治之的方法,复杂的K个链表合并问题分解成简单的合并2个链表的问题(2)要学会优化,从最初需要k-1次合并,变为log(k)次合并,时间复杂度大大降低。

【leetcode24两两交换链表中的节点】

题目:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

给定 1->2->3->4, 你应该返回 2->1->4->3.

第一次做题:思路1:咋感觉自己越做题越大意了,要考虑特殊情况如奇偶链表链表为空的情况,这道题其实就是一个断节点和连接节点的过程。代码如下面注释部分。

遇到的困难:1、对p.next=q(连接节点)不够熟悉,此时p指针不移动2、p = p.next 和p.next=p.next.next都对p指针移动了一次。3、奇偶链表的情况不同在while 循环的条件哪里有所体现。

第二次做题:思路2:设置三个指针prev,now,post,最后对post的情况进行讨论,关键在于翻转后的两个结点接下来如何连接下面结点的问题,在while循环内部,如果post和post.next同时存在,那么prev,now,post指针是都需要移动的,如果下面没有结点即post is None,那就连接post,同时退出循环;如果下面有一个结点即post在,post.next为空,接着连接post并且退出循环

代码如下:

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

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        # dummy = ListNode(0)
        # p = dummy
        # p.next = head
        # if head is None:
        #     return None
        # q = p.next
        # while q and q.next:#注意奇偶链表的情况
        #     p.next = q.next
        #     q.next = q.next.next
        #     p.next.next = q
        #     p = p.next.next
        #     q = q.next
        # return dummy.next
        if head is None or head.next is None:
            return head
        dummy = ListNode(0)
        dummy.next = head
        prev = head
        now = prev.next
        post = now.next
        dummy.next = now
        while now:
            now.next = prev
            if post and post.next:
                prev.next = post.next
                prev = post
                now = prev.next
                post = now.next
            elif post:
                prev.next = post
                break
            elif post is None:
                prev.next = post
                break
        return dummy.next
        
        

总结1:分清楚链表的基本操作:连接节点,移动指针,考虑特殊情况。

总结2:三个指针,先考虑一般情况,后来主要考虑代码循环内部遇到的特殊情况,然后进行分支结构。

leetcode25.K个一组翻转链表

题目:

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

给定这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5

说明 :

  • 你的算法只能使用常数的额外空间。
  • 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

思路:递归法。先翻转前k个节点,然后递归。

class Solution:
    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        #先解决前k个的翻转[a, b),然后接上用递归

        # 非递归法
        def reverseab(head, end): #关键是左闭右开
            pre = None
            now = head
            while now != end:
                tmp = now.next
                now.next = pre
                pre = now
                now = tmp
            return pre
        #递归法 翻转前N个节点
        self.after = None
        def reverseN(head, k):
            if k == 1:
                self.after = head.next
                return head
            last = reverseN(head.next, k - 1)
            head.next.next = head
            head.next = self.after
            return last
        def to_kth_node(head, k): #注意k-1 和不减的区别
            # k -= 1
            p = head
            while k > 0 and p:
                p = p.next
                k -= 1
            return p
        
        q = head
        m = k
        while m > 0 and q:
            q = q.next
            m -= 1
        if m > 0:
            return head 
        node = to_kth_node(head, k)
        # new_head = reverseab(head, node)
        new_head = reverseN(head, k)
        head.next = self.reverseKGroup(node, k)   
        return new_head
 #整理:递归法翻转链表、非递归法翻转链表;翻转指定mn长度的链表、递归法非递归法;k个一组翻转链表,递归法。 
 #总结,pre,now非递归翻转指定mn长度的链表,注意这样好做[a,b);to_kth_node一开始不用k-=1。

详解:

  1. to_kth_node函数很简单,请看如下代码就可以搞明白。
  2. reverse函数是核心,在reverse函数中,有三个指针分别为prev/now/post,其中prev/now是为了对相邻节点进行翻转,即now.next = prev,在这个操作后前面和post之间的链就断开了,而post就是起到一个找到(连接)后面节点继续操作的一个作用。需要注意两点,一是reverse对begin和end进行处理后,其实是一个相交链,像环一样,在reverseKGroup函数中要注意对now_end.next进行处理(和后面的节点相连接),即now.next=prev后,此时仍有prev.next=now,因此要对prev.next这条链也就是后面的now_end.next进行处理;二是当begin.next=end=now,虽然没进入循环,但是仍需要now.next=prev,别忘了。
  3. reverseKGroup函数是灵魂,首先在to_kth_node和reverse函数的基础上设置四个指针,now_begin/now_end/next_begin/next_end其中next_begin也是起到一个找到下面一组节点的作用。需要注意的是当next_end存在时,now_end.next = next_end,不存在时now_end.next = next_begin.

  4. 代码如下:

  5. # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
            if head is None:
                return None
            if self.to_kth_node(head, k) is None:
                return head
            now_begin = head
            new_head = self.to_kth_node(head, k)
            now_end = self.to_kth_node(now_begin, k)
            next_begin = self.to_kth_node(now_begin, k+1)
            next_end = self.to_kth_node(next_begin, k)
            while now_begin and now_end:
                now_begin, now_end = self.reverse(now_begin, now_end)
                if next_end:
                    now_end.next = next_end
                else:
                    now_end.next = next_begin
                now_begin = next_begin
                now_end = self.to_kth_node(now_begin, k)
                next_begin = self.to_kth_node(now_begin, k+1)
                next_end = self.to_kth_node(next_begin, k)
            return new_head       
        def to_kth_node(self, head, k):
            point = head
            k -= 1
            while k > 0 and point:
                point = point.next
                k -= 1
            return point
    
        def reverse(self, begin, end): #这个可不是完全相等的
            if begin is end: #试试这可不可以写等号
                return begin, end
            prev = begin
            now = prev.next
            post = now.next
            while post is not end.next:  
                now.next = prev
                prev = now
                now = post
                post = post.next
            now.next = prev
            return end, begin
    
    #这个题主要是把reverse函数写出来,然后再把reverseKGroup函数写对。参考csdn总结。

    总结:通过这道题,深刻明白了节点间断链连接链的基本操作。在断链前一定要先找到下一节点,要不就找不到啦。

  6. 二次版本代码:思路较清楚

  7. class Solution:
        def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
            if head is None or head.next is None:
                return head
            now_begin = head
            now_end = self.to_kth_node(head, k)
            if now_end is None:
                return head
            new_head = now_end
            next_begin = self.to_kth_node(head, k+1)
            next_end = self.to_kth_node(next_begin, k)
            while True:
                now_begin, now_end = self.reverse(now_begin, now_end)
                if next_end is None or next_begin is None:
                    now_end.next = next_begin
                    break
                now_end.next = next_end
                now_begin = next_begin
                now_end = next_end
                next_begin = self.to_kth_node(now_begin, k+1)
                next_end = self.to_kth_node(next_begin, k)
            return new_head
        def to_kth_node(self, head, k):
            k -= 1
            point = head
            while k > 0 and point:
                point = point.next
                k -= 1
            return point
        def reverse(self, begin, end):
            if begin is end:
                return begin, end
            prev = begin
            now = prev.next
            post = now.next
            while post is not end.next:
                now.next = prev
                prev = now
                now = post
                post = post.next
            now.next = prev
            return end, begin
    #这个题主要是把reverse函数写出来,然后再把reverseKGroup函数写对。参考csdn总结。

    这个版本注意一开始next_end不存在的情况,因为k可能大于整个链表的长度。

  8. 【leetcode61.旋转列表】

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

  10. 输入: 0->1->2->NULL, k = 4
    输出: 2->0->1->NULL
    解释:
    向右旋转 1 步: 2->0->1->NULL
    向右旋转 2 步: 1->2->0->NULL
    向右旋转 3 步: 0->1->2->NULL
    向右旋转 4 步: 2->0->1->NULL

    代码如下:

  11. # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def rotateRight(self, head: ListNode, k: int) -> ListNode:
            if head is None:
                return head
            length_link = self.length_of_link(head)
            if k % length_link == 0:
                return head
            p = head
            k_real = k % length_link
            left = self.to_kth_node(head, length_link-k_real)
            right = left.next
            end = self.to_kth_node(head, length_link)
            end.next = p
            left.next = None
            return right
    
        def to_kth_node(self, head, k):
            point = head
            k -= 1
            while k > 0 and point:
                point = point.next
                k -= 1
            return point
        # PrintList(to_kth_node(node1, 2))
        def length_of_link(self, head):
            q = head
            i = 0
            while q:
                q = q.next
                i += 1
            return i
        # print(length_of_link(node1))
        # PrintList(rotateRight(node1, 4))

    【leetcode83.删除列表中的重复元素】

  12. 题目:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

  13. 输入: 1->1->2
    输出: 1->2

思路:设置三个指针,prev,now,p如果now.val和prev.val相等,那么prev.next = p,循环的条件是while p:,因此在p为空时跳出循环,需要再次判断prev和now是否相等,这一点很容易忽略,记得补充。

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

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if head is None or head.next is None:
            return head
        dummy = head
        prev = dummy
        now = prev.next
        p = now.next
        while p:
            if now.val == prev.val:
                prev.next = p
                now = p
                p = p.next
            else:
                prev = now
                now = now.next
                p = p.next
        if now.val == prev.val:
            prev.next = None
        return dummy
        

第二遍做的时候,用了两个指针prev和now,小心点,防止出错。

【leetcode82.删除排序链表中的重复元素 II

题目:给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:

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

 

思路:

  1. 建立两个指针pre和cur,最终想pre.next = cur
  2. 首先找到相同节点的入口,然后cur定位到第一个不是重复节点的值,循环直至没有cur.val == cur.next.val情况出现
  3. 令pre.next = cur
  4. 继续循环pre = cur,cur=cur.next,直至cur为空
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if head is None:
            return None
        dummy = ListNode(0)
        dummy.next = head #注意pre和cur一开始都指向dummy
        pre = dummy
        cur = pre
        while cur:
            pre = cur
            cur = cur.next
            while cur and cur.next and cur.val == cur.next.val:
                tmp = cur.val
                while cur and cur.val == tmp:
                    cur = cur.next
            pre.next = cur
        return dummy.next

【Leetcode86.分隔链表】

题目:给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

思路:可以想象成把这个链表分成两个小链表,一个是全部小于x的节点组成的列表,一个是大于等于x的节点组成的列表,最后将两个小链表连接起来,就可以得到完整的链表。定义链表头节点为head1,head2,定义三个指针p在(head1链表)移动,q在(head2链表)移动,m在整个head大链表中移动。需要注意的地方一是当两个小链表有一个不存在时,直接返回存在的链表,二是当两个链表都存在时,注意避免死循环,让p.next和q.next都等于None。

代码如下:

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

class Solution:
    def partition(self, head: ListNode, x: int) -> ListNode:
        if head is None or head.next is None:
            return head
        m = head
        head1 = None
        head2 = None
        while m:
            if m.val < x:
                if head1 is None:
                    head1 = m
                    p = head1
                    m = m.next
                else:
                    p.next = m
                    p = m
                    m = m.next
            else:
                if head2 is None:
                    head2 = m
                    q = head2
                    m = m.next
                else:
                    q.next = m
                    q = m
                    m = m.next
        if head1 != None and head2 != None: #注意这里,要分情况讨论
            l = p
            p.next = None
            q.next = None
            l.next = head2
            return head1
        if head1 == None and head2:
            return head2
        if head2 == None and head1:
            return head1

总结:发现链表题,开头要注意特殊情况head,head.next是否是None,中间循环要注意(常规思路),最后结尾要注意特殊情况。 

【leetcode92.翻转链表2】

题目:反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

思路:这个比25题简单多了,但是思路不变,仍然设置两个函数to_kth_node()和reverse(),然后再分情况讨论,m=n时,不需要翻转;m=1时,now_end连接第n+1个节点,返回新的头节点now_begin;m>1时连接now_begin,now_end,返回head;需要注意的也是同25题一致,注意reverse处理后其实是相交链,需要对now_end.next进行处理。另外,将if head is None or head.next is None放在主函数中才有用呀!!!

代码如下:

def to_kth_node(head, k):
    point = head
    k -= 1
    while k > 0 and point:
        point = point.next
        k -= 1
    return point
def reverse(head, begin, end):
    if begin is end:
        return end, begin
    prev = begin
    now = prev.next
    post = now.next
    while end.next is not post and post:
        now.next = prev
        prev = now
        now = post
        post = post.next
    now.next = prev #最后一步需要连接
    return end, begin
def reverseBetween(head: ListNode, m: int, n: int):
    if head is None or head.next is None:
        return head
    now_begin = to_kth_node(head, m)
    now_end = to_kth_node(head, n)
    end_next = now_end.next
    now_begin, now_end = reverse(head, now_begin, now_end)
    if m == n:
        return head
    if m == 1:
        now_end.next = end_next #对now_end处理,防止死循环;end_next为原始end的后一个值
        return now_begin #注意这个返回值
    if m > 1:
        now_left = to_kth_node(head, m-1)
        now_left.next = now_begin
        now_end.next = end_next
        return head

总结:

指针问题,简单的基本插入删除操作搞明白;在处理过程中出现next时考虑是否存在next;循环到最后一步是否符合题目需要做判断;跳出循环一般是while 条件不满足,还可以结合break跳出。

 

 

 

 

 

 

 

 

 

 

 

 

 


版权声明:本文为weixin_42723548原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。