[Python] 链表中的排序相关问题

前言

本文主要总结链表中排序相关的问题与解法。

合并两个排序的链表

合并k个已排序的链表

链表相加(二)

单链表的排序

链表的奇偶重排

删除有序链表中重复的元素-I

删除有序链表中重复的元素-II

这一类问题的基本都是根据题目给定的条件,对链表进行各种组合,如:

  • 基于归并排序思想,根据节点的数值,合并两个链表(合并两个排序的链表、合并k个已排序的链表)
  • 根据节点的位置,对链表重新排序(链表的奇偶重排)
  • 对两个链表节点的数值相加(链表相加(二))
  • 穿针引线(删除有序链表中重复的元素-I、删除有序链表中重复的元素-II)

一般套路是:

  • 新建一个虚拟头节点 dummpyNode = ListNode(-1) 和一个游走节点 pHead = dummpyNode
  • dummpyNode负责指返回链表,游走节点负责生成链表
  • 如果满足xx要求,则有 pHead.next = head; pHead = pHead.next;再开始下一轮循环
  • 由于在最前面,pHead.next = head,所以dummpyNode.next也就指向了整个链表的头部,后面dummpyNode一直没有动,只有pHead在游走。因此,返回 dummpyNode.next,也就是返回了整个链表。

基于归并排序

合并两个排序的链表

输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

输入:

{1,3,5},{2,4,6}

返回值:

{1,2,3,4,5,6}
# -*- coding:utf-8 -*-
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        # write code here
        dumpyNode = ListNode(-1)
        pHead = dumpyNode

        while pHead1 or pHead2:
            if pHead1 and pHead2:
                if pHead1.val<pHead2.val:
                    pHead.next = pHead1
                    pHead1 = pHead1.next
                else:
                    pHead.next = pHead2
                    pHead2 = pHead2.next
            elif pHead1:
                pHead.next = pHead1
                pHead1 = pHead1.next
            elif pHead2:
                pHead.next = pHead2
                pHead2 = pHead2.next

            pHead = pHead.next

        return dumpyNode.next

合并k个已排序的链表

前面合并两个排序的链表的对象,是两个单独的链表。对于有k个链表的数组,可以使用分治+递归的思想,把k个元素逐渐拆分k/2、k/4、...、2,就可以套用前面的merge函数。

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


class Solution:
    def mergeKLists(self , lists ):
        # write code here
        start = 0
        end = len(lists)
        return self.subMergeKLists(lists, start, end-1)

    def subMergeKLists(self , lists, start, end):
        if start>end:
            return None
        elif start==end:
            return lists[start]
        # mid = int((end-start)/2)+start
        mid = (end-start)/2+start
        pHead1 = self.subMergeKLists(lists, start, mid)
        pHead2 = self.subMergeKLists(lists, mid+1, end)
        return self.merge(pHead1, pHead2)

    def merge(self, pHead1, pHead2):
        # write code here
        dumpyNode = ListNode(-1)
        pHead = dumpyNode

        while pHead1 or pHead2:
            if pHead1 and pHead2:
                if pHead1.val<pHead2.val:
                    pHead.next = pHead1
                    pHead1 = pHead1.next
                else:
                    pHead.next = pHead2
                    pHead2 = pHead2.next
            elif pHead1:
                pHead.next = pHead1
                pHead1 = pHead1.next
            elif pHead2:
                pHead.next = pHead2
                pHead2 = pHead2.next

            pHead = pHead.next

        return dumpyNode.next

细节提示

需要注意判断递归停止的条件是:

  1. 当start>end时,返回None
  2. 当start==end时,返回一个链表
  3. 否则,继续分治

链表相加

链表相加(二)

假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。

给定两个这种链表,请生成代表两个整数相加值的结果链表。

要求:空间复杂度 O(n),时间复杂度 O(n)

例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。

输入:

[9,3,7],[6,3]

返回值:

{1,0,0,0} 

整体思路

如题目,链表的顺序与加法的顺序是相反的,自然的想到两种思路:

  1. 把链表的元素压入栈中,借助栈实现对反转链表的元素进行操作;
  2. 直接反转链表

