📅  最后修改于: 2023-12-03 15:22:42.917000             🧑  作者: Mango
双向链表是一种经常用于实现LRU Cache(最近最少使用缓存)等数据结构的基础数据结构。在实际开发中,在某些情况下,我们需要删除双向链表中所有出现的给定键,这里为大家介绍如何实现。
在阅读本文之前,建议程序员需要掌握的知识点包括:
如果您还不熟悉双向链表或链表节点的定义,可以先阅读以下文章:
要删除双向链表中所有出现的给定键,我们可以遍历整个链表,每当遇到给定键的节点就将其删除,直到遍历完整个链表。需要注意的是,由于有些节点被删除之后,链表的头指针也可能发生变化,因此我们需要在遍历的过程中维护头指针的正确性。
具体来说,我们可以采用以下的算法:
基于以上的思路分析,我们可以编写如下C++代码实现双向链表中所有出现给定键的删除操作:
struct ListNode {
int val;
ListNode* prev;
ListNode* next;
ListNode(int x) : val(x), prev(NULL), next(NULL) {}
};
class Solution {
public:
ListNode* deleteNode(ListNode* head, int key) {
ListNode *p = head;
ListNode *prev = NULL;
while (p != NULL) {
if (p->val == key) {
if (prev == NULL) {
head = p->next; // 如果要删除的节点是头节点,更新头指针
if (head != NULL) {
head->prev = NULL;
}
} else {
prev->next = p->next;
if (p->next != NULL) {
p->next->prev = prev; // 如果要删除的节点是中间节点,更新前后指针
}
}
ListNode *q = p;
p = p->next;
free(q);
} else {
prev = p;
p = p->next;
}
}
return head;
}
};
以上代码定义了一个双向链表节点的结构体ListNode,里面有val(节点的值)、prev(指向前一个节点的指针)、next(指向后一个节点的指针)三个字段。同时,这里我们用面向对象的思想定义了一个Solution类,其中的deleteNode函数实现了我们的思路分析。
具体来说,deleteNode函数的输入参数head是要删除节点的双向链表的头指针,key是要删除的节点的值。它的返回值是删除后的双向链表的头指针。
在deleteNode函数中,我们用一个while循环遍历整个链表,每当遇到要删除的节点时,就将其删除。如果要删除的节点是头节点,那么我们需要更新头指针head的值;如果要删除的节点是中间节点,那么我们需要更新前后指针的值。最后,我们还需要释放被删除的节点的内存空间,然后将指针p指向被删除节点的下一个节点。
在出现key相等的节点时,我们需要删除该节点,同时维护prev和p指针的值。如果p指针指向的节点不是要删除的节点,那么我们只需要将prev指针指向p,将p指针指向p的下一个节点即可,由于该节点不需要删除,我们无需释放其内存空间。
假设链表的长度为n,我们需要遍历整个链表,时间复杂度为O(n)。在每次遇到给定键时,我们需要删除一个节点,因此需要进行常数次的内存读写操作,时间复杂度为常数级别。因此,总时间复杂度为O(n)。
由于双向链表中可以通过指针快速定位前后节点,因此其删除操作的空间复杂度通常为O(1)。但在本算法实现中,由于采用了free函数释放节点空间,因此空间复杂度看起来是O(n)的。不过由于空间复杂度与双向链表的实现方法和具体算法实现相关,因此在不同场景下可能会发生变化。
本文介绍了如何删除双向链表中所有出现的给定键。我们从思路分析、代码实现和性能分析三方面对该算法进行了介绍和分析。双向链表作为常用的基础数据结构,其删除节点的操作是十分重要的,希望读者通过本文的学习能掌握该算法并在实际开发中得到应用。