これは面接で私に提起された質問です。
「メモリ内に1つのリンクリストがあります。ノードを削除する必要があります。削除するノードのアドレスのみを入力として使用し、他には何も(ヘッドを含む)使用しない、そのノードを削除する関数を作成する必要があります。」
以下の投稿で答えたのと同じような答えを出しました-次のノードの内容を削除するノードにコピーして、次のノードを削除します。
前のノードへのポインタが使用できない場合に単一のリンクリストから中間ノードを削除する
しかし、インタビュアーは、最後のノードのアドレスを渡したらどうなるか、もう一度私に尋ねました。次はNULLになるので、そのNULLを、同じくNULLである次のノードへのアドレスとともにデータフィールドにコピーすることを彼に伝えました。それから彼は私にポインターがぶら下がっている問題があるだろうと私に言いました...私は少し理解していませんでした。誰かがこの問題に光を当てることができますか?これに対する一般的な解決策はありますか?
更新(2日後):少し追加。リストの最後に特別なノードがないことを考慮してください。そして、最後のノードがNULLを指し、そのノードが入力として指定されている場合、最後の前のノードがNULLを指すようにする方法。それとも不可能ですか?
簡単に言えば:ノードが関数への入力として与えられている場合、それを参照するポインターを作成する方法は、NULLを指します
ダングリングポインター:
(http://en.wikipedia.org/wiki/Dangling_reference)
コンピュータプログラミングのダングリングポインタとワイルドポインタは、適切なタイプの有効なオブジェクトを指していないポインタです。これらは、メモリ安全違反の特殊なケースです。
ダングリングポインタは、ポインタの値を変更せずにオブジェクトが削除または割り当て解除されたときに発生するため、ポインタは割り当て解除されたメモリのメモリ位置を指します。システムが以前に解放されたメモリを別のプロセスに再割り当てする可能性があるため、元のプログラムが(現在の)ダングリングポインタを逆参照すると、メモリに完全に異なるデータが含まれる可能性があるため、予期しない動作が発生する可能性があります。
あなたの答えでは、与えられたノードを削除するには、実際にはnextノードを削除します。これはポインタによって参照されている可能性があります。このようにして、ダングリングポインタの問題が発生します。
(1)注記で明確にしているように、リストへの外部参照はありません。 (2)インタビュアーが言ったように、ダングリングポインタの問題が発生する可能性があります。
(1)と(2)の両方を同時に正しくすることはできません。つまり、どこかに誤解があるということです。
最後のノードの削除について:
しかし、インタビュアーは、最後のノードのアドレスを渡したらどうなるか、もう一度私に尋ねました。次はNULLになるので、そのNULLを、同じくNULLである次のノードへのアドレスとともにデータフィールドにコピーすることを彼に伝えました。
これら2つのことを混同していると思います:(1)NULLを指すポインターp、(2)データフィールドにNULLがあるリンクリストノード。
データ構造がa -> b -> c -> d
であるとします。 dのデータフィールドにNULLを書き込んでも、cがそのnext
フィールドにNULLポインタを持つようになることはありません。
最後のノードを削除できますリンクリストに常に特別な最後のノードがある場合削除されることはありません。たとえば、a -> b -> c -> d -> LAST
ここで、LASTのデータフィールドには、それが実際に最後の要素であることを示す特別な値があります。ここでdを削除するには、LASTを削除して、dのデータフィールドに特別な値を書き込むことができます。
たぶん、これらはあなたが面接中に伝えようとしたこととまったく同じです。その場合、あなたと面接官の間で何らかの誤解があったに違いありません。
手順:
関数:
void delete_node(node* node)
{
node->Data = node->Next->Data;
node* temp = node->Next->Next;
delete(node->Next);
node->Next = temp;
}
次に、指定されたノードが最後のノードであるかどうかをプログラムでチェックインする必要があります。
void delete_node(node* node1)
{
node* search=head;
if(node1==head)
{
head=head->next;
search->next=NULL;
node1->next=NULL;
}
while(search->next != node1)
search=search->next;
if(node1->next==NULL)
{
search->next=NULL;
}
else
{
search->next=node1->next;
node1->next=NULL;
}
delete node1;
}
現在のノードにコピーされてから削除される次のノードを指している他の要素がある場合、この操作はバグをもたらします。したがって、あなたの答えでは、リストへの外部参照がない場合にのみソリューションが機能することを強調する必要があります。
ソリューションは、データ構造が特定の「最後のノード」要素で拡張されている場合にのみ、最後のノードで機能します。 (Smalltalkを使用している場合は、self become: nil
と書くことができます。他の言語に類似したものはありません)
いいえ、リストへの外部参照がある場合、一般的な解決策はありません。インタビュアーは、あなたがその主題について本当に知識があるのか、それとも単に記憶された答えを繰り返しているのかを見たかったと思います。
おそらく、リンクリストのトラバースでは、値に関係なく、null
を指すノードはすべてnull
ノードであると想定する必要があります。
a->b->c->d->NULL
したがって、d
はnull
ノードであり、ノードをノードと見なすべきではありません。このように、一般的な意味で正しくないため、特別な値を使用する手間を省くことができます。それ以外に、前のノードがnull
を指す方法は他にありません。
public void removeNode(Node node){
/* if no node return null */
if(node==null) return;
/* if only 1 node then delete node */
if(node.getNext()==null) {
node = null;
return ;
}
/* copy next node data to this node */
node.data = node.getNext().data();
/* store the next next node */
Node second = node.getNext().getNext();
/* remove next node */
removeNode(node.getNext());
/* set the copied node as next */
node.setNext(second);
}