题目1:在O(1)时间内删除链表的节点(任意指定的一个):给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点
注:个人认为下面代码中pListHead译为头指针更准确
已知:
给定链表节点与函数的定义如下
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
}
void deleteNode(ListNode** pListNode, ListNode* pToBeDeleted);
复制下一节点法
void deleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
//判有效性
if(!pListHead || !pToBeDeleted)
return;
//欲删节点不是尾结点
if(pToBeDeleted-> m_pNext != nullptr)
{
//定义一个指针指向欲删节点的下一节点(为了方便表示,个人觉得不额外定义也可,只是要用 //pToBeDeleted->m_pNext表示
ListNode* pNext = pToBeDeleted->m_pNext;
//将欲删节点的下一节点的数据复制替换给欲删节点
pToBeDeleted->m_nValue = pNext->m_nValue;
//欲删节点指向欲删节点下一节点的下一节点
pToBeDeleted->m_pNext = pNext->m_pNext;
//感觉是固定格式
delete pNext;
pNext = nullptr;
}
//链表只有一个节点
else if(pToBeDeleted == *pListHead)
{
//将链表头指针设为空
*pListHead = nullptr;
delete pNext;
pNext = nullptr;
}
//链表有多个节点且欲删节点为尾结点
else
{
//因尾节点无下一节点,无法使用上述复制下一节点法,且单向链表无指向上一节点的指针,因此设一指针从
//头开始找到尾节点
ListNode* pNode = *pListHead;
while(pNode->m_pNext != pToBeDeleted)//此处pToBeDeleted改为nullptr感觉也可
{
pNode = pNode->m_pNext;
}
//找到尾节点的前一节点,并将该节点的指针域指向空
pNode->m_pNext = nullptr;
delete pToBeDeleted;
pToBeDeleted = nullptr;
}
}
题目一个人总结:
代码并非完美,是基于要删除的节点的确在链表中的假设,判断节点是否在链表中需O(n),但受题干O(1)时间限制所限只能将责任推给了函数的调用者.面试时可以主动提出这一点,证明自己考虑问题很全面.
题目二:删除链表中重复的节点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
//判断非法性
if(pHead == nullptr || pHead->next == nullptr) //此处还需判断头节点的下一节点是否为空,以确保第一次执行大循环第一句时令pNext指向它有意义
return pHead;
//建立当前节点指针和前一节点指针
ListNode* pNode = pHead;
ListNode* pPreNode = nullptr;
//大循环遍历整个链表
while(pNode != nullptr)
{
//建立下一节点指针 和 是否重复需删除标识
ListNode* pNext = pNode->next;
bool needDeleted = false;
//如果当前节点与下一节点重复,删除标识位置为真
if(pNext != nullptr && pNode->val == pNext->val)
needDeleted = true;
//判断是否需要删除
//不需删除,则前一指针和当前指针各前移一位
if(!needDeleted)
{
pPreNode = pNode;
pNode = pNext;
}
//至少有两个重复节点,需删除
else
{
//定义需删除节点指针 及 节点重复数值
ListNode* pToBeDeleted = pNode;
int value = pToBeDeleted->val;
//针对此时的重复数值value,通过每次的小循环 判断 后续更多的节点 是否重复并在当次循环实时删除之
while(pToBeDeleted != nullptr && pToBeDeleted->val == value)
{
//保存当前重复节点的下一节点的指针,留作下次判断
pNext = pToBeDeleted->next;
//删除套路
delete pToBeDeleted;
pToBeDeleted = nullptr;
//设定下一待判重待删除节点
pToBeDeleted = pNext;
}
/************针对一个重复值value已删除完毕************/
//判断此时头节点情况
//如头节点因重复被删,直接令头节点指向重复节点后一节点
//不能使用pHead == nullptr来判断,好像pHead一直没有变过
//即使头节点因重复被删,也不能用头指针作为判断头节点是否被删的条件
if(pPreNode == nullptr)
pHead = pNext;
//头节点没被删,重复节点前一节点 指向 重复节点后一节点
else
pPreNode->next = pNext;
//当前指针节点 置于 重复节点后一节点
pNode = pNext;
}
}
return pHead;
}
};
题目二个人总结:
传入一级指针,在函数内部可以改变指针指向对象的值(即*head),但在函数内部无法改变head(指针的指向),因此如果实现算法过程中在函数内部涉及改变指针指向的操作则必须传入二级指针(**head),即此时可以改变 *head(即指针的指向)
其他博客参考:
用一级指针和二级指针的区别:
一级指针:传入一个地址,我们可以对地址上的值进行修改。但如果是一个空指针,你在函数内为之开辟空间是无法带回函数外部的。
二级指针:传入一个指针的地址,此时指针是作为地址上的值让我们修改的。
报错1:
段错误:您的程序发生段错误,可能是数组越界,堆栈溢出(比如,递归调用层数太多)等情况引起
原因:
//判断此时头节点情况
//头节点被删
正解:if(pPreNode == nullptr) 错解:if(pHead == nullptr)
如头节点因重复被删,直接令头节点指向重复节点后一节点,不能使用pHead == nullptr来判断,好像pHead一直没有变过,即使头节点因重复被删,也不能用头指针作为判断头节点是否被删的条件