web-dev-qa-db-ja.com

前のノードへのポインターが使用できない場合に、単一のリンクリストから中間ノードを削除する

使用可能な唯一の情報が削除されるノードへのポインタであり、前のノードへのポインタではない場合、単一のリンクリスト内の中間ノードを削除することは可能ですか?削除されたノード。

40
Nitin

それは間違いなく、実際の問題というよりもクイズです。ただし、何らかの仮定を立てることが許可されている場合、O(1)時間で解決できます。これを行うには、リストポイントの制約がコピー可能である必要があります。アルゴリズムは以下:

次のようなリストがあります:...-> Node(i-1)-> Node(i)-> Node(i + 1)-> ...そしてNode(i)を削除する必要があります。

  1. Node(i + 1)からNode(i)にデータ(ポインターではなくデータ自体)をコピーすると、リストは次のようになります。..-> Node(i-1)-> Node(i + 1)-> Node(i + 1)-> ...
  2. 2番目のNode(i + 1)のNEXTを一時変数にコピーします。
  3. 次に、2番目のノード(i + 1)を削除します。前のノードへのポインターは不要です。

擬似コード:

void delete_node(Node* pNode)
{
    pNode->Data = pNode->Next->Data;  // Assume that SData::operator=(SData&) exists.
    Node* pTemp = pNode->Next->Next;
    delete(pNode->Next);
    pNode->Next = pTemp;
}

マイク。

87

構造を持つリストを仮定しましょう

A-> B-> C-> D

Bへのポインタだけがあり、それを削除したい場合は、次のようにできます。

tempList = B->next;
*B = *tempList;
free(tempList);

リストは次のようになります

A-> B-> D

しかし、BはCの古い内容を保持し、Bにあったものを事実上削除します。他のコードがCへのポインターを保持している場合、これは機能しません。ノードDを削除しようとしても機能しません。この種の操作を行う場合は、実際に使用されないダミーのテールノードを使用してリストを作成する必要があるため、有用なノードにNULLの次ポインターがないことを保証します。これは、ノードに保存されているデータの量が少ないリストでもうまく機能します。のような構造

struct List { struct List *next; MyData *data; };

大丈夫ですが、それは

struct HeavyList { struct HeavyList *next; char data[8192]; };

少し面倒です。

26
Ben Combee

ありえない。

削除を模倣するハックがあります。

しかし、実際には、ポインターが指しているノードは削除されません。

followingノードを削除し、その内容をactualノードにコピーして削除する一般的なソリューションには、外部ポインターポインティングがある場合に副作用がありますリスト内のノードに移動します。この場合、次のノードを指す外部ポインターがぶら下がります。

11
codaddict

1つのアプローチは、データにnullを挿入することです。リストを走査するたびに、前のノードを追跡します。 nullデータが見つかった場合、リストを修正し、次のノードに進みます。

4
Joe Hildebrand

最善のアプローチは、次のノードのデータを削除するノードにコピーし、ノードの次のポインターを次のノードの次のポインターに設定し、次のノードを削除することです。

削除するノードを指す外部ポインターの問題は、trueの場合、次のノードにも当てはまります。次のリンクリストを検討してください。

A-> B-> C-> D-> E-> FおよびG-> H-> I-> D-> E-> F

(最初のリンクリストで)ノードCを削除する必要がある場合は、前述の方法で、コンテンツをノードCにコピーした後にノードDを削除します。これにより、次のリストが表示されます。

A-> B-> D-> E-> FおよびG-> H-> I->ダングリングポインター。

NODE Cを完全に削除した場合、結果のリストは次のようになります。

A-> B-> D-> E-> FおよびG-> H-> I-> D-> E-> F。

ただし、ノードDを削除する場合、以前のアプローチを使用すると、外部ポインターの問題が依然として存在します。

4
Sandeep Mathias

最初の提案は、変換することでした:

a-> b-> c

に:

a->、c

たとえば、ノードのアドレスから次のノードのアドレスへのマップなどの情報を保持している場合、次回チェーンを修正してリストを走査できます。次の走査の前に複数のアイテムを削除する必要がある場合は、削除の順序(つまり、変更リスト)を追跡する必要があります。

標準ソリューションでは、スキップリストなどの他のデータ構造を検討します。

3
Allan Wind

おそらくソフト削除を実行しますか? (つまり、ノードに「削除済み」フラグを設定します)必要に応じて、後でリストをクリーンアップできます。

3
perimosocordiae

与えられた

A-> B-> C-> D

そして、たとえば、アイテムBへのポインタは、次の方法で削除します。
1。 Bのメンバーに属するメモリをすべて解放します
2。 Cの内容をBにコピーします(これには「次の」ポインターが含まれます)
3。アイテムC全体を削除します

