Java:LinkedList分块反转
如果为您提供了一个链表的头部,并且要求您反转每个k个节点序列,那么在Java中该如何做呢?e、 例如,Java:LinkedList分块反转,java,algorithm,Java,Algorithm,如果为您提供了一个链表的头部,并且要求您反转每个k个节点序列,那么在Java中该如何做呢?e、 例如,a->b->c->d->e->f->g->h如果k=3,则将是c->b->a->f->e->d->h->g->f 任何一般帮助或伪代码都将不胜感激!谢谢 这是相当高的级别,但我认为它会提供一些指导 我有一个助手方法,比如voidswap3(Node-first,Node-last),它在列表的任意位置获取三个元素并将它们反转。这应该不难,可以递归地完成(交换外部元素,递归内部元素,直到列表的大小
a->b->c->d->e->f->g->h
如果k=3,则将是c->b->a->f->e->d->h->g->f
任何一般帮助或伪代码都将不胜感激!谢谢 这是相当高的级别,但我认为它会提供一些指导 我有一个助手方法,比如
voidswap3(Node-first,Node-last)
,它在列表的任意位置获取三个元素并将它们反转。这应该不难,可以递归地完成(交换外部元素,递归内部元素,直到列表的大小为0或1)。现在我想起来了,如果使用递归,您可以很容易地将其概括为swapK()
完成后,您就可以沿着链接列表遍历并调用
swapK()
每个k
节点。如果列表的大小不能被k
整除,您可以不交换最后一位,或者使用交换技术反转最后一个长度%k
节点。如果k
预计相当小,我只会做最简单的事情:忽略它是一个链接列表的事实,并将每个子序列视为要反转的数组类型
因此,如果链接列表的节点类是节点
,请创建一个大小为k
的节点[]
。对于每个段,将k
节点加载到数组列表中,然后使用一个简单的For
循环反转它们的元素。在伪代码中:
// 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
优点:非常简单,O(N)性能,利用易于识别的反转算法
缺点:需要一个k
大小的数组(只需一次,因为每个段可以重复使用)
还请注意,这意味着每个节点
不会在列表中移动,只有节点
持有的对象才会移动。这意味着每个节点
最终将持有与以前不同的项目。这可能是好的,也可能不是,取决于您的需要。时间O(n);空间O(1)
列表反转的一个常见要求是在O(n)时间和O(1)空间中进行。这消除了递归、堆栈或临时数组(如果K==n怎么办?),等等。
因此,这里的挑战是修改就地反转算法,以考虑K
因素。我使用dist
表示距离,而不是K
这里有一个简单的就地反转算法:使用三个指针原地遍历列表: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;
}
例如,通过公共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)))))
(第k版(平方公里)
(如果(使用堆栈并递归地从列表中删除k个项目,则将它们推到堆栈中,然后将它们弹出并添加到位。不确定这是否是最佳解决方案,但堆栈提供了一种正确的反转方式。请注意,如果您没有列表,而是有队列,则此方法也有效。
只需将k个项目出列,将它们推到堆栈中,从堆栈中弹出并将它们放入队列:)此实现使用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);
}
LinkedList列表;
//在方法的参数检查之后在方法内部
ListIterator it=(ListIterator)list.iterator();
ListIterator reverseIt=(ListIterator)list.ListIterator(k);
对于(int i=0;i<(int)k/2;i++)
{
T元素=it.next();
it.set(reverseIt.previous());
反向集合(元素);
}
k=3将是c->b->a->f->e->d->h->g?递归使用额外的空间O(k),其中k可能是n。请参阅我的O(n)时间,O(1)空间解决方案。@kasavbere你在这里发了一点垃圾信息,imho。你发布了你的答案并解释得很好;没有必要回复几乎所有的答案只是为了指向你的答案。我们可以看到你的答案,没有这些。@yshavit,这不是我的本意。我只回复了那些不完美的帖子,指出它们的不足之处。我不喜欢告诉人们他们做错了什么,而不告诉他们“对”是什么样子;因此我指向我发布的回复。这是一个O(n)时间,O(1)空间算法。人们可以解决没有k
(或者说k=n
)的基本问题然后稍微修改一下,以解决kErm的情况,当然,你也可以只交换两个节点,节点和所有节点。这就是我在观看曲棍球时得到的答案…如果k不小呢?这个问题可以在O(n)时间和O(1)内解决空间,看我的solution@kasavbere是的,当然可以。不过,你的解决方案比我的复杂一点,所以如果你知道(从函数的req来自的任何来源)k很小,我会说使用更简单的代码。但你是对的,它有局限性,这就是为什么我显式地调用它们(两次,甚至两次)。递归使用太多空间。请参阅我的解决方案递归使用太多空间。请参阅我的解决方案太多空间。请参阅我的解决方案假设第一个算法是正确的,为什么不反向使用它?假设第一个算法是正确的
?很容易测试。使用插入
打印,编写一个int
链接列表类
和此反向功能。然后在main()中
method用int
s从1到x=26
或其他什么填充列表。然后对dist
的不同值进行反向操作。至于为什么不反向使用它呢?
不清楚你的意思。我的意思是你修改简单的反向操作,使它从一个索引位置反向到另一个索引位置(并保留一个额外的指针)。然后使用返回的最后一个指针,使用不同的索引反复调用simpleReverse
。类似这样的内容…节点simpleReverse(Node from,int len,Node NextFrom);这
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
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);
}