記事ディレクトリ
リンクリストの高速ポインタと低速ポインタ
1リンクリストの概要
2高速ポインタと低速ポインタの魔法の使用
- 速度ポインターは2つのポインターを定義することです。
- 動きの速度は速く、遅いので、望ましい違いが生まれます
- この違いにより、リンクされたリストで対応するノードを見つけることができます
2.1中央値を見つける
- 一般的なアイデア:最初にリンクリストを1回走査し、ノードの数を記録します。
- 次に、もう一度トラバースして中点を見つけます。
- 高速ポインタと低速ポインタを使用する
- 最初は、低速ポインタと高速ポインタの両方がリンクリストの最初のノードを指しています。
- 次に、ゆっくりと1つずつポインタを動かし、速くすると2つずつポインタを動かします
2.2リンクされたリストのリングを判断する
- ラウンドトラックでは、2人の速度差があり、遅かれ早かれ出会うことになりますが、出会うと輪があることになります。
- リングに2つのノードが追加されます。2つのポインターがリングにぶつかっている限り、速度の違いにより、それらの間の距離は常に遠くから近くになると想像できます。それは私たちの世界の何人かの人々があなたの人生で通り過ぎるような感覚です。
2.3最後から2番目のノードを削除する
- まず、削除する要素の前にある要素(n-1番目のノード)を見つけます。
- リンクされたリストでノードを見つけるという問題に変わりました。これは、高速ポインタと低速ポインタが最も得意とするシーンです。
- (n-1)番目の要素を見つける方法は?
- 最初は、高速ポインタは低速ポインタよりn + 1要素高速です。次に、両方のポインタが段階的に下がります。
- 高速で終了すると、低速ポインタは(n-1)番目の要素にとどまります。
- n = 2の場合(dummyHeadは手動で追加された仮想ヘッドノードです):
ダブルポインター
- 2つのポインター手法は2つのカテゴリーに分類されます。
- スピードポインター、
- 左手と右手。
- 前者は主にリンクリストの問題を解決し、
- リンクされたリストにリングが含まれているかどうかの判定など。
- 後者は主に配列(または文字列)の問題を解決します。
- たとえば、バイナリ検索。
まず、高速ポインタと低速ポインタの一般的なアルゴリズム
リンクリストにリングが含まれていることがわかったら、リングの開始位置に戻ります
ListNode detectCycle(ListNode head) {
ListNode fast, slow;
fast = slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow)
break;
}
slow = head;
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
- 初めて会議を行うとき、スローはkステップ歩き、次に高速ポインタは2ステップ歩きます
- 高速は低速よりもkステップかかります
- kはリングの長さ
- 速度の違いは1つしかないためです。!!
- 待ち合わせ点とリングの始点の間の距離をmとすると、
- リングの始点とヘッドノード間の距離はk-m、
- つまり、頭からk-mステップ進むと、リングの先頭に到達できます。
- 遭遇地点からk-mを進め続けると、リングの最初に到達することも起こります。
- ヘッドへの高速ポインターと低速ポインターのいずれかを再ポイントし、2つのポインターが同じ速度で前進する限り、それらはk-mステップ後に出会います。出会いポイントはリングの始点です。
リンクされたリストの中点を見つける
ListNode slow, fast;
slow = fast = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
// slow 就在中间位置
return slow;
- 長さが奇数の場合、スローは偶然中間点で停止します。
- でも、slowの最終位置は中央の右側です。
- リンクリストの中間点を見つける重要な機能は、リンクリストをマージしてソートすることです。
配列がマージおよびソートされていることを思い出してください。中点インデックスを見つけて、配列を再帰的に2つに分割し、最後に2つの順序付けられた配列をマージします。リンクされたリストの場合、2つの順序付けられたリンクされたリストをマージすることは非常に簡単です。困難は二分法にあります。
しかし、リンクリストの中点を見つける方法を学習したので、リンクリストの二分法を達成できます。この記事では、マージソートの特定の内容については説明しません。
リンクリストの最後から2番目の要素を見つける
- 最初に速い手でkステップ進み、次に速い手と遅い手が同じ速度で前進し始めます。
- このようにして、高速ポインタがリンクリストの最後でnullに到達すると、低速ポインタの位置は最後から2番目のリンクリストノードになります(簡単にするために、kはリンクリストの長さを超えないと想定されています)。
ListNodeが遅い、速い。
遅い=速い=頭;
while(k--> 0)
fast = fast.next;
while(fast!= null){
slow = slow.next;
fast = fast.next;
}
ゆっくり戻ります。
第二に、左と右のポインターの一般的に使用されるアルゴリズム
- 左と右のポインタは、実際には配列内の2つのインデックス値を参照します。
- 通常、初期化は左= 0、右= nums.length-1です。
1.バイナリサーチ
以前のバイナリサーチアルゴリズムについて詳しく説明しますが、ここでは、デュアルポインターの特性を強調することを目的とした、最も単純なバイナリサーチアルゴリズムのみを記述します。
int binarySearch(int [] nums、int target){
int left = 0;
int right = nums.length-1;
while(左<=右){
int mid =(右+左)/ 2;
if(nums [mid] == target)
return mid;
else if(nums [mid] <target)
left = mid + 1;
else if(nums [mid]> target)
right = mid-1;
}
}
2. 2つの数値の合計
LeetCodeのタイトルを直接見てみましょう。
配列が順序付けられている限り、ダブルポインター手法を考える必要があります。この問題の解決策は、バイナリ検索と多少似ていますが、合計のサイズは、左右を調整することで調整できます。
3.配列を逆にします
void reverse(int [] nums){
int left = 0;
int right = nums.length-1;
while(left <right){
// swap(nums [left]、nums [right])
int temp = nums [left];
nums [左] = nums [右];
nums [right] = temp;
左++;
正しい-;
}
}
4.スライディングウィンドウアルゴリズム
これは、ダブルポインターテクニックの最高レベルである可能性があります。このアルゴリズムを習得すると、サブクラスマッチングの大きなクラスの問題を解決できますが、「スライディングウィンドウ」アルゴリズムは、上記のアルゴリズムよりも少し複雑です。
幸い、このタイプのアルゴリズムにはフレームテンプレートがあります。次の記事では、「スライディングウィンドウ」アルゴリズムテンプレートについて説明し、LeetCodeサブストリングのマッチングに関するいくつかの問題を解決します。
リンク
- https://www.cnblogs.com/kyoner/p/11087755.html