由于两种方式都需要新建链表,存储两个整数的相加值,因此空间复杂度都是o(n)。方法1比2多一个栈的空间,但是总的空间复杂度也是o(n)。

细节提示

  1. 加法的10进制的进位。设置进位标志incre,每次循环判断 val1 = list1.pop(-1)+list2.pop(-1)+incre。并且,在循环结束后,需要判断incre是否>0,如果>0,需要在链表中增加
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def addInList(self , head1 , head2 ):
        # write code here
        list1 = []
        while head1:
            list1.append(head1.val)
            head1 = head1.next

        list2 = []
        while head2:
            list2.append(head2.val)
            head2 = head2.next

        list3 = []
        incre = 0
        while len(list1) and len(list2):
            val1 = list1.pop(-1)+list2.pop(-1)+incre
            incre = val1/10
            val1 = val1%10
            list3.append(val1)
   
        while len(list1):
            val1 = list1.pop(-1)+incre
            incre = val1/10
            val1 = val1%10
            list3.append(val1)

        while len(list2):
            val1 = list2.pop(-1)+incre
            incre = val1/10
            val1 = val1%10
            list3.append(val1)
        
        if incre>0:
            list3.append(incre)
 
        dumpyNode = ListNode(-1)
        pHead = dumpyNode
        
        while len(list3):
            pHead.next = ListNode(list3.pop(-1))
            pHead = pHead.next
        return dumpyNode.next

    def addInList2(self , head1 , head2 ):
        cur1 = head1
        pre = None
        while cur1:
            next1 = cur1.next
            cur1.next = pre
            pre = cur1
            cur1 = next1
        head1 = pre
        
        cur2 = head2
        pre2 = None
        while cur2:
            next2 = cur2.next
            cur2.next = pre2
            pre2 = cur2
            cur2 = next2
        head2 = pre2

        dumpyNode3 = ListNode(-1)
        pHead = dumpyNode3
        incre = 0
        while head1 and head2:  
            val = head1.val+head2.val+incre
            incre = val/10
            val = val%10
            head = ListNode(val)
            pHead.next = head
            pHead = pHead.next
            head1 = head1.next
            head2 = head2.next

        while head1:
            val = head1.val+incre
            incre = val/10
            val = val%10
            head = ListNode(val)
            pHead.next = head
            pHead = pHead.next
            head1 = head1.next
        
        while head2:
            val = head2.val+incre
            incre = val/10
            val = val%10
            head = ListNode(val)
            pHead.next = head
            pHead = pHead.next
            head2 = head2.next

        if incre>0:
            head = ListNode(incre)
            pHead.next = head
            pHead = pHead.next

        pHead = dumpyNode3.next

        cur1 = pHead
        pre = None
        while cur1:
            next1 = cur1.next
            cur1.next = pre
            pre = cur1
            cur1 = next1
        
        return pre

排序

单链表的排序

给定一个节点数为n的无序单链表,对其按升序排序。

数据范围:0 < n \le 1000000<n≤100000

输入:

[1,3,2,4,5]

复制返回值:

{1,2,3,4,5}
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def sortInList(self , head ):
        # write code here
        list1 = []
        while head:
            list1.append(head.val)
            head = head.next

        list1.sort()

        dummpyHead = ListNode(-1)
        pHead = dummpyHead
        while list1:
            pHead.next = ListNode(list1.pop(0))
            pHead = pHead.next
        
        return dummpyHead.next

 链表的奇偶重排

给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。

注意是节点的编号而非节点的数值。

要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

输入:

{1,2,3,4,5,6}

返回值:

{1,3,5,2,4,6}
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def oddEvenList(self , head ):
        # write code here
        dumpyHead = ListNode(-1)
        pHead = dumpyHead

        dumpyHead2 = ListNode(-1)
        pHead2 = dumpyHead2

        i = 1
        while head:
            val = head.val
            if i%2 == 0:
                pHead.next = ListNode(val)
                pHead = pHead.next
            else:
                pHead2.next = ListNode(val)
                pHead2 = pHead2.next
            head = head.next
            i = i+1
        pHead2.next = dumpyHead.next

        return dumpyHead2.next

元素删除

删除有序链表中重复的元素-I

删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次

进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

输入:

{1,1,2}

