序文
特別なトピックの形でブラッシングの投稿を更新します。私と一緒にブラッシングを学ぶことを歓迎します。私を信じてください。あなたの粘り強さは間違いなく予想外の利益をもたらします。それぞれの質問は簡単な答えを提供します、あなたがよりエレガントなアプローチを持っているなら、ガイダンスを提供することを歓迎します、ありがとう
【トピック説明】
【請求】
入力:循環単一リンクリストのヘッドノードと報告された数m。
戻り値:最後に残ったノード。このノードはそれ自体で循環単一リンクリストを形成し、他のすべてのノードは削除されます。
【困難】
タクシー:★☆☆☆
【回答】
方法1:時間の複雑さはO(n * m)です
時間の複雑さを考慮しなければ、この問題は非常に単純です。循環リンクリストをトラバースし、m個のノードをトラバースするたびにノードを削除します。リンクリストに残っているノードは1つだけであることを知っていれば十分です。
コードは以下のように表示されます
1 //时间复杂度为O(n*m)的解决方法
2 public static Node josephusKill(Node head, int m) {
3 if(head == null || m < 1)
4 return head;
5 Node last = head;
6 //定位到最后一个节点
7 while (head.next != last) {
8 head = head.next;
9 }
10 int count = 0;
11 while (head.next != head) {
12 if (++count == m) {
13 head.next = head.next.next;
14 count = 0;
15 } else {
16 head = head.next;
17 }
18 }
19 return head;
20 }
一部の携帯電話では文字が文字化けしている可能性があるため、ここに写真を投稿します。
このメソッドの時間の複雑さはO(n * m)です。時間の複雑さは、以下の解決策として使用されます。
方法2:時間の複雑さはO(n)です
この方法の難しさは次のとおりです。
学校:★★★☆
循環リンクリストのノードに番号を付けることができます。リンクリストのノード数がnの場合、ヘッドノードから始めて、ノードに順番に番号を付けます。つまり、ヘッドノードは1、次のノードは2、最後のノードはnです。
f(n)を使用して、循環リンクリストの長さがnの場合、生存者の数はf(n)であることを示します。明らかに、n = 1の場合、f(n)= 1です。f(n)とf(n-1)の関係がわかれば、再帰的に解くことができます。人数はn人、mに報告した人は自殺すると仮定します。その場合、最初の番号は
…
m-2
m-1
m
m + 1
m + 2
…
1回削除すると、mという番号のノードが削除されます。削除後、残っているノードはn-1個だけです。削除前後の数値変換関係は次のとおりです。
削除する前---削除した後
…---…
m-2 --- n-2
m-1 --- n-1
m ----なし(番号が削除されているため)
m + 1 --- 1(次回はここから数えるため)
m + 2 ---- 2
…----…
新しいリングにはn-1個のノードしかありません。そして、m + 1、m + 2、m + 3の番号が付けられたノードは、新しいリングで1、2、3の番号が付けられたノードになります。
oldが削除前のノード番号、newがノード削除後の番号であるとすると、oldとnewの関係はold =(new + m-1)%n +1です。
注:なぜ古いものではないのか疑問に思う人もいるかもしれません=(新しい+ m)%n?主な理由は、番号付けが0からではなく1から始まるためです。new + m == nの場合、最終的な計算結果はold = 0になります。とても古い=(新しい+ m-1)%n +1。
このようにして、f(n)とf(n-1)の間の関係、およびf(1)= 1を取得します。したがって、再帰的な方法でそれを行うことができます。
コードは次のように表示されます。
1 //时间复杂度为O(n)
2 public static Node josephusKill2(Node head, int m) {
3 if(head == null || m < 1)
4 return head;
5 int n = 1;//统计一共有多少个节点
6 Node last = head;
7 while (last.next != head) {
8 n++;
9 last = last.next;
10 }
11 //直接用递归算出目的编号
12 int des = f(n, m);
13 //把目的节点取出来
14 while (--des != 0) {
15 head = head.next;
16 }
17 head.next = head;
18 return head;
19 }
20
21 private static int f(int n, int m) {
22 if (n == 1) {
23 return 1;
24 }
25 return (f(n - 1, m) + m - 1) % n + 1;
26 }
画像コード:
問題の拡大
前の質問で、K番目のノードから番号を削除するとしますか?それを解決する方法は?
回答
舞台裏の返信回答3回答を得る
過去
【リンクリストの問題】単一リンクリストの中間ノードを削除する
[リンクリストの問題]単一リンクリストをエレガントに反転する方法
[リンクリストの問題]単一リンクリストのK番目のノードを削除します
[アルゴリズムの戦闘]ウィンドウの最大値の配列を生成する