Leetcode刷题日记-链表(三)

1. 两数相加I (2)

题目可以看-原题地址

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

示例:

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

分析:
(1)注意长度不同的两个数进行相加,且对应位相加的时候是三个数相加,x+y+carry
(2)注意需要进位的情况,答案比原来的数要多一位 如 11 + 99 = 110,因此当任何一个数组没有遍历完或者有进位carry的情况,循环继续。
(3)时间复杂度:O(max(m, n)),假设 m 和 n 分别表示 l1 和 l2 的长度,上面的算法最多重复 O(max(m, n)) 次。
(4)空间复杂度:O(max(m, n)), 新列表的长度最多为 O(max(m, n))+1。

在这里插入图片描述

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = tail = ListNode(0)
        s = 0
        while l1 or l2 or s:
            s += (l1.val if l1 else 0) + (l2.val if l2 else 0)
            tail.next = ListNode(s % 10)
            tail = tail.next 
            s //= 10
            l1 = l1.next if l1 else None
            l2 = l2.next if l2 else None 
        return dummy.next

2. 两数相加II (445)

题目可以看-原题地址

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。

进阶:
如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
示例:
输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 8 -> 0 -> 7

分析:
(1)这道题与上一道题的区别:上一道题的高位在链表的尾部,因此可以设定两个指针在链表头部按位遍历再相加。而本题的高位在尾部,要想先让尾部按位相加,可以利用栈的知识,先将两个链表由到尾压入栈中,栈的特性是先入后出,因此可以再出栈时按位相加。
(2)两种特殊情况和上一题就一样了,循环的时候要判断是不是栈是否为空,是否还有进位。另一个就是,按位相加是三个数相加,x+y+carry
(3)如何进行连接,设置dummy节点,新节点的.next指向dummy.next,而dummy.next永远指向头节点。

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        stack1, stack2 = [], []
        carry = 0
        dummy = ListNode(0)

        while l1:
            stack1.append(l1.val)
            l1 = l1.next
        while l2:
            stack2.append(l2.val)
            l2 = l2.next
        
        while stack1 or stack2 or carry:
            if stack1:
                val1 = stack1.pop()
            else:
                val1 = 0
            if stack2 :
                val2 = stack2.pop()
            else:
                val2 = 0
            value = val1 + val2 + carry
            valnode = value % 10
            carry = value // 10

            node = ListNode(valnode)
            node.next = dummy.next
            dummy.next = node
        return dummy.next

3. 回文链表 (234)

题目可以看-原题地址

请判断一个链表是否为回文链表
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

分析:
本题要空间复杂度为O(1)所以说我们不能开辟新的内存空间。因此我们可以按以下步骤和方法来解体:
(1)截取两段链表:因为是对称链表,因此我们可以截取两段链表。方法:使用快慢指针,快指针是慢指针的2倍速度。
(2)反转链表。方法:三个指针,或者递归都可以。
(3)然后两个链表进行一一比较。
截取链表时要注意,当为奇数链表时,slow指针位于中间点,而偶数链表时,位于中间两个偏右的节点,但是当我们反转链表一一比较时候,只要有一个链表为空时,我们就可以不比较了。所以也不用在意到底是奇数链表还是偶数链表。

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if not head or not head.next:
            return True
        # 取中位数的上边界,比如[1, 2, 2, 3] 取到是第二个2
        slow = head
        fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        # 反转链表
        pre = None
        cur = slow
        while cur:
            next_node = cur.next
            cur.next = pre
            pre = cur 
            cur = next_node
        # 链表对比
        p1 = head
        p2 = pre
        # 若为奇数,中间元素不用比较,所以当有一个链表为空则可以退出循环
        while p1 and p2:
            if p1.val != p2.val:
                return False
            p1 = p1.next
            p2 = p2.next
        return True

4. 分隔链表 (725)

题目可以看-原题地址

给定一个头结点为 root 的链表, 编写一个函数以将链表分隔为 k 个连续的部分。
每部分的长度应该尽可能的相等: 任意两部分的长度差距不能超过 1,也就是说可能有些部分为 null。
这k个部分应该按照在链表中出现的顺序进行输出,并且排在前面的部分的长度应该大于或等于后面的长度。
返回一个符合上述规则的链表的列表。
举例: 1->2->3->4, k = 5 // 5 结果 [ [1], [2], [3], [4], null ]
示例 1:
输入:
root = [1, 2, 3], k = 5
输出: [[1],[2],[3],[],[]]
示例 2:
输入:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
输出: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
解释:
输入被分成了几个连续的部分,并且每部分的长度相差不超过1.前面部分的长度大于等于后面部分的长度。

思路:
(1)首先遍历链表长度counts,然后再根据K把其分为K段。
(2)counts // K就是每一段的长度。
(3)根据题意,任意两段的长度不能相差大于1,因此counts % K的余数为m,则代表前面m段,每段的长度要比m段后面的每段长度大多1位。
(4)设置两个循环,外层循环遍历K段,每次把节点放入列表中。
(5)内层循环则是遍历每段的大小,然后移动指针,直到该段达到分配的长度后,就把这段截断,后面的在之后的循环中继续分配,循环的条件是cur不为空,

class Solution:
    def splitListToParts(self, root: ListNode, k: int):
        total_len = 0
        cur = root
        while cur:
            total_len += 1
            cur = cur.next
        length = total_len // k   # 每段的基础长度
        m = total_len % k         # 前 l 段需要在基础长度上+1

        res = []
        cur = root
        for i in range(k):
            res.append(cur)
            size = length + (1 if m > 0 else 0)   # 算出每段的长度
            if cur:   # 这里判断cur是否存在,防止cur.next不存在报错
                for j in range(size-1):
                    cur = cur.next
                m -= 1
                tmp = cur.next   # 把后面一段截掉,后面一段需在后面继续划分
                cur.next = None
                cur = tmp
        return res

class Solution:
    def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]:
        counts = 0
        cur = root
        while cur:
            cur = cur.next
            counts += 1
        per_len = counts // k
        m = counts % k
        cur = root # 指针别忘了移回来,指到头节点
        alist = []
        for i in range(k):
            alist.append(cur)
            size = per_len + (1 if m > 0 else 0)
            if cur:
                for j in range(size - 1):
                    cur = cur.next
                m -= 1
                tmp = cur.next
                cur.next = None
                cur = tmp
        return alist

5. 奇偶链表 (328)

题目可以看-原题地址
常规思路:
头节点为奇数,第二个节点为偶数节点,因此我们可以先连接奇数链表,然后在连接偶数连边表,然后让奇数链表的尾部指向偶数链表的头部即可。

class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head:return head
        odd = head
        even_head = even = head.next
        while odd.next and even.next:
            odd.next = odd.next.next
            even.next = even.next.next
            odd,even = odd.next,even.next
        odd.next = even_head
        return head
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值