返回值:

{1,2}
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def deleteDuplicates(self , head ):
        # write code here
        dumpyNode = ListNode(-1)
        dumpyNode.next = head

        cur = head
        while cur:
            while cur.next and cur.val == cur.next.val:
                cur.next = cur.next.next

            cur = cur.next

        return dumpyNode.next

删除有序链表中重复的元素-II 

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

要求:空间复杂度 O(n),时间复杂度 O(n)

进阶:空间复杂度 O(1),时间复杂度 O(n)

输入:

{1,2,2}

复制返回值:

{1}
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None


class Solution:
    def deleteDuplicates(self , head ):
        # write code here
        dumpyNode = ListNode(-1)
        dumpyNode.next = head

        cur = head
        pre = dumpyNode

        is_re = 0
        while cur:
            while cur.next and cur.val == cur.next.val:
                cur.next = cur.next.next
                is_re = 1
            
            if is_re==0:
                pre = cur
                cur = cur.next
            else:
                pre.next = cur.next
                cur = cur.next
            
            is_re = 0
        
        return dumpyNode.next

细节提示

注意删除有序链表中重复的元素-I和删除有序链表中重复的元素-II 两道题的区别。

1中的重复元素可以保留,所以可以直接用cur来遍历链表;

2中的重复元素不可以保留,所以需要使用pre来跳过,且分两种情况:

  • cur是重复元素,跳过cur,pre.next = cur.next, cur = cur.next
  • cur不是重复元素,逐个遍历,pre = cur,cur = cur.next 

总结

其实上面的题目的思路都很简单,相当于把简单的排序从数组迁移到了链表中。个人认为技巧在于链表节点的生成与穿针引线,一般可以使用两个辅助节点,定义虚拟节点和游走节点,虚拟节点负责返回整个链表,游走节点负责穿针引线。

  • 新建一个虚拟头节点 dummpyNode = ListNode(-1) 和一个游走节点 pHead = dummpyNode
  • dummpyNode负责指返回链表,游走节点负责生成链表
  • 如果满足xx要求,则有 pHead.next = head; pHead = pHead.next;再开始下一轮循环
  • 由于在最前面,pHead.next = head,所以dummpyNode.next也就指向了整个链表的头部,后面dummpyNode一直没有动,只有pHead在游走。因此,返回 dummpyNode.next,也就是返回了整个链表。
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
链表归并排序是一种常见的排序算法,用于对链表进行排序。下面是一个 Python 实现的链表归并排序的示例代码: ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def merge_sort(head): if not head or not head.next: return head # 找到链表点 slow, fast = head, head.next while fast and fast.next: slow = slow.next fast = fast.next.next right_head = slow.next slow.next = None # 递归地对子链表进行归并排序 left_sorted = merge_sort(head) right_sorted = merge_sort(right_head) # 合并两个有序链表 dummy = ListNode(0) curr = dummy while left_sorted and right_sorted: if left_sorted.val < right_sorted.val: curr.next = left_sorted left_sorted = left_sorted.next else: curr.next = right_sorted right_sorted = right_sorted.next curr = curr.next if left_sorted: curr.next = left_sorted else: curr.next = right_sorted return dummy.next ``` 这个示例代码,我们定义了一个 `ListNode` 类来表示链表节点。`merge_sort` 函数使用递归的方式实现了链表归并排序。首先找到链表点,然后将链表切分为两个子链表,分别对两个子链表进行归并排序,最后合并两个有序子链表并返回结果。 你可以使用以下代码测试上述实现: ```python # 创建链表 head = ListNode(4) node1 = ListNode(2) node2 = ListNode(1) node3 = ListNode(3) head.next = node1 node1.next = node2 node2.next = node3 # 打印排序前的链表 curr = head while curr: print(curr.val, end=" ") curr = curr.next # 对链表进行归并排序 sorted_head = merge_sort(head) # 打印排序后的链表 print("\nSorted list:") curr = sorted_head while curr: print(curr.val, end=" ") curr = curr.next ``` 以上代码首先创建了一个包含 4、2、1、3 的链表,然后对链表进行归并排序,并打印排序前后的链表结果。 希望能对你有所帮助!如果有任何问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值