面试题18:删除链表的结点
题目1:在O(1)的时间内删除链表结点
给定单项链表的头指针和一个结点指针,定义一个函数在O(1)的时间内删除该结点。
分析:要在O(1)的时间内删除结点,按照常规的找到删除结点的上一个结点,然后删除该结点的思路肯定行不通。但是我们可以将待删除的结点的下一个结点的值赋给待删除结点,然后删除下一个结点,可以达到同样的目的。但以下几个问题要注意:
- 待删除的结点是单项链表最后一个结点;
- 待删除的结点是头结点(为简化处理,本次写的代码中头节点不包含数据,仅作标记,所以这条可以不考虑,但如果头节点不是标记包含数据就需要考虑);
代码:
//删除某个结点
void deleteNode(Node *pHead, Node *toDelNode)
{
if (pHead == NULL || toDelNode == NULL)
return;
if (toDelNode->next != NULL)
{
Node *nextNode = toDelNode->next;
toDelNode->value = nextNode->value;
toDelNode->next = nextNode->next;
delete nextNode;
nextNode = NULL;
}
else
{
Node *node = pHead;
while (node->next != toDelNode)
{
node = node->next;
}
node->next = NULL;
delete toDelNode;
toDelNode = NULL;
}
}
///
///题目:删除单向链表中的某个结点,时间复杂度要求为O(1)
///
//思路:将删除结点的下一个结点的值复制到该结点,然后删除下一个结点,即可在O(1)的时间复杂度内完成
//注意:若删除结点下一个结点为空,则需从头结点开始找到删除结点的前一个结点
// 头结点不包含数据,只做一个标记
#include <iostream>
using namespace std;
typedef struct Node
{
int value;
struct Node *next;
} Node;
//新增结点
Node *addNewNode(Node *pHead, int newValue);
//打印链表
void printNodeList(Node *pHead);
//删除某个结点
void deleteNode(Node *pHead, Node *toDelNode);
int main()
{
Node *pHead = new Node();
pHead->value = -1;
pHead->next = NULL;
addNewNode(pHead, 1);
addNewNode(pHead, 2);
addNewNode(pHead, 3);
addNewNode(pHead, 3);
addNewNode(pHead, 4);
Node *node6 = addNewNode(pHead, 4);
addNewNode(pHead, 5);
Node *node8 = addNewNode(pHead, 6);
//打印删除后的链表
printNodeList(pHead);
//删除结点
deleteNode(pHead, node8);
//打印删除后的链表
printNodeList(pHead);
return 0;
}
//新增结点
Node *addNewNode(Node *pHead, int newValue)
{
Node *node = pHead;
while (node->next != NULL)
{
node = node->next;
}
Node *newNode = new Node();
newNode->value = newValue;
newNode->next = NULL;
node->next = newNode;
node = NULL;
return newNode;
}
//打印链表
void printNodeList(Node *pHead)
{
Node *node = pHead->next;
while (node != NULL)
{
cout << node->value << ",";
node = node->next;
}
cout << endl;
}
当待删除结点是单项链表的最后一个结点时,从头节点开始找到待删除结点的前一个结点的时间复杂度是O(n),所以总的平均时间复杂度是[(n-1)XO(1)]/n,结果还是O(1)
题目二:删除链表中重复的结点
在一个排序的单向链表中,删除链表中存在重复值的结点。例如链表:1->2->3->3->4->4->5,删除重复的3和4后,链表变为:1->2->5
分析:在遍历链表时,我们可以定义三个指针,分别指向当前结点、前一个结点和后一个结点。
- 比较当前结点和后一个结点的值是否相同;
- 若相同,从当前结点开始,逐步遍历删除值相同的结点,直到遇到值不同的结点或者遍历链表结束,然后将前一个结点指针的下一个结点指针指向值不同结点,重新使链表连接起来;
- 若不同,分别将前一个结点指针、当前结点指针和下一个结点指针分别赋值,继续向前遍历,重复1步骤。
代码:
//删除重复的结点
void deleteDuplicateNode(Node *pHead)
{
Node *node = pHead->next;
Node *preNode = NULL;
while (node != NULL)
{
Node *nextNode = node->next;
bool toDelete = false;
if (nextNode != NULL && node->value == nextNode->value)
{
toDelete = true;
}
if (toDelete)
{
int value = node->value;
Node *delNode = node;
while (delNode != NULL && delNode->value == value)
{
nextNode = delNode->next;
delete delNode;
delNode = NULL;
delNode = nextNode;
}
if (preNode == NULL)
{
preNode = pHead;
}
preNode->next = nextNode;
node = nextNode;
}
else
{
preNode = node;
node = node->next;
}
}
}
int main()
{
Node *pHead = new Node();
pHead->value = -1;
pHead->next = NULL;
addNewNode(pHead, 1);
addNewNode(pHead, 2);
addNewNode(pHead, 3);
addNewNode(pHead, 3);
addNewNode(pHead, 4);
addNewNode(pHead, 6);
addNewNode(pHead, 7);
addNewNode(pHead, 7);
//打印删除后的链表
printNodeList(pHead);
//删除结点
deleteDuplicateNode(pHead);
//打印删除后的链表
printNodeList(pHead);
return 0;
}