【面试题18 删除链表的节点】
难度: 简单
说明: 题目保证链表中节点的值互不相同
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
Leetcode题目对应位置: 面试题18:删除链表的节点
删除单链表节点分 3 种情况:
其实只有删除头节点需要进行特殊处理,其他两种情况都可以进行统一解决。另外,由于题设条件是所有节点值互不相同,所以只要碰到与目标值相同的节点,即为需要删除的节点。
对于中间节点被删除的情况,需要让其前一个节点的指针指向其后一个节点。对于单链表,则需要有一个指针指向当前节点的前一个节点,来辅助删除操作。
代码逻辑:
head->next
pre
指向头节点,指针 cur
指向链表第二个节点cur
为空 或 指针 cur
指向的节点值就是目标值Python 代码:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
if head == val: return head.next
pre, cur = head, head.next
while cur and cur.val != val:
pre, cur = cur, cur.next
if cur:
pre.next = cur.next
return head
C++ 代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
if(!head) return NULL;
if(head->val == val) return head->next;
ListNode* pre = head;
ListNode* cur = head->next;
while (cur && (cur->val != val)){
pre = cur;
cur = cur->next;
}
if(cur) pre->next = cur->next;
return head;
}
};
其实双指针是不必要的,单指针即可。
C++ 代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
if(!head) return NULL;
if(head->val == val) return head->next;
ListNode* p = head;
while(p->next && p->next->val != val){
p = p->next;
}
if(p->next) p->next = p->next->next;
return head;
}
};
注意一个细节,while(p->next && p->next->val != val)
循环中的 p->next
不能省,否则对于特殊测试用例(要删除的目标值不在链表中)通不过。
在剑指 offer 上的原题要求是这样的:
在 O(1) 时间内删除链表节点。给定单向链表的头指针和一个节点指针,定义一个函数在 O(1) 时间内删除该节点。链表结点与函数定义如下:
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
}
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted);
按照上面的思路,如果从头节点开始顺序查找并删除,时间复杂度是 O(n),不符合本题要求。之所以需要顺序查找,是因为对于单链表 ...->h->m->n->...
来说,要删除 m,就要知道 h,并将 h 的指针指向 n,这样才能安全地删除 m。
但是!h 并不是一定得知道的,介绍一下 “乾坤大挪移” 删除法:
已知一个当前要删除的节点和其下一个节点,只需要将下一个节点的内容复制到当前节点上,然后将当前节点的指针指向下一个节点的下一个节点,就完成了目标值的删除。实际上就是用删除下一个节点来替代删除当前节点。
代码逻辑:
时间复杂度: 对于 n 个节点的单链表,前 n-1 个节点都可以在 O(1) 时间内把下一个节点的内存复制到要删除的节点并删除下一个节点;只有最后一个节点需要 O(n) 来顺序查找,因此总的时间复杂度 = [(n - 1) * O(1) + O(n)] / n,结果是 O(1)。
C++ 代码:
#include
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted);
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
/*非法输入*/
if (!pListHead || !pToBeDeleted) return;
/*删除非尾节点*/
if (pToBeDeleted->m_pNext != nullptr) {
ListNode* pNext = pToBeDeleted->m_pNext;
pToBeDeleted->m_nValue = pNext->m_nValue;
pToBeDeleted->m_pNext = pNext->m_pNext;
delete pNext;
pNext = nullptr;
}
/*只有一个节点*/
else if (*pListHead == pToBeDeleted) {
delete pToBeDeleted;
pToBeDeleted = nullptr;
*pListHead = nullptr;
}
/*多个节点删除尾节点*/
else {
ListNode* pNext = *pListHead;
while (pNext->m_pNext != pToBeDeleted) {
pNext = pNext->m_pNext;
}
pNext->m_pNext = nullptr;
delete pToBeDeleted;
pToBeDeleted = nullptr;
}
}
在 O(1) 时间内删除的要求,需要基于被删除的节点一定在单链表中这个前提,解答前应该跟面试官讨论清楚这个问题。
在一个排序的链表中,如何删除重复的节点?(这里意思是只要出现了重复,就将所有值为重复值的节点都删除,比如 1->2->3->3->4->4->5
删除后为 1->2->5
)
测试用例:
根据上面列出的测试用例,可以发现头节点也可能被删除,所以函数声明时传入的参数应该是 ListNode* pHead
而不是 ListNode* pHead
。
#include
#include
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
void DeleteRepeatNode(ListNode** pListHead);
void DeleteRepeatNode(ListNode** pListHead)
{
/*非法值处理*/
if (pListHead == nullptr || *pListHead == nullptr) return;
ListNode* preNode = nullptr;
ListNode* curNode = *pListHead;
bool repeat = false;
while (curNode != nullptr)
{
while (curNode->m_pNext != nullptr && curNode->m_pNext->m_nValue == curNode->m_nValue) {
repeat = true;
curNode->m_pNext = curNode->m_pNext->m_pNext;
}
if (repeat) {
//链表中间重复
if (preNode != nullptr)
preNode->m_pNext = curNode->m_pNext;
//链表头重复
else {
*pListHead = curNode->m_pNext;
curNode = *pListHead;
}
repeat = false;
}
else {
preNode = curNode;
curNode = curNode->m_pNext;
}
}
}
测试用例:
1234567 -> 1234567
1114567 -> 4567
1234447 -> 1237
1234577 -> 12345
1133577 -> 5
1133377 -> /