数据结构与算法——小小链表不简单

一、两数相加

此题为leetcode第2题
思路:链表的头结点是个位,依次往后是十位、百位等,在每位的数相加时可能有进位,我们要预先有个进位flag=0,后续可能为0也可能为1,每次相加时都要加上这个进位。加完后的数可能大于10,如果大于10则flag=1,否则flag=0。然后这个数取余便是这位相加后的结果。同时以此遍历两个链表,一个到结尾时另一个继续遍历。

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        res= head = ListNode(0)
        p1, p2 = l1, l2
        flag = 0
        while p1 or p2 or flag != 0:
            a1 = p1.val if p1 else 0
            a2 = p2.val if p2 else 0
            if p1:
                p1 = p1.next
            if p2:
                p2 = p2.next
            
            temp = a1 + a2 + flag
            flag = temp // 10
            head.next = ListNode(temp % 10)
            head = head.next
            
        return res.next

二、两数相加II

此题为leetcode第445题
思路:和第一个题不一样的是,这里数字最高位是链表的开始,而且不能翻转链表。可以遍历链表将val存入栈中,这样出栈的时候就是低位先出来了。剩下相加的过程就和上一题几乎一样了。

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        stack1, stack2 = [], []
        p1, p2 = l1, l2
        while p1:
            stack1.append(p1.val)
            p1 = p1.next
        while p2:
            stack2.append(p2.val)
            p2 = p2.next
        
        res = None
        flag = 0
        while stack1 or stack2 or flag != 0:
            a1 = stack1.pop() if stack1 else 0
            a2 = stack2.pop() if stack2 else 0
            
            temp = a1 + a2 + flag
            flag = temp // 10
            node = ListNode(temp % 10)
            
            node.next = res
            res = node
        
        return res

三、重排链表

此题为leetcode第143题
思路:分为三步:(1)先利用快慢指针,找到链表的中点,将链表分为两部分;(2)反转后半部分;(3)两部分错位合并

class Solution:
    def reorderList(self, head: ListNode) -> None:
        """
        Do not return anything, modify head in-place instead.
        """
        # 链表长度小于等于2
        if head is None or head.next is None or head.next.next is None:
            return 

        # 快慢指针找到链表中间
        fast, slow = head, head
        while fast is not None and fast.next is not None:
            fast = fast.next.next
            slow = slow.next
        
        # 反转后半部分
        fast = slow.next
        slow.next = None
        fast = self.reverse(fast)

        # 后半部合并到前半部
        while fast:
            p2 = fast.next
            fast.next = head.next
            head.next = fast
            head = fast.next
            fast = p2
    
    # 反转链表
    def reverse(self, head):
        if head is None or head.next is None:
            return head
        
        temp = self.reverse(head.next)
        head.next.next = head
        head.next = None
        return temp

四、排序链表

此题为leetcode第148题
思路:(1)首先如果没有“常数级空间复杂度”的限制,此题使可以用归并排序,显然是递归操作,空间复杂度为 O ( n ) O(n) O(n)。在每层递归中,我们需要找到当前链表的中点。使用快慢指针,慢指针指向head,快指针指向head.next,每次慢指针走一步,快指针走两步,到结尾后慢指针后面部分的链表为被切割的一半链表。将这两半链表进行递归,返回排序好的连个链表的头指针,对这两个排好序的链表进行归并操作。

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        # 归并排序
        if not head or not head.next: 
            return head 
        # 切割链表,找到链表中间
        slow, fast = head, head.next
        while fast and fast.next:
            fast, slow = fast.next.next, slow.next
        mid, slow.next = slow.next, None 
        # 递归左右两边
        left, right = self.sortList(head), self.sortList(mid)
        # merge `left` and `right` linked list and return it.
        h = res = ListNode(0)
        while left and right:
            if left.val < right.val: 
                h.next, left = left, left.next
            else: 
                h.next, right = right, right.next
            h = h.next
        if left:
            h.next = left
        else:
            h.next = right
        return res.next

(2)在常数级空间复杂度的限制下,可以使用自底向上的归并排序,迭代完成。迭代的过程如下图所示:
在这里插入图片描述

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        # 自底向上
        # 计算链表长度
        h, length = head, 0
        while h:
            length += 1
            h = h.next
        
        intv = 1    # 每层归并时,小单元的长度
        res = ListNode(0)
        res.next = head
        # 开始归并
        while intv < length:
            prev, h = res, res.next
            while h:    # 对于每一个intv进行遍历
                # 找到第一个需要合并的单元的头节点
                h1, i = h, intv
                while h and i:
                    h, i = h.next, i - 1
                if i:   # 如果i大于0,说明h==none,那么这个intv层就只有这一个单元了,无法进行合并,进入下一层
                    break
                # 找到第二个需要合并的单元的右节点
                h2, i = h, intv
                while h and i:
                    h, i = h.next, i - 1
                # c1、c2为h1、h2的长度
                c1, c2 = intv, intv - i
                # 合并
                while c1 and c2:
                    if h1.val < h2.val:
                        prev.next, h1, c1 = h1, h1.next, c1 - 1
                    else:
                        prev.next, h2, c2 = h2, h2.next, c2 - 1
                    prev = prev.next
                # 合并结束后,将prev指向下次要合并的单元的头节点
                prev.next = h1 if c1 else h2
                while c1 > 0 or c2 > 0:
                    prev, c1, c2 = prev.next, c1 - 1, c2 - 1
                prev.next = h
            intv *= 2
        return res.next
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值