这是一个在采访中向我提出的问题。
“内存中存在单个链表。您必须删除一个节点。您需要编写一个删除该节点的函数,该节点仅删除要删除的节点的地址作为输入而不包含其他内容(包括头)“
我给出了类似于下面帖子中回答的答案 - 将下一个节点的内容复制到要删除的节点中并删除下一个节点。
Deleting a middle node from a single linked list when pointer to the previous node is not available
但是面试官再次问我,如果我传递最后一个节点的地址怎么办?我告诉他,因为下一个将是一个NULL,将NULL复制到数据字段以及下一个节点的地址也是NULL。然后他告诉我将会出现悬挂指针的问题......我对此并不了解。请问有人可以解决这个问题吗?有没有通用的解决方案呢?
更新(两天后):再补充一点。考虑到列表末尾没有特殊节点。最后一个节点指向NULL,如果该节点作为输入,则如何使前一个节点指向NULL。或者这是不可能的?
简单地说:如果一个节点作为函数的输入,如何使引用它的指针,指向NULL
答案 0 :(得分:13)
步骤:
功能:
void delete_node(node* node)
{
node->Data = node->Next->Data;
node* temp = node->Next->Next;
delete(node->Next);
node->Next = temp;
}
答案 1 :(得分:9)
悬空指针:
(http://en.wikipedia.org/wiki/Dangling_reference)
计算机编程中的悬空指针和狂野指针是 指向不指向适当类型的有效对象的指针。 这些都是违反记忆安全的特殊情况。
删除或取消分配对象时会出现悬空指针, 无需修改指针的值,使指针仍然存在 指向释放的内存的内存位置。作为系统 可以将以前释放的内存重新分配给另一个进程,如果是 原始程序然后取消引用(现在)悬空指针, 可能导致不可预测的行为,因为内存现在可能包含 完全不同的数据。
在您的回答中,要删除给定节点,您实际上删除了 next 节点,该节点可能被指针引用。这就是悬垂指针问题出现的原因。
(1)正如您在备注中说明的那样,没有外部参考列表。 (2)面试官说,可能会出现悬垂指针问题。
(1)和(2)都不能同时正确。这意味着某处存在误解。
关于删除最后一个节点:
但是面试官再次问我,如果我通过该地址,该怎么办? 最后一个节点我告诉他,因为下一个将是NULL,复制那个NULL 进入数据字段以及到下一个节点的地址 也是NULL。
我认为你混淆了这两件事:(1)指向NULL的指针p,(2)在其数据字段中具有NULL的链表节点。
假设数据结构为a -> b -> c -> d
。将NULL写入d's数据字段不会使c在其next
字段中具有NULL指针。
如果链接列表始终具有永不删除的特殊最后一个节点,则可以删除最后一个节点。例如,a -> b -> c -> d -> LAST
其中LAST在其数据字段中有一个特殊值,表示它实际上是最后一个元素。现在要删除d,你可以删除LAST并在d's数据字段中写入特殊值。
也许这些正是你在面试中试图说的,在这种情况下,你和面试官之间肯定会有一些误解。
答案 2 :(得分:3)
然后应该在程序中检查给定节点是否是最后一个节点。
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;
}
答案 3 :(得分:2)
如果有其他元素指向将被复制到当前节点然后删除的下一个节点,则此操作将引入错误。因此,在您的回答中,您应该强调,只有在没有外部引用列表的情况下,您的解决方案才有效。
只有在使用特定的“最后一个节点”元素扩充数据结构时,您的解决方案才能使用最后一个节点。 (如果您使用的是Smalltalk,则可以编写self become: nil
没有其他语言有类似的内容)
不,如果列表有外部引用,则没有通用解决方案。我认为面试官想知道你是否真的知道这个主题,或者只是重复一个记忆的答案。
答案 4 :(得分:1)
可能您的链接列表遍历可能需要假设任何指向null
的节点都是null
节点,而不管其值是什么...
a->b->c->d->NULL
所以d
是null
节点,不应将该节点视为节点。这样你可以节省使用特殊值,因为它们在一般意义上是不正确的。除此之外,您将没有任何其他方式让前一个节点指向null
。
答案 5 :(得分:0)
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);
}
答案 6 :(得分:0)
假设:deleteNode() 引用了节点指针。
没有while循环的简单解决方案。在deleteNode()中,if case处理头节点和中间节点的删除,else处理最后一个节点的删除。
#include <iostream>
using namespace std;
class Node{
public:
int data;
Node* next;
Node(int d): data(d){}
};
void insert(Node* &head, int data){
Node *node = new Node(data);
node->next = head;
head = node;
}
void print(Node *head){
Node *temp = head;
while(temp !=NULL){
cout << temp->data <<" ";
temp = temp->next;
}
cout << endl;
}
void deleteNode(Node *&node){
cout << "Deleting "<<node->data<<endl;
if(node->next != NULL){
Node *t = node->next;
node->data = node->next->data;
node->next = node->next->next;
delete t;
}else{
delete node;
node = NULL;
}
}
int main(int argc, char const *argv[]) {
Node *head = NULL;
insert(head, 10);
insert(head, 20);
insert(head, 30);
insert(head, 40);
insert(head, 50);
print(head);
deleteNode(head->next); //delete 40
deleteNode(head->next->next->next); //delete last node 10
print(head);
return 0;
}