题目一:在O(1)时间内删除链表节点。
给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。
C++链表节点与函数的定义如下:
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted);
思路:
直接删除单链表某一节点,无法在o(1)时间得到该节点的前一个节点,因此无法完成题目要求。
可以将欲删节点的后一个节点的值拷贝到欲删节点之上,删除欲删节点的后一个节点,从而可以在o(1)时间内完成删除。(对于尾节点,删除仍然需要o(n),其他点为o(1),因此平均时间复杂度为o(1),满足要求)
基于以上思路,java参考代码如下:
java链表节点的定义如下:
package chapter3;
public class P119_DeleteNodeInList {
public static class ListNode{
int val;
ListNode next;
public ListNode(int val){
this.val=val;
this.next=null;
}
}
public static ListNode deleteNode(ListNode head, ListNode node){
if(head==null||node==null)//注意点1:特殊输入测试
return head;
if(node==head){//仅有一个节点(既是头也是尾)
return head.next;
}
else if(node.next==null){//要删除的是尾节点
ListNode cur=head;
while (cur.next!=node){//需要在node前一个节点返回
cur=cur.next;
}
cur.next=null;
return head;
}
else{//node.next!=null,
node.val=node.next.val;
node.next=node.next.next;
return head;
}
}
public static void printNode(ListNode node){
if(node==null)
System.out.println("no node!");
ListNode cur=node;
while (cur.next!=null){
System.out.print(cur.val+"->");
cur=cur.next;
}
System.out.print(cur.val);
System.out.println();
}
public static void main(String[] args){
ListNode head = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
head.next = node2;
node2.next = node3;
printNode(head);
head = deleteNode(head,node3);
printNode(head);
head = deleteNode(head,head);
printNode(head);
}
}
注意点见代码注解部分。
C++书上的参考代码如下:
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* pNode=*pListHead;
while(pNode->m_pNext!=pToBeDeleted)
{
pNode=pNode->m_pNext;
}
pNode->m_pNext=nullptr;
delete pToBeDeleted;
pToBeDeleted=nullptr;
}
}
测试用例:
a.功能测试(从有多个节点的链表的中间删除一个节点;从有多个节点的链表中删除头节点;从有多个节点的链表中删除尾节点;从只有一个节点的链表中删除唯一的节点)。
b.特殊输入测试(指向链表头节点的为nullptr指针;指向要删除节点的为nullptr指针)。
题目二:删除链表中重复的节点。
在一个排序的链表中,如何删除重复的节点?
思路:
(1)从头到尾遍历链表,如果当前结点与下一个结点值相同,它们就是重复结点,都被删除。(2)为了保证删除之后链表没有从中间断开,需要把当前结点的前一个结点(pPreNode)和后面不重复的第一个结点(pNext)相连。
(3)同时注意头结点有可能也会被删除。
参考代码如下:
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :val(x), next(nullptr) {}
};
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if (pHead == nullptr)
return nullptr;
ListNode* pNewHead = new ListNode(-1); //申请一个新的头结点,防止原头结点重复,被删除
pNewHead->next = pHead;
ListNode* pPreNode = pNewHead; //始终指向要删除结点的前一个结点
ListNode* pNode = pHead; //用来寻找要删除的结点
ListNode* pNext = nullptr; //始终指向删除结点的后一个结点,防止链表断开
while (pNode != nullptr && pNode->next != nullptr)
{
pNext = pNode->next;
if (pNode->val == pNext->val) //如果有重复结点
{
while (pNext != NULL&&pNext->val == pNode->val) //找到重复结点后的第一个不重复结点
pNext = pNext->next;
pPreNode->next = pNext; //删除重复结点
pNode = pNext;
}
else
{
pPreNode = pNode;
pNode = pNode->next;
}
}
return pNewHead->next;
}
};
测试用例:
a.功能测试(重复的节点位于链表的头部/中间/尾部;链表中没有重复的节点)。
b.特殊输入测试(指向链表头节点的为nullptr指针;链表中所有节点都是重复的)。
参考:
https://www.bbsmax.com/A/VGzlW0rOdb/
https://blog.csdn.net/YF_Li123/article/details/70228721
https://www.jianshu.com/p/5e2275757a3d