如果您被提供链表的头部,并被要求反转每个k序列的节点,那么如何在Java中完成?例如,k = 3的a->b->c->d->e->f->g->h
将为c->b->a->f->e->d->h->g->f
任何一般帮助甚至伪代码都将不胜感激!谢谢!
答案 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
以便于在b
和c
之间进行交换。
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);
}