目录
1. 删除链表的节点
1.1 题目描述
1.2 Leetcode解题的思路一(双指针)
1.3 Leetcode解题的思路二(单指针)
1.4 剑指offer上的原题
1. 删除链表的节点
原题链接:
剑指 Offer 18. 删除链表的节点 - 力扣(LeetCode)https://leetcode.cn/problems/shan-chu-lian-biao-de-jie-dian-lcof/
1.1 题目描述
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
1.2 Leetcode解题的思路一(双指针)
首先根据题目我们知道该链表是一个单向链表,在遍历链表找到值等于val的节点后无法向前找到该节点的前一个节点,因此我们维护两个指针prev,和cur,用指针prev指向正在遍历链表的指针cur的前一个节点,当指针cur指向的val等于要删除的val值时结束遍历。然后将指针prev指向的节点的next赋值为cur的下一个节点,最后释放掉cur,cur置为空指针。
这样做显然有一个严重的问题,如果要删除的节点就是头结点,那么我们让prev->next指向cur->next就会使得空指针解引用,引起程序崩溃,对此情况我们需要单独进行处理。
时间复杂度:O(N),空间复杂度:O(1)。
struct ListNode* deleteNode(struct ListNode* head, int val){
//如果删除的是头结点,单独处理
if(head->val == val)
{
//找到头结点的下一个节点/NULL
struct ListNode* next = head->next;
//释放掉删除的节点
free(head);
//防止野指针
head = NULL;
//返回新的头结点
return next;
}
else
{
//前驱指针,
struct ListNode* prev = NULL;
//遍历指针
struct ListNode* cur = head;
//找到要被删除的节点值
while(cur->val!=val)
{
prev = cur;
cur = cur->next;
}
//链接新的节点
prev->next = cur->next;
free(cur);
cur = NULL;
return head;
}
}
1.3 Leetcode解题的思路二(单指针)
上面的解题思路需要一个前驱指针,那是不是一定需要得到被删除节点的前一个节点呢?显然没这个必要我们只需要找到要删除节点的前一个节点即可,然后再删除连接。这种思路其实跟上面的解法本质上是一样的,为了方便我们就把他当作另一种解法吧。这种方法也需要单独处理删除头结点的情况。
时间复杂度:O(N),空间复杂度:O(1)。
struct ListNode* deleteNode(struct ListNode* head, int val)
{
//删除头结点单独处理
if (head->val == val)
{
struct ListNode* next = head->next;
free(head);
return next;
}
else
{
//一个指针
struct ListNode* cur = head;
//如果遍历到的节点的下一个节点的val等于删除的值,停止循环
while (cur->next->val != val)
{
cur = cur->next;
}
//保存节点并连接
struct ListNode* f_next = cur->next;
struct ListNode* s_next = f_next->next;
free(f_next);
cur->next = s_next;
return head;
}
}
1.4 剑指offer上的原题
Leetcode上也注明了对原题有改动,那咱就来看看原题吧:题目大体相同,只是传入的参数是要删除节点的指针。如果是这样传参的话,我们就能做到在O(1)的时间复杂度删除节点。
如果我们把下一个节点的内容复制到需要删除的节点上覆盖原有的内容,再把下一个节点
删除,那是不是就相当于把当前需要删除的节点删除了?
我们要删除节点i,先把i的下一个节点j的内容复制到i,然后把i的指针指向节点j的下一个节点。此时再删除节点j,其效果刚好是把节点i删除了。
上述思路还有一个问题:如果要删除的节点位于链表的尾部,那么它就没有下一个节点,怎么办?我们仍然从链表的头节点开始,顺序遍历得到该节点的前序节点,并完成删除操作。
最后需要注意的是,如果链表中只有一个节点,而我们又要删除链表的头节点(也是尾节点),那么,此时我们在删除节点之后,还需要把链表的头节点设置为 nullptr。
这种思路的平均时间复杂度是O(1)。
struct ListNode
{
int val;
struct ListNode* next;
};
//打印链表
void print(struct ListNode* head)
{
assert(head);
struct ListNode* cur = head;
while (cur)
{
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL");
}
//返回值和参数都不同于Leetcode上的题目, 不要返回值,需要改变原头结点必须传入二级指针
void deleteNode(struct ListNode** pphead, struct ListNode* pToBeDeleted)
{
assert(pphead);
assert(pToBeDeleted);
//要删除的节点不是尾节点
if (pToBeDeleted->next != NULL)
{
struct ListNode* next = pToBeDeleted->next;
pToBeDeleted->val = next->val;
pToBeDeleted->next = next->next;
free(next);
next == NULL;
}
else if (*pphead == pToBeDeleted) //只有一个节点
{
free(pToBeDeleted);
pToBeDeleted = NULL;
*pphead = NULL;
}
else //删除尾节点
{
struct ListNode* cur = *pphead;
while (cur->next != pToBeDeleted)
{
cur = cur->next;
}
cur->next = NULL;
free(pToBeDeleted);
pToBeDeleted = NULL;
}
}
int main()
{
//创建一个4,5,1,9的链表
struct ListNode* node1 = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* node2 = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* node3 = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* node4 = (struct ListNode*)malloc(sizeof(struct ListNode));
node1->val = 4;
node2->val = 5;
node3->val = 1;
node4->val = 9;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
//删除5
deleteNode(&node1, node2);
print(node1);
//删除9
//deleteNode(&node1, node4);
//print(node1);
return 0;
}