PG。编程访谈暴露的书中有29个具有以下示例代码,用于从链表中删除元素:
bool deleteElement(IntElement **head, IntElement *deleteMe)
{
IntElement *elem = *head;
if(deleteMe == *head){ /*special case for head*/
*head = elem->next;
delete deleteMe;
return true;
}
while (elem){
if(elem->next == deleteMe){
/*elem is element preceding deleteMe */
elem->next = deleteMe->next;
delete deleteMe;
return true;
}
elem = elem->next;
}
/*deleteMe not found */
return false;
}
我的问题是关于语句“delete deleteMe”,这是否实现了我们想要的效果,即实际删除该位置的元素,还是只删除指向deleteMe元素的指针的副本?
答案 0 :(得分:4)
delete deleteMe;
调用元素上的析构函数并释放其关联的内存。这段代码是C ++,顺便说一句。
其余代码改变了数据结构列表,以取消元素与其邻居的链接。
答案 1 :(得分:3)
您的问题已经得到解答,但我不得不指出,如果我正在采访某人,并且他们以这种方式编写代码,我就不会非常感动。
对于初学者来说,在C ++中使用指向指针的指针虽然合理,但在C ++中是完全没有必要的。相反,我更愿意看到对指针的引用。其次,通常更喜欢const正确的代码。
bool deleteElement(IntElement const *&head, IntElement const *deleteMe)
{
IntElement *elem = head;
if(deleteMe == head){ /*special case for head*/
head = elem->next;
delete deleteMe;
return true;
}
while (elem){
if(elem->next == deleteMe){
/*elem is element preceding deleteMe */
elem->next = deleteMe->next;
delete deleteMe;
return true;
}
elem = elem->next;
}
/*deleteMe not found */
return false;
}
最后,我还要添加一个特殊案例,以避免不必要的遍历列表。除非要删除的项目恰好位于列表的最后,否则可以避免遍历。我们假设您的IntElement类似于:
struct IntElement {
int data;
IntElement *next;
};
在这种情况下:
bool simple_delete(IntElement *deleteMe) {
IntElement *temp = deleteMe->next;
deleteMe->data = temp->data;
deleteMe->next = temp->next;
delete temp;
return true;
}
他们正在搜索整个列表以找到前一个元素,因此他们可以删除后面的元素。相反,我们只是将下一个节点复制到当前节点,然后删除下一个节点(注意:在某些情况下,交换数据而不是复制会更好/更快)。还要注意,如果其他东西可能持有指向下一个节点的指针,这可以/将会破坏(非常彻底)。
[对于它的价值,我最初是从算法+数据结构=程序,由Niklaus Wirth学习这种技术,虽然我相信它起源于Knuth。]
无论如何,一旦我们有了这个,我们只需要向deleteElement
添加一些代码来使用它,除非要删除的节点恰好是列表中的最后一个:
bool deleteElement(IntElement const *&head, IntElement *deleteMe) {
if (deleteMe->next != NULL)
return simple_delete(deleteMe);
// previous body of deleteElement
}
在原始总是具有线性复杂性的情况下,在大多数情况下这具有恒定的复杂性。通过在末尾使用带有sentinel值而不是NULL指针的列表,可以确保所有情况下的常量复杂性(以及simple_delete
处理所有情况 - 您可以消除其余的代码完全)。
答案 2 :(得分:1)
它实际上是删除节点本身而不是它的副本。