合并排序链表

时间:2014-08-25 21:51:47

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

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

每当我分区并请求前半部分时,另一半结果就是前一个的相同。但如果我没有请求上半场,则下半场正确返回。以下是我的代码;

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)

3 个答案:

答案 0 :(得分:1)

首先,在上半部分的代码中,为第二个元素调用

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)之后,列表的后半部分会丢失。

答案 1 :(得分: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 :(得分:1)

您是否打算对分区进行破坏?也就是说,在运行分区后原始列表是否仍然存在及其所有值?你可以分道。

破坏性分区可能会快一些,因为它只需要取消一个节点的链接(尽管找到正确的节点是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)。对于奇数长度列表,返回的前半部分将始终大于后半部分。