Java:块中的LinkedList反转

时间:2012-04-22 21:23:44

标签: java algorithm

如果您被提供链表的头部,并被要求反转每个k序列的节点,那么如何在Java中完成?例如,k = 3的a->b->c->d->e->f->g->h将为c->b->a->f->e->d->h->g->f

任何一般帮助甚至伪代码都将不胜感激!谢谢!

6 个答案:

答案 0 :(得分:4)

如果预期k相当小,我会选择最简单的事情:忽略它根本就是链表的事实,并将每个子序列视为一个数组类型的东西到被逆转。

因此,如果您的链接列表的节点类是Node<T>,请创建大小为Node<?>[]的{​​{1}}。对于每个细分,请将k k加载到数组列表中,然后使用简单的Nodes循环反转其元素。在伪代码中:

for

优点:非常简单,O(N)性能,利用易于识别的反转算法。

缺点:需要一个// reverse the elements within the k nodes for i from 0 to k/2: nodeI = segment[i] nodeE = segment[segment.length-i-1] tmp = nodeI.elem nodeI.elem = nodeE.elem nodeE.elem = tmp 大小的数组(只需一次,因为您可以按段重用它)

另请注意,这意味着每个k都不会在列表中移动,只会移动Node所持有的对象。这意味着每个Node最终将持有与之前不同的项目。根据您的需要,这可能没有问题。

答案 1 :(得分:1)

这是非常高级别的,但我认为它会提供一些指导。

我有一个像void swap3(Node first, Node last)这样的辅助方法,它在列表的任意位置取三个元素并反转它们。这应该不难,并且可以递归地完成(交换外部元素,递归内部元素,直到列表的大小为0或1)。现在我想起来了,如果你正在使用递归,你可以很容易地将它推广到swapK()

完成后,您就可以沿着链接列表走,并在每个swapK()个节点上调用k。如果列表的大小不能被k分割,您可以不使用交换技术交换最后一位,或者反转最后length%k个节点。

答案 2 :(得分:1)

TIME O(n);空间O(1)

列表反转的通常要求是在O(n)时间和O(1)空间中执行。这消除了递归或堆栈或临时数组(如果K == n?),等等。 因此,这里的挑战是修改就地反转算法以考虑K因子。而不是K我使用dist作为距离。

这是一个简单的就地反转算法:使用三个指针来遍历列表:b指向新列表的头部; c指向未处理列表的移动头; a以便于在bc之间进行交换。

 A->B->C->D->E->F->G->H->I->J->L //original
 A<-B<-C<-D    E->F->G->H->I->J->L //during processing
          ^    ^
          |    |
          b    c
 `a` is the variable that allow us to move `b` and `c` without losing either of
  the lists.

Node simpleReverse(Node n){//n is head
if(null == n || null == n.next)
   return n;
Node a=n, b=a.next, c=b.next;
a.next=null; b.next=a;
while(null != c){
   a=c;
   c=c.next;
   a.next=b;
   b=a;
}
return b;
}

要将simpleReverse算法转换为chunkReverse算法,请执行以下操作:

1]在倒转第一个块后,将head设置为b; head是结果列表的永久负责人。

2]对于所有其他块,请将tail.next设置为b;回想一下,b是刚处理过的块的头部。

其他一些细节:

3]如果列表中有一个或更少的节点或dist为1或更小,则返回列表而不进行处理。

4]使用计数器cnt来跟踪dist个连续节点何时被反转。

5]使用变量tail来跟踪刚刚处理的块的尾部,并使用tmp来跟踪正在处理的块的尾部。

6]注意在处理一个块之前,它的头部(它必然会成为它的尾部)是你遇到的第一个节点:所以,将它设置为tmp,这是一个临时尾部。

public Node reverse(Node n, int dist) {
  if(dist<=1 || null == n || null == n.right) 
      return n;
  Node tail=n, head=null, tmp=null;
  while(true) {
      Node a=n, b=a.right; n=b.right;
      a.right=null; b.right=a;
      int cnt=2;
      while(null != n && cnt < dist) {
          a=n; n=n.right; a.right=b; b=a;
          cnt++;
      }
      if(null == head) head = b;
      else {
          tail.right=b;tail=tmp;
      }
      tmp=n;
      if(null == n) return head;
      if(null == n.right) {
          tail.right=n;
          return head;
      }
  }//true
  }

答案 3 :(得分:1)

E.g。通过Common Lisp

(defun rev-k (k sq)
  (if (<= (length sq) k)
      (reverse sq)
      (concatenate 'list (reverse (subseq sq 0 k)) (rev-k k (subseq sq k)))))

其他方式 例如。通过F#使用Stack

open System.Collections.Generic
let rev_k k (list:'T list)  =
  seq {
    let stack = new Stack<'T>()
    for x in list do
      stack.Push(x)
      if stack.Count = k then
        while stack.Count > 0 do
          yield stack.Pop()
    while stack.Count > 0 do
      yield stack.Pop()
  }
  |> Seq.toList

答案 4 :(得分:0)

使用堆栈并递归删除列表中的k个项目,将它们推送到堆栈然后弹出它们并将它们添加到位。不确定它是否是最佳解决方案,但堆栈提供了一种正确的反转方式。请注意,如果您有一个队列而不是列表,这也有效。 简单地将k项出列,将它们推入堆栈,从堆栈中弹出它们并将它们排队:)

答案 5 :(得分:0)

此实现使用ListIterator类:

LinkedList<T> list;
//Inside the method after the method's parameters check
ListIterator<T> it = (ListIterator<T>) list.iterator(); 
ListIterator<T> reverseIt =  (ListIterator<T>) list.listIterator(k); 

for(int i = 0; i< (int) k/2; i++ )
{
    T element  = it.next();
    it.set(reverseIt.previous());
    reverseIt.set(element);
}