リンクリストの先頭が提供され、ノードのkシーケンスごとに逆にするように求められた場合、Javaでこれをどのように行うことができますか?たとえば、a->b->c->d->e->f->g->h
k=3の場合はc->b->a->f->e->d->h->g->f
一般的なヘルプや擬似コードでさえも大歓迎です!ありがとう!
かなり小さいと予想される場合k
は、最も単純なことを選択します。リンクリストであるという事実を無視し、各サブシーケンスを逆にする配列型のものとして扱います。
したがって、リンクリストのノードクラスがである場合は、サイズNode<T>
のを作成します。セグメントごとに、配列リストにロードしてから、単純なループで要素を逆にします。擬似コードの場合:Node<?>[]
k
k
Nodes
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
サイズの配列が必要です(セグメントごとに再利用できるため、1回だけ)
Node
また、これは、それぞれがリスト内で移動せず、Node
保持しているオブジェクトのみが移動することを意味することに注意してください。これは、それぞれNode
が以前とは異なるアイテムを保持することになることを意味します。必要に応じて、これで問題ない場合もあります。
これはかなりハイレベルですが、ある程度のガイダンスになると思います。
void swap3(Node first, Node last)
リストの任意の位置にある3つの要素を取得して、それらを逆にするようなヘルパーメソッドがあります。これは難しいことではなく、再帰的に実行できます(外側の要素を交換し、リストのサイズが0または1になるまで内側の要素を再帰的に実行します)。考えてみると、swapK()
再帰を使用している場合は、これを簡単に一般化できます。
それが完了したら、リンクリストに沿って歩き、swapK()
すべてのk
ノードを呼び出すことができます。リストのサイズがで割り切れない場合は、最後のビットをスワップしないか、スワップ手法を使用して最後のノードをk
逆にすることができます。length%k
時間O(n); スペースO(1)
リスト反転の通常の要件は、O(n)時間とO(1)空間で行うことです。これにより、再帰、スタック、または一時配列(K == nの場合はどうなりますか?)などが排除されます。したがって、ここでの課題は、K
ファクターを考慮してインプレース反転アルゴリズムを変更することです。距離K
に使う代わりに。dist
単純なインプレース反転アルゴリズムを次に示します。3つのポインターを使用して、リストを所定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]リストのノードが1つ以下の場合、または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
}
例: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
スタックを使用して、リストから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);
}