もちろん、1つのアイテムのリストで作業するなど、Edgeのケースには注意する必要があります。

Bがあったところで、Cがあり、以前はCであったストレージが解放されます。

1
DarenW

リストの通過可能性を維持したい場合ではありません。次のノードにリンクするには、前のノードを更新する必要があります。

とにかく、どうしてこのような状況に陥ったのですか?この質問をするために何をしようとしていますか?

1
Allen

前のノードを見つけるためにリストを行進する必要があります。これにより、一般的にO(n ** 2)が削除されます。あなたが削除を行う唯一のコードである場合、前のノードをキャッシュし、そこで検索を開始することにより、実際にはより良い方法がありますが、これが役立つかどうかは削除のパターンに依存します。

1
Kimbo

はい。ただし、リンクを解除することはできません。メモリの破損を気にしない場合は、先に進んでください;-)

0
Steven A. Lowe

次のコードはLLを作成し、nを削除してから、削除するノードへのポインターを指定するようユーザーに要求します。削除後にリストを印刷します。削除するノードの後に​​ノードをコピーし、削除するノードの上にコピーしてから、削除するノードの次のノードを削除します。すなわち

あいうえお

cをbにコピーし、cを解放して、結果が

a-c-d

struct node  
{
    int data;
    struct node *link;
 };

void populate(struct node **,int);

void delete(struct node **);

void printlist(struct node **);

void populate(struct node **n,int num)
{

    struct node *temp,*t;
    if(*n==NULL)
    {
        t=*n;
        t=malloc(sizeof(struct node));
        t->data=num;
        t->link=NULL;
        *n=t;
    }
    else
    {
        t=*n;
        temp=malloc(sizeof(struct node));
        while(t->link!=NULL)
            t=t->link;
        temp->data=num;
        temp->link=NULL;
        t->link=temp;
    }
}

void printlist(struct node **n)
{
    struct node *t;
    t=*n;
    if(t==NULL)
        printf("\nEmpty list");

    while(t!=NULL)
    {
        printf("\n%d",t->data);
        printf("\t%u address=",t);
        t=t->link;
    }
}


void delete(struct node **n)
{
    struct node *temp,*t;
    temp=*n;
    temp->data=temp->link->data;
    t=temp->link;
    temp->link=temp->link->link;
    free(t);
}    

int main()
{
    struct node *ty,*todelete;
    ty=NULL;
    populate(&ty,1);
    populate(&ty,2);
    populate(&ty,13);
    populate(&ty,14);
    populate(&ty,12);
    populate(&ty,19);

    printf("\nlist b4 delete\n");
    printlist(&ty);

    printf("\nEnter node pointer to delete the node====");
    scanf("%u",&todelete);
    delete(&todelete);

    printf("\nlist after delete\n");
    printlist(&ty);

    return 0;
}

はい。ただし、削除するとリストは壊れます。

この特定のケースでは、リストを再度走査して、そのポインターを取得します!一般的に、この質問をしている場合、おそらくあなたがしていることにバグが存在します。

0
owenmarshall
void delself(list *list)
{
   /*if we got a pointer to itself how to remove it...*/
   int n;

   printf("Enter the num:");
   scanf("%d",&n);

   while(list->next!=NULL)
   {
      if(list->number==n) /*now pointer in node itself*/
      {
         list->number=list->next->number;   /*copy all(name,rollnum,mark..)
                             data of next to current, disconnect its next*/
         list->next=list->next->next;
      }
      list=list->next;
   }
}
0
Aneesh

前のリストアイテムに到達するには、現在のアイテムを指すnextポインターを持つエントリが見つかるまで、リストを最初からトラバースする必要があります。次に、現在のアイテムをリストから削除するために変更する必要がある各アイテムへのポインターがあります-単にprevious->nextからcurrent->nextその後、currentを削除します。

編集:キンボは1分以内に私を打ち負かしました!

0
Eltariel

フラグを使用してノードをリストからリンク解除するように設定し、次の適切なトラバーサルでノードを削除する場合、遅延リンク解除を実行できます。リンク解除されるように設定されたノードは、リストをクロールするコードによって適切に処理される必要があります。

リスト内のアイテムを指すものが見つかるまで、リストを最初からもう一度走査することもできると思います。ほとんど最適ではありませんが、少なくとも遅延リンク解除よりもはるかに優れたアイデアです。

一般に、元のアイテムへのポインタを知っている必要があり、それを渡す必要があります。

