当指向前一个节点的指针不可用时,从单个链表中删除中间节点

时间:2008-09-16 03:41:09

标签: c data-structures linked-list

当我们可用的唯一信息是指向要删除的节点的指针而不是指向前一节点的指针时,是否可以删除单个链表中的中间节点?删除后,前一节点应指向已删除节点旁边的节点。

24 个答案:

答案 0 :(得分:86)

这绝对是一个测验,而不是一个真正的问题。但是,如果允许我们做出一些假设,可以在O(1)时间内解决。要做到这一点,列表指向的限制必须是可复制的。算法如下:

我们有一个列表如下:... - >节点(i-1) - >节点(i) - >节点(i + 1) - > ...我们需要删除Node(i)。

  1. 将数据(不是指针,数据本身)从节点(i + 1)复制到节点(i),列表将如下所示:... - >节点(i-1) - >节点(i + 1) - >节点(i + 1) - > ...
  2. 将第二个节点(i + 1)的NEXT复制到临时变量中。
  3. 现在删除第二个节点(i + 1),它不需要指向前一个节点的指针。
  4. 伪代码:

    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;
    }
    

    麦克

答案 1 :(得分:25)

我们假设一个结构列表

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]; };

会有点累赘。

答案 2 :(得分:11)

不可能。

有些黑客可以模仿删除。

但是这些都不会实际删除指针所指向的节点。

如果你有外部指针,那么删除跟随节点并将其内容复制到要删除的实际节点的流行解决方案会产生副作用指向列表中的节点,在这种情况下,指向以下节点的外部指针将变为悬空。

答案 3 :(得分:5)

我很欣赏这个解决方案的独创性(删除下一个节点),但它没有回答问题的问题。如果这是实际的解决方案,那么正确的问题应该是#34;从链表中删除包含在指针所在的节点中的VALUE"。但是,当然,正确的问题可以为您提供解决方案的提示。 制定这个问题的目的是让人感到困惑(事实上这件事发生在我身上,特别是因为面试官甚至没有提到节点在中间)。

答案 4 :(得分:4)

最好的方法仍然是将下一个节点的数据复制到要删除的节点中,将节点的下一个指针设置为下一个节点的下一个指针,然后删除下一个节点。

指向要删除的节点的外部指针的问题虽然为真,但也适用于下一个节点。请考虑以下链接列表:

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,并使用前面的方法,那么外部指针的问题仍然存在。

答案 5 :(得分:4)

一种方法是为数据插入null。每当遍历列表时,都会跟踪前一个节点。如果找到空数据,则修复列表,然后转到下一个节点。

答案 6 :(得分:3)

最初的建议是改造:

a - > b - > ç

为:

a - >,c

如果您保留信息,例如,从节点地址到下一个节点的地址的地图,那么您可以在下次遍历列表时修复链。如果需要在下次遍历之前删除多个项目,则需要跟踪删除的顺序(即更改列表)。

标准解决方案是考虑其他数据结构,例如跳过列表。

答案 7 :(得分:3)

也许做一个软删除? (即,在节点上设置“已删除”标志)如果需要,可以稍后清理列表。

答案 8 :(得分:1)

考虑下面的链接列表

  

1-> 2-> 3->空

     

指向节点2的指针说“ ptr”。

我们可以使用伪代码,看起来像这样:

struct node* temp = ptr->next;
ptr->data = temp->data;
ptr->next = temp->next;
free(temp);

答案 9 :(得分:1)

鉴于

A - > B - > C - > d

和指向项目B的指针,你会删除它 1.释放属于B成员的任何记忆 2.将C的内容复制到B(这包括其“下一个”指针)
3.删除整个项目C

当然,您必须小心边缘情况,例如处理一个项目的列表。

现在有B的地方,你有C并且以前是C的存储被释放。

答案 10 :(得分:1)

您必须沿着列表前进才能找到上一个节点。这将使删除一般为O(n ** 2)。如果你是唯一进行删除的代码,你可以在实践中通过缓存前一个节点并在那里开始搜索来做得更好,但这是否有帮助取决于删除的模式。

答案 11 :(得分:1)

如果要保持列表的可遍历性,则不行。您需要更新上一个节点以链接到下一个节点。

无论如何,你怎么会在这种情况下结束?你想做什么让你问这个问题?

答案 12 :(得分:0)

唯一明智的做法是使用几个指针遍历列表,直到前导者找到要删除的节点,然后使用尾随指针更新下一个字段。

如果要有效地从列表中删除随机项,则需要进行双向链接。如果您想从列表的头部获取项目并在尾部添加它们,则不需要双重链接整个列表。单独链接列表,但使列表中最后一项的下一个字段指向列表中的第一个项目。然后将列表“head”指向尾部项目(而不是头部)。然后很容易添加到列表的尾部或从头部删除。

答案 13 :(得分:0)

你有名单的负责人,对吗?你只是遍历它。

假设您的列表由“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;

答案 14 :(得分:0)

您可以使用标志将节点设置为脱离列表,然后在下一次正确的遍历中删除它们,从而延迟脱钩。设置为脱链的节点需要由爬网列表的代码正确处理。

我想您也可以从头开始再次遍历列表,直到找到指向列表中项目的内容。几乎没有最佳,但至少比延迟脱钩要好得多。

一般来说,你应该知道你刚来自的项目的指针,你应该传递它。

(编辑:Ick,我花了很多时间来填写一个完整的答案,三千万人几乎涵盖了我要提及的所有要点。:()

答案 15 :(得分:0)

为了进入上一个列表项,您需要从头开始遍历列表,直到找到一个带有next指针的条目指向您当前的项目。然后,您将有一个指向您必须修改的每个项目的指针,以从列表中删除当前项目 - 只需将previous->next设置为current->next,然后删除current。< / p>

编辑:Kimbo不到一分钟就打败了我!

答案 16 :(得分:0)

以下代码将创建LL,n然后要求用户将指针指向要删除的节点。它将在删除后打印列表 它通过在要删除的节点之后复制节点,在要删除的节点上执行相同的操作,然后删除要删除的节点的下一个节点。 即

A-B-C-d

将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;
}

答案 17 :(得分:0)

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, disconect its next*/
           list->next=list->next->next;
       }
       list=list->next;
   }
}

答案 18 :(得分:0)

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;
   }
}

答案 19 :(得分:0)

如果您有链接列表A - &gt; B - &gt; C - &gt; D和指向节点B的指针。如果必须删除此节点,可以将节点C的内容复制到B,将节点D复制到C中并删除D.但是,如果是单链表,我们就不能删除节点如果我们这样做,节点A也将丢失。虽然我们可以在双链表的情况下回溯到A.

我是对的吗?

答案 20 :(得分:0)

是的,但删除后,您的列表会被破坏。

在这种特定情况下,再次遍历列表并获取指针!一般来说,如果你问这个问题,可能存在你正在做的事情的错误。

答案 21 :(得分:0)

这是我放在一起执行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);
}

答案 22 :(得分:0)

是的,但你不能脱钩。如果你不关心腐败记忆,请继续; - )

答案 23 :(得分:-1)

Void deleteMidddle(Node* head)
{
     Node* slow_ptr = head;
     Node* fast_ptr = head;
     Node* tmp = head;
     while(slow_ptr->next != NULL && fast_ptr->next != NULL)
     {
        tmp = slow_ptr;
        slow_ptr = slow_ptr->next;
        fast_ptr = fast_ptr->next->next;
     }
     tmp->next = slow_ptr->next;
     free(slow_ptr);
    enter code here

}