Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 有效地重新排列链表_Algorithm_Linked List - Fatal编程技术网

Algorithm 有效地重新排列链表

Algorithm 有效地重新排列链表,algorithm,linked-list,Algorithm,Linked List,我在一次采访中得到了以下问题: 如果我有这样一个链表 1->2->3->4->5->6 我必须把它转换成 1->6->2->5->3->4 如果是这样的话 1->2->3->4->5->6->7 我必须把它转换成 1->7->2->6->3->5->4 而且,最重要的是,我必须修改原始的链表,我不能创建新的链表。我想到了一个递归。但是,我无法真正解决它。此外,这是一个限制,即该函数只能具有链表的标题。这可以在线性时间O(n)内完成,通常在访谈期间(不幸的是)比解决方案的稳健性更重要 您可以通过将

我在一次采访中得到了以下问题:

如果我有这样一个链表

1->2->3->4->5->6

我必须把它转换成

1->6->2->5->3->4

如果是这样的话

1->2->3->4->5->6->7

我必须把它转换成

1->7->2->6->3->5->4


而且,最重要的是,我必须修改原始的链表,我不能创建新的链表。我想到了一个递归。但是,我无法真正解决它。此外,这是一个限制,即该函数只能具有链表的标题。

这可以在线性时间
O(n)
内完成,通常在访谈期间(不幸的是)比解决方案的稳健性更重要

您可以通过将原始列表拆分为两个(尽可能)大小相等的列表,然后反转第二个列表,并逐个元素(第一个列表中的第一个元素、第二个列表中的第二个元素等)合并这两个列表来实现。您不需要太多额外的空间,因为您可以只使用现有的指针

例如:

1->2->3->4->5->6

1->2->3 and 4->5->6 // after split, 3 points to null, 4 is head of second list
1->2->3 and 4<-5<-6 // after reorder
1->6->2->3 and 4<-5 // first phase of merge
1->6->2->5->3 and 4 // second phase of merge
1->6->2->5->3->4    // last phase of merge
1->2->3->4->5->6
1->2->3和4->5->6//拆分后,3点为空,4为第二个列表的头
1->2->3和42->3和46->2->5->3和4//合并的第二阶段
1->6->2->5->3->4//合并的最后阶段
您可以使用
运行指针找到拆分点。遍历列表时,一个指针一次指向一个节点,一个指针一次指向两个节点。当较快的指针到达端点(null)时,较慢的指针将位于拆分之前,拆分之前的节点必须指向null,而不是下一个节点(在本例中不是4),下一个节点(4)将成为第二个列表的头

反转第二个列表并合并是简单的指针交换


注意空指针:-)

这可以使用递归算法完成。您需要以“螺旋”方式遍历列表(先到后)。因此,我的想法是分离列表的第一个和最后一个元素,连接它们,然后递归地对其余元素执行相同的操作

下面是算法的大致轮廓:

modifyList(head):
    if head.next == null or head.next.next == null: # when the list contains 1 or 2 elements, keep it unchanged
        return head

    nextHead = head.next # head of the list after removing head and last item
    last = head.next
    beforeLast = head
    while last.next != null: # find the last item, and the item before it
        beforeLast = last
        last = last.next

    head.next = last # append last item after first
    beforeLast.next = null # remove the last item from list
    last.next = modifyList(nextHead) # recursively modify the 'middle' elements and append to the previous last item
    return head

这里有一种递归方法

创建一个函数
nreverse
,该函数可反转已放置的链接列表。Common Lisp具有此功能

盯着列表的开头,如果列表长度超过一个元素,
nreverse
第一个元素后面的列表。在计划中:

(setcdr listx (nreverse (cdr listx)))
在列表的下一个子列表上递归。以下是Common Lisp中的全部内容:

? (defun munge (listx)
  (let ((balx (cdr listx)))
    (if (null balx)
        listx
        (progn (rplacd listx (nreverse balx))
               (munge (cdr listx))
               listx))))
MUNGE
? (munge '(1 2 3))
(1 3 2)
? (munge '(1 2 3 4 5 6))
(1 6 2 5 3 4)
? (munge '(1 2 3 4 5 6 7))
(1 7 2 6 3 5 4)
? 

@John Lui有时迭代解更简单。值得考虑的是,这在n^2时间内