Part1. 我的思路和代码
函数参数为待删除节点的值,而不是节点的指针,因此需要先定位到该节点才能删除
。采用2个指针
p1、p2,其中p2最终指向待删除的节点,p1指向p2的前一个节点,作用为便于删除操作的代码编写。
具体做法为:先判断删除的是否为头节点,若是,则释放头节点的空间并返回头节点的next指针即可;否则将p1指向头节点,p2指向p1的后继节点,然后开始遍历链表,当p2定位到待删除节点时,将p1的next指针指向p2的next指针指向的节点,然后将p2节点的空间释放。
需要遍历,时间复杂度为O(n)。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
ListNode* deleteNode(ListNode* head, int val) {
ListNode *p1 = nullptr;
ListNode *p2 = nullptr;
if(head->val == val){ // 删除的是头节点
p1 = head;
p2 = p1->next;
delete p1;
return p2;
}
p1 = head; // 删除的是头节点后的节点
p2 = head->next;
while(p2 && p2->val!=val){
p1 = p2;
p2 = p2->next;
}
if(p2->val == val){
p1->next = p2->next;
delete p2;
}
return head;
}
Part2. 相关题目
和该题目不同,已经知道了指向待删除节点的指针
。
我的想法
受限于
“链表中删除一个节点,需要先找到它的前一个节点”的思路,我的想法还是从头到尾遍历链表,只是查找停止的条件不是用节点值判断,而是判断节点的地址是否和待删除节点的地址相同。
其他做法
把待删除节点node1的下一个节点node2的内容复制到待删除节点node1上,再把下一个节点node2删除并将node1的next指针指向node2的后继节点,如图:
特殊情况
:如果链表只有一个节点且删除的就是该节点,则直接删除并返回空指针;如果链表不止有一个节点但删除的是尾节点,此时只能从头到尾遍历,定位到尾节点的上一个节点来删除尾节点。
时间复杂度上,n个节点中,若删除的节点是除尾节点外的n-1个节点,则时间复杂度均为O(1),若删除的是尾节点,则由于遍历,于是为O(n),平均情况为((n-1)xO(1)+1xO(n))/n,结果还是O(1)
。
Part3. 心得体会
- 删除链表节点时,需要对
头节点
、尾节点
加以特别的考虑。 - 学到了
删除节点的新方式
,打破了我一直以来“链表中删除一个节点,需要先找到它的前一个节点”的认知,深刻赞叹该新的方式。