配列の場合、最後に要素を挿入および削除する方が効率的であり、時間の複雑さはO(1)ですが、途中または最初に要素を挿入または削除すると、データの移動が必要になり、時間の複雑さはOになります。 (N)、効率が低い。
したがって、最後の記事 O(1)で、配列内の要素を削除/検索 する方法について説明しました。削除する要素を最後の要素に交換してから削除すると、データの移動を回避できます。
PS:私は100以上のオリジナル記事を注意深く書き、200のバックルの質問を手作業でブラッシングしました。これらはすべて、labuladongのアルゴリズムチートシートに公開され ており、継続的に更新されています。収集し、私の記事の順序で質問をブラッシングし、さまざまなアルゴリズムルーチンを習得し、それらを質問の海にキャストすることをお勧めします。
順序付けられた配列/リンクされたリストの重複排除
最初に、順序付けられた配列を重複排除する方法について説明します。まず、次のトピックを見てください。
関数の署名は次のとおりです。
int removeDuplicates(int [] nums);
もちろん、配列がソートされているので、重複する要素を接続する必要があります。それらを見つけるのは難しくありませんが、重複する要素がすべて見つかった場合は、すぐに削除されます。つまり、削除操作は配列の途中で実行されます。全体的な時間の複雑さはO(N ^ 2)。
PS:私は100以上のオリジナル記事を注意深く書き、200のバックルの質問を手作業でブラッシングしました。これらはすべて、labuladongのアルゴリズムチートシートに公開され ており、継続的に更新されています。収集し、私の記事の順序で質問をブラッシングし、さまざまなアルゴリズムルーチンを習得し、それらを質問の海にキャストすることをお勧めします。
その場での変更とは何かを簡単に説明します。
その場で変更されていない場合は、int[]
配列を直接新規作成し、 重複排除後に要素をこの新しい配列に配置してから、この新しい配列に戻ります。
ただし、その場で削除しても新しい配列を新しくすることはできません。元の配列を操作してから長さを返すことしかできないため、重複排除後に返された長さと元の配列を介して要素を取得できます。
この要件は、アレイ関連のアルゴリズムの問題では非常に一般的です。一般的な解決策は 、前の記事の ダブルポインター手法の高速および低速ポインター手法です。
遅いポインタ slow
を後ろにfast
置き、速いポインタ を前に歩いて道を探索し、繰り返しのない要素を見つけslow
たらそれを伝えて、前に進み ます slow
。このように、fast
ポインタが配列全体を横切る とき nums
、nums[0..slow]
要素は繰り返されません。
int removeDuplicates(int [] nums){ if(nums.length == 0){ return 0; } int slow = 0、fast = 0; while(fast <nums.length){ if(nums [fast]!= nums [slow]){ slow ++; //维护nums [0..slow]ú重复nums [slow] = nums [fast]; } fast ++; } //数配列、度修正+ 1 return slow + 1; }
アルゴリズム実行のプロセスを見てください。
順序付けられたリンクリストを提供する場合、重複を削除するにはどうすればよいですか?これは83番目の質問です。実際、これは配列の重複排除とまったく同じです。唯一の違いは、配列の割り当て操作が操作ポインターになることです。
ListNode deleteDuplicates(ListNode head){ if(head == null)return null; ListNode slow = head、fast = head; while(fast!= null){ if(fast.val!= slow.val){ // nums [slow] = nums [fast]; slow.next = fast; //遅い++; slow = slow.next; } // fast ++ fast = fast.next; } //断開与后面重复要素的 無接slow.next = null; リターンヘッド; }
関数の署名は次のとおりです。
int removeElement(int [] nums、int val);
タイトルでは 、その場でnums
値val
を持つすべての 要素を 削除する必要があり ますが、ダブルポインター手法ではスピードポインターを使用する必要があり ます。
fast
削除する必要のある要素に遭遇した場合 は、直接スキップします。それ以外の場合は、 slow
ポインターに指示して、 slow
一歩先に進めます。
これは、前述の配列の重複排除の問題の解決策とまったく同じなので、GIFを描画せず、コードを確認します。
int removeElement(int [] nums、int val){ int fast = 0、slow = 0; while(fast <nums.length){ if(nums [fast]!= val){ nums [slow] = nums [fast]; 遅い++; } fast ++; } 戻りが遅い; }
これと順序付けされた配列の重複排除との間には重要な違いがあることに注意してください。ここではnums[slow]
、最初に値を割り当ててから値 を指定します。 slow++
これによりnums[0..slow-1]
、値val
要素が含まれていない ことを確認でき 、最終結果の配列の長さは slow
です。
PS:私は100以上のオリジナル記事を注意深く書き、200のバックルの質問を手作業でブラッシングしました。これらはすべて、labuladongのアルゴリズムチートシートに公開され ており、継続的に更新されています。収集し、私の記事の順序で質問をブラッシングし、さまざまなアルゴリズムルーチンを習得し、それらを質問の海にキャストすることをお勧めします。
ゼロを移動
これはLeikouの質問283です。次の質問について説明します。
配列nums
を入力してください。その 場で変更してください。配列内の値が0のすべての要素を配列の最後に移動してください。関数の署名は、次のとおりです。
void moveZeroes(int [] nums);
たとえばnums = [0,1,4,0,2]
、入力を与えると 、アルゴリズムは値を返しませんが、そのnums
場で配列を 変更し [1,4,2,0,0]
ます。
前述の質問を組み合わせて、あなたはすでに答えを持っていますか?
タイトルを使用すると、すべての0を最後に移動できます。実際には、nums
すべての0を削除してから、次のすべての要素を0に割り当てるのと同じ です。
したがってremoveElement
、前の質問の機能を再利用できます 。
void moveZeroes(int [] nums){ // nums内のすべての0を削除します// 0を削除した 後の配列の長さを返しますintp = removeElement(nums、0); // pの後のすべての要素を0に割り当てます for(; p <nums.length; p ++){nums [p] = 0; } } // int removeElement(int [] nums、int val);を実現するには、上記のコードを参照してください。
この時点で、4つの「その場での変更」アルゴリズムの問題は終了しました。実際、コアは依然としてスピードポインター技術です。それを学びましたか?