【问题标题】:Merge sorting a linked list对链表进行合并排序
【发布时间】:2014-08-25 21:51:46
【问题描述】:

我在使用链表上的合并排序时遇到了问题。我发现的问题是我的分区方法 中的问题。

每当我分区并请求前半部分时,另一半结果与前半部分完全相同。但如果我不请求前半部分,后半部分会正确返回。下面是我的代码;

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

def print_list(root):
  while root:
    print root.val,
    root = root.next
  print ''

def partition(root, is_first_half):
  print_list(root)
  if not root.next:
    return root
  slow_pointer, fast_pointer = root, root.next
  while fast_pointer and fast_pointer.next != None:
    fast_pointer = fast_pointer.next.next
    slow_pointer = slow_pointer.next
  if not is_first_half:
    return slow_pointer
  first_half, first_half_head, first_half_pointer = None, None, root
  while first_half_pointer and first_half_pointer != slow_pointer:
    if not first_half_head:
      first_half = first_half_pointer
      first_half_head = first_half
    else:
      first_half.next = first_half_pointer
      first_half = first_half_pointer
      first_half.next = None
    first_half_pointer = first_half_pointer.next
  return first_half_head


def merge(list1, list2):
    new_list_head, new_list = None, None
    if not list1:
      return list2
    if not list2:
      return list1
    while list1 and list2:
      if list1.val < list2.val:
        if not new_list_head:
          new_list = list1
          new_list_head = new_list
        else:
          new_list.next = list1
          new_list = list1
          new_list.next = None
        list1 = list1.next
      else:
        if not new_list_head:
          new_list = list2
          new_list_head = new_list
        else:
          new_list.next = list2
          new_list = list2
          new_list.next = None
        list2 = list2.next
    if not list1:
      while list2:
        new_list.next = list2
        new_list = list2
        new_list.next = None
        list2 = list2.next
      return new_list_head
    while list1:
      new_list.next = list1
      new_list = list1
      new_list.next = None
      list1 = list1.next
    return new_list_head


def mergesort(root):
  if not root.next:
    return root
  list1 = mergesort(partition(root, True))
  list2 = mergesort(partition(root, False))
  return merge(list1, list2)


if __name__ == '__main__':
  a = ListNode(2, ListNode(4, ListNode(3, ListNode(1, ListNode(7)))))
  print_list(a)
  a = mergesort(a)
  print_list(a)

【问题讨论】:

  • 你正在丢弃它看起来的列表。在list1 = ...之后放一个'print_list(root)',你会发现整个列表都不存在了。

标签: python algorithm data-structures linked-list partitioning


【解决方案1】:

您的merge 方法似乎有一个错误,它会从列表中删除一堆元素。想想以下代码行中发生了什么:

      new_list.next = None

您实际上是在删除列表的其余部分。每当您执行合并时,您似乎将保留最小的元素,加上每个列表中的最小元素,总共三个元素。试试下面的main 方法看看我的意思:

if __name__ == '__main__':
  a = ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5)))))
  b = ListNode(6, ListNode(7, ListNode(8, ListNode(9, ListNode(10)))))
  print_list(a)
  print_list(b)
  #a = mergesort(a)
  c = merge(a, b)
  print_list(c)
  print_list(a)
  print_list(b)

【讨论】:

    【解决方案2】:

    首先,在前半部分的代码中,对于您调用的第二个元素

    first_half = first_half_pointer
    first_half.next = None
    

    然后

    first_half_pointer = first_half_pointer.next
    

    所以first_half_pointer 变成None 并且循环结束,所以前半部分总是不超过两个元素。

    其次,更重要的是,您通过这一行打破了原始列表的连通性:

    first_half.next = None
    

    所以在partition(root, True) 之后,列表的后半部分就丢失了。

    【讨论】:

      【解决方案3】:

      您是否希望您的分区具有破坏性?也就是说,在您运行分区后,原始列表是否应该仍然存在所有值?任何一种方式都可以分区。

      破坏性分区可能会快一点,因为它只需要取消链接一个节点(尽管找到正确的节点是O(N))。要非破坏性地获取列表的前半部分,您需要复制它(为每个值创建新节点)。这也是O(N),但可能由于内存分配而具有更大的常数项。

      这是一个快速破坏性分区。它使用与您的代码不同的初始值,因此我们可以直接在 slow_pointer 之后而不是在它之前进行拆分(这更困难)。一次只获取破坏性分区的一半也没有什么意义,因为如果您请求前半部分,后半部分将变得无法访问,因此我们总是返回两半。

      def partition(root):
          fast_pointer = slow_pointer = ListNode(None, root)  # new node "before the root"
          while fast_pointer and fast_pointer.next:
              fast_pointer = fast_pointer.next.next
              slow_pointer = slow_pointer.next
          mid = slow_pointer.next
          slow_pointer.next = None                            # break one link in the list
          return root, mid                                    # return both halves of the list
      

      这是一个非破坏性的替代版本。列表的后半部分是对与原始节点相同的节点的引用,但返回的前半部分是新节点中的副本

      def partition_non_destructive(root):
          root = fast_pointer = slow_pointer = ListNode(None, root)  # root anchors the copy
          while fast_pointer and fast_pointer.next:
              fast_pointer = fast_pointer.next.next
              slow_pointer.next = ListNode(slow_pointer.next.value,  # copy the next node
                                           slow_pointer.next.next)
              slow_pointer = slow_pointer.next  # slow_pointer is always the latest copied node
          mid = slow_pointer.next
          slow_pointer.next = None                           # unlink copied half from the rest
          return root.next, mid                              # return both halves of the list
      

      这两个函数都适用于任意长度的列表,包括长度为 1 (ListNode("foo")) 和长度为零 (None) 的列表。对于奇数长度的列表,返回的前半部分总是大于后半部分。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-27
        • 2010-10-20
        相关资源
        最近更新 更多