リンクされたリストからアイテムを削除する正しい方法


9

このスラッシュドットのインタビューで、 Linus Torvaldsは次のように語っています。

"prev"エントリを追跡して片方向リンクリストエントリを削除し、そのエントリを削除する人が多すぎます。

if(prev)
  prev-> next = entry-> next;
else
  list_head = entry-> next;

そして、そのようなコードを見るときはいつでも、「この人はポインタを理解していません」とだけ行きます。悲しいことに、それはかなり一般的です。

ポインタを理解している人は、「エントリポインタへのポインタ」を使用して、list_headのアドレスで初期化します。そして、リストをトラバースするときに、「* pp = entry-> next」を実行するだけで、条件を使用せずにエントリを削除できます。

PHP開発者として、私は10年前の大学でのC入門以来ポインタに触れていません。でも、これは少なくとも知っておくべき状況だと思います。Linusは何について話しているのですか?正直なところ、リンクされたリストを実装してアイテムを削除するように求められた場合、上記の「間違った」方法は私がそれについて取り組む方法です。Linusが最もよく言うように、コーディングするために何を知る必要がありますか?

私は実際に本番コードでこれに問題を抱えていないので、スタックオーバーフローではなく、ここで質問します。


1
彼が言っているのは、prevノード全体を格納するのではなく、の場所を格納する必要がある場合は、の場所を格納するprev.nextだけでよいということです。そして、あなたがそうするなら、あなたは愚かなことを避けますif、なぜならあなたはlist_headノードの外からのポインターであるという厄介なケースがないからです。リストの先頭へのポインタは、意味的に次のノードへのポインタと同じです。
Ordous

@Ordous:なるほど、ありがとう。なぜコメント?これは、簡潔で明確で明快な答えです。
dotancohen 2015

@Ordousそのコードスニペットに含まれるすべてのものはポインタであるため、彼のポイントは、ノード全体へのポインタの格納とノードへのポインタの格納との関係がありません。
Doval 2015

回答:


9

L331 MSペイントのスキルを使用する:

ここに画像の説明を入力してください

元の解決策は、を介してノードを指すことcurrです。その場合、次のノードにcurr削除値があるかどうかを確認し、ある場合はcurrノードnextポインターをリセットします。問題は、リストの先頭を指すノードがないことです。つまり、それをチェックするには特別なケースが必要です。

Linusが代わりに提案しているのは、現在調べられているノードへのポインターを保存するのではなく、現在のノードへのポインターへのポインターを保存することです(ラベルが付けられていますpp)。操作は同じです。ppポインタが正しい値のノードを指している場合は、ポインタをリセットしppます。

違いはリストの最初にあります。リストの先頭を指すノードはありませんが、実際には、リストの先頭へのポインターがあります。また、ノードへのポインタと同じで、別のノードnextポインタと同じです。したがって、リストの先頭に特別な句は必要ありません。


ああ、今、あなたは毎日新しいことを学びます。
ローレンスアイエロ2015

1
私はあなたが物事を正しく説明していると思いますが、適切な解決策は、最初の実際のデータ項目を指すノードをlist_head持つ何かをnext指すようにする(そしてprev同じダミーオブジェクトに初期化する)ことをお勧めします。prevこのようなトリックはエイリアスによって未定義の動作を引き起こし、コードを構造体のレイアウトに不必要に敏感にする可能性があるので、私は別のタイプの何かを指すという考えは好きではありません。
スーパーキャット2015

@supercatそれがまさにポイントです。prevノードを指すのではなく、ポインタを指します。それは常に同じタイプの何か、つまりノードへのポインタを指します。あなたの提案は本質的に同じです- prevnextノードを持つ」何かにポイントを持っています。シェルを破棄すると、最初のlist_headポインタが表示されます。または言い換えると、次のノードへのポインタを持つことによってのみ定義されるものは、意味的にはノードへのポインタと同等です。
Ordous、2015

@Ordous:それは前提でlist_headありnext、同じ「種類」のポインタを保持することを前提としていますが、それは理にかなっています。Cの問題ではないかもしれませんが、おそらくC ++の問題です。
スーパーキャット2015

@supercat私はいつもそれがリンクされたリストの「標準的な」表現であり、言語にとらわれないものだと思っていました。しかし、CとC ++の違いが本当にあるのか、標準の実装はどこにあるのかを判断するのに十分な能力はありません。
Ordous、2015

11

ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください

コード例

// ------------------------------------------------------------------
// Start by pointing to the head pointer.
// ------------------------------------------------------------------
//    (next_ptr)
//         |
//         v
// [head]----->[..]----->[..]----->[..]----->[to_remove]----->[....]
Node** next_ptr = &list->head;

// ------------------------------------------------------------------
// Search the list for the matching entry.
// After searching:
// ------------------------------------------------------------------
//                                  (next_ptr)
//                                       |
//                                       v
// [head]----->[..]----->[..]----->[..]----->[to_remove]----->[next]
while (*next_ptr != to_remove) // or (*next_ptr)->val != to_remove->val
{
    Node* next_node = *next_ptr
    next_ptr = &next_node->next;
}

// ------------------------------------------------------------------
// Dereference the next pointer and set it to the next node's next
// pointer.
// ------------------------------------------------------------------
//                                           (next_ptr)
//                                                |
//                                                v
// [head]----->[..]----->[..]----->[..]---------------------->[next]
*next_ptr = to_remove->next;

ノードを破壊するロジックが必要な場合は、最後にコード行を追加するだけです。

// Deallocate the node which is now stranded from the list.
free(to_remove);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.