(編集:Ick、私が完全な答えをタイプするのにかかった時間で、3兆の人々が私が言及しようとしていたほとんどすべてのポイントをカバーしました。:()

0
DJ Capelis

これを行うための唯一の賢明な方法は、先頭のノードが削除するノードを見つけるまで、いくつかのポインターでリストを走査し、後続のポインターを使用して次のフィールドを更新することです。

ランダムアイテムをリストから効率的に削除する場合は、二重にリンクする必要があります。ただし、リストの先頭から項目を取得して末尾に追加する場合は、リスト全体を二重にリンクする必要はありません。リストを単独でリンクしますが、リストの最後の項目の次のフィールドがリストの最初の項目を指すようにします。次に、リストの「ヘッド」がテールアイテム(ヘッドではなく)を指すようにします。リストの末尾に追加したり、先頭から削除したりするのは簡単です。

0
Paul

リストの先頭にいますよね?あなたはそれをただ横断します。

リストが「head」でポイントされ、それを削除するノードが「del」であるとしましょう。

Cスタイルの擬似コード(ドットは-> Cになります):

prev = head
next = prev.link

while(next != null)
{
  if(next == del)
  {
    prev.link = next.link;
    free(del);
    del = null;
    return 0;
  }
  prev = next;
  next = next.link;
}

return 1;
0
Charles Graham

リンクリストA-> B-> C-> DとノードBへのポインターがある場合。このノードを削除する必要がある場合、ノードCの内容をBに、ノードDをCに、Dを削除できます。単一リンクリストの場合、ノードAも失われるため、ノード自体を削除することはできません。二重リンクリストの場合、Aに戻ることができますが。

私は正しいですか?

0
Smitha

これは私がまとめたコードの一部であり、OPが要求していたことを実行し、リストの最後の要素を削除することもできます(最もエレガントな方法ではなく、完了します)。リンクリストの使用方法を学びながら書きました。それが役に立てば幸い。

#include <cstdlib>
#include <ctime>
#include <iostream>
#include <string>

using namespace std;


struct node
{
    int nodeID;
    node *next;
};

void printList(node* p_nodeList, int removeID);
void removeNode(node* p_nodeList, int nodeID);
void removeLastNode(node* p_nodeList, int nodeID ,node* p_lastNode);

node* addNewNode(node* p_nodeList, int id)
{
    node* p_node = new node;
    p_node->nodeID = id;
    p_node->next = p_nodeList;
    return p_node;
}

int main()
{
    node* p_nodeList = NULL;
    int nodeID = 1;
    int removeID;
    int listLength;
    cout << "Pick a list length: ";
    cin >> listLength;
    for (int i = 0; i < listLength; i++)
    {
        p_nodeList = addNewNode(p_nodeList, nodeID);
        nodeID++;
    }
    cout << "Pick a node from 1 to " << listLength << " to remove: ";
    cin >> removeID;
    while (removeID <= 0 || removeID > listLength)
    {
        if (removeID == 0)
        {
            return 0;
        }
        cout << "Please pick a number from 1 to " << listLength << ": ";
        cin >> removeID;
    }
    removeNode(p_nodeList, removeID);
    printList(p_nodeList, removeID);
}

void printList(node* p_nodeList, int removeID)
{
    node* p_currentNode = p_nodeList;
    if (p_currentNode != NULL)
    {
        p_currentNode = p_currentNode->next;
        printList(p_currentNode, removeID);
        if (removeID != 1)
        {
            if (p_nodeList->nodeID != 1)
            {
                cout << ", ";
            }

            cout << p_nodeList->nodeID;
        }
        else
        {
            if (p_nodeList->nodeID !=2)
            {
                cout << ", ";
            }
            cout << p_nodeList->nodeID;
        }
    }
}

void removeNode(node* p_nodeList, int nodeID)
{
    node* p_currentNode = p_nodeList;
    if (p_currentNode->nodeID == nodeID)
    {
        if(p_currentNode->next != NULL)
        {
            p_currentNode->nodeID = p_currentNode->next->nodeID;
            node* p_temp = p_currentNode->next->next;
            delete(p_currentNode->next);
            p_currentNode->next = p_temp;
        }
        else
        {
            delete(p_currentNode);
        }
    }
    else if(p_currentNode->next->next == NULL)
    {
        removeLastNode(p_currentNode->next, nodeID, p_currentNode);
    }
    else
    {
        removeNode(p_currentNode->next, nodeID);
    }
}

void removeLastNode(node* p_nodeList, int nodeID ,node* p_lastNode)
{
    node* p_currentNode = p_nodeList;
    p_lastNode->next = NULL;
    delete (p_currentNode);
}
0
Desyn8686