返回

融汇自然智慧,窥探循环链表的奥秘——LeetCode题解

后端

将循环链表转换为逆序链表

在数据结构的王国里,循环链表是一个独一无二的存在,它是一条首尾相连的链表,形成一个闭环。剑指Offer II 029. 排序的循环链表这道难题向我们发起了挑战,要求我们将一个按非递减顺序排序的循环链表变身为一个按非递增顺序排列的循环链表。

要破解这道难题,我们可以采取分而治之的策略:

第一步:化圆为直,抽丝剥茧

循环链表的独特之处在于它的首尾相连,这给我们带来了些许障碍。为了便于操作,我们首先要将它转化为一条常规的链表,也就是首尾相连的单向链表。

# 将循环链表转化为常规链表
def convert_circular_to_linear(head):
    if not head:
        return None
    
    # 找到尾节点
    tail = head
    while tail.next != head:
        tail = tail.next
    
    # 断开环形结构
    tail.next = None
    
    return head

第二步:快刀斩乱麻,排序链表

拿到了常规链表,我们就可以施展排序的魔法了。这里你可以选择你擅长的排序算法,比如冒泡排序、选择排序、快速排序或者归并排序。

# 对常规链表进行排序
def sort_linked_list(head):
    # 如果链表为空或只有一个节点,则直接返回
    if not head or not head.next:
        return head
    
    # 使用归并排序算法对链表进行排序
    # 归并排序:将链表拆分成两个较小的子链表,对子链表进行排序,然后再合并排序后的子链表
    def merge_sort(head):
        # 终止条件:链表为空或只有一个节点
        if not head or not head.next:
            return head
        
        # 拆分链表
        slow = head
        fast = head.next
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        
        mid = slow.next
        slow.next = None
        
        # 对子链表进行排序
        left = merge_sort(head)
        right = merge_sort(mid)
        
        # 合并排序后的子链表
        return merge(left, right)
    
    # 合并两个有序链表
    def merge(left, right):
        dummy = ListNode(0)
        curr = dummy
        
        while left and right:
            if left.val < right.val:
                curr.next = left
                left = left.next
            else:
                curr.next = right
                right = right.next
            curr = curr.next
        
        curr.next = left or right
        
        return dummy.next
    
    return merge_sort(head)

第三步:首尾相接,循环重生

常规链表排序完毕,我们就可以将其重新连接成循环链表了。

# 将排序后的常规链表转换为循环链表
def convert_linear_to_circular(head):
    if not head:
        return None
    
    # 找到尾节点
    tail = head
    while tail.next:
        tail = tail.next
    
    # 连接首尾
    tail.next = head
    
    return head

代码示例:

# 定义链表节点
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# 构建循环链表
def create_circular_linked_list(nums):
    if not nums:
        return None
    
    head = ListNode(nums[0])
    curr = head
    for num in nums[1:]:
        curr.next = ListNode(num)
        curr = curr.next
    
    # 连接尾节点和头节点,形成循环
    curr.next = head
    
    return head

# 打印链表
def print_linked_list(head):
    if not head:
        print("Empty list")
        return
    
    curr = head
    while curr:
        print(curr.val, end=" ")
        curr = curr.next
        if curr == head:
            break
    print()

# 测试代码
nums = [3, 5, 1, 2, 4]
head = create_circular_linked_list(nums)
print("Original circular linked list:")
print_linked_list(head)

head = convert_circular_to_linear(head)
head = sort_linked_list(head)
head = convert_linear_to_circular(head)

print("Sorted circular linked list:")
print_linked_list(head)

总结:

剑指Offer II 029. 排序的循环链表这道难题考验了我们的算法思维和数据结构的理解。通过将循环链表转换为常规链表,然后进行排序,再将排序后的常规链表重新连接为循环链表,我们成功破解了这道难题。

常见问题解答:

  1. 为什么需要将循环链表转换为常规链表?

因为循环链表的首尾相连会给排序带来不便,而常规链表可以方便地使用各种排序算法。

  1. 为什么选择归并排序算法?

归并排序是一种稳定的排序算法,可以保证链表中元素的相对顺序。

  1. 如何判断链表是否为空?
if not head:
    return None
  1. 如何找到链表的尾节点?
tail = head
while tail.next:
    tail = tail.next
  1. 如何连接首尾节点形成循环链表?
tail.next = head