O(1)时间内删除指定链表结点
题目
给定单链表头指针和一个结点指针,定义一个函数在O(1)时间内删除该结点。

分析
对于上图实例链表(a)删除指针p有两种方式
- 思路1:(b)找到前一个指针pre,赋值pre->next = p->next,删掉p
- 思路2:(c)目的是删除p,但是不删p,直接用p->next的值赋值给p,把p->next删除掉(好处:不用遍历找到p的前一个指针pre,O(1)时间内搞定)
于是,定位到思路2,但是思路2有两个特例:
- 删除的是尾指针,需要遍历找到前一个指针
- 整个链表就一个结点(属于删尾指针,但没法找到前面的指针,需要开小灶单独处理)
大体算法思路
待删指针不是尾指针:
待删指针的值用待删指针的下一个指针的值覆盖
删掉待删指针的下一个指针
待删指针是尾指针:
待删指针同时是头指针:
删掉头指针
待删指针不是头指针
找到待删指针的前一个指针
删掉待删指针,前一个指针的next赋值为空
参考代码
bool deleteNode(ListNode *&head, ListNode *p)
{
if(!p || !head)
return false;
if(p->m_pNext != NULL) //不是尾指针
{
ListNode *del = p->m_pNext;
p->m_nValue = del->m_nValue;
p->m_pNext = del->m_pNext;
delete del;
del = NULL;
}
else if(head == p) //是尾指针,同时只有一个结点
{
delete p;
head = NULL;
}
else //是尾指针,同时有多个结点
{
ListNode *tmp = NULL, *pre = head;
while(pre->m_pNext != p)
{
pre = pre->m_pNext;
}
delete p;
p = NULL;
pre->m_pNext = NULL;
}
return true;
}
完整代码1
#include <iostream>
using namespace std; struct ListNode
{
int m_nValue;
ListNode* m_pNext;
}; bool deleteNode(ListNode *&head, ListNode *p)
{
if(!p || !head)
return false;
if(p->m_pNext != NULL)
{
ListNode *del = p->m_pNext;
p->m_nValue = del->m_nValue;
p->m_pNext = del->m_pNext;
delete del;
del = NULL;
}
else if(head == p)
{
delete p;
head = NULL;
}
else
{
ListNode *tmp = NULL, *pre = head;
while(pre->m_pNext != p)
{
pre = pre->m_pNext;
}
delete p;
p = NULL;
pre->m_pNext = NULL;
}
return true;
}
void createList(ListNode *&head)
{
head = new(ListNode);
head->m_nValue = ;
head->m_pNext = NULL; ListNode *p2 = new(ListNode);
p2->m_nValue = ;
p2->m_pNext = NULL;
head->m_pNext = p2; ListNode *p3 = new(ListNode);
p3->m_nValue = ;
p3->m_pNext = NULL;
p2->m_pNext = p3; ListNode *p4 = new(ListNode);
p4->m_nValue = ;
p4->m_pNext = NULL;
p3->m_pNext = p4;
} void deleteList(ListNode *p)
{
ListNode *next = NULL;
while(p != NULL)
{
cout << p->m_nValue << endl;
next = p->m_pNext;
delete p;
p = NULL;
p = next;
}
} int main()
{
ListNode *head = NULL;
createList(head);
ListNode *p = head->m_pNext->m_pNext->m_pNext;
deleteNode(head, p);
deleteList(head);
}
完整代码2
#include <iostream>
using namespace std; struct ListNode
{
int m_nValue;
ListNode* m_pNext;
}; bool deleteNode(ListNode **head, ListNode *p)
{
if(!p || !head)
return false;
if(p->m_pNext != NULL)
{
ListNode *del = p->m_pNext;
p->m_nValue = del->m_nValue;
p->m_pNext = del->m_pNext;
delete del;
del = NULL;
}
else if(*head == p)
{
delete p;
*head = NULL;
}
else
{
ListNode *tmp = NULL, *pre = *head;
while(pre->m_pNext != p)
{
pre = pre->m_pNext;
}
delete p;
p = NULL;
pre->m_pNext = NULL;
}
return true;
}
void createList(ListNode *&head)
{
head = new(ListNode);
head->m_nValue = ;
head->m_pNext = NULL; ListNode *p2 = new(ListNode);
p2->m_nValue = ;
p2->m_pNext = NULL;
head->m_pNext = p2; ListNode *p3 = new(ListNode);
p3->m_nValue = ;
p3->m_pNext = NULL;
p2->m_pNext = p3; ListNode *p4 = new(ListNode);
p4->m_nValue = ;
p4->m_pNext = NULL;
p3->m_pNext = p4;
} void deleteList(ListNode *p)
{
ListNode *next = NULL;
while(p != NULL)
{
cout << p->m_nValue << endl;
next = p->m_pNext;
delete p;
p = NULL;
p = next;
}
} int main()
{
ListNode *head = NULL;
createList(head);
ListNode *p = head->m_pNext->m_pNext;
deleteNode(&head, p);
deleteList(head);
}
分析
删除非尾结点时间复杂读为O(1),删除尾结点的时间复杂读为O(n), 平均时间复杂度为[(n-1)*O(1) + O(N)] / N = O(1)
还有删除函数并不能处理待删结点就是该链表中的指针,因此需要人为调用时控制,如果得验证的话,那就没必要做这些处理了,直接O(N)
技术细节——传值操作
#include <iostream>
using namespace std; struct ListNode
{
int m_nValue;
ListNode* m_pNext;
}; void createList(ListNode *head)
{
head = new(ListNode);
head->m_nValue = ;
head->m_pNext = NULL;
}
void deleteList(ListNode *p)
{
ListNode *next = NULL;
while(p != NULL)
{
cout << p->m_nValue << endl;
next = p->m_pNext;
delete p;
p = NULL;
p = next;
}
} int main()
{
ListNode *head = NULL;
createList(head);
cout << head << endl;
deleteList(head);
}
主函数中的指针head为传值调用,传到函数并没有改变主函数中的值,图示

改进的措施就是引用传值,直接操纵原指针。
O(1)时间内删除指定链表结点的更多相关文章
- 在O(1)时间内删除单链表结点
// 在O(1)时间内删除单链表结点 /* 思考: 很显然链表是一个节点地址不连续的存储结构 删除节点一般很容易会想到是修改p节点的前一个节点的next为p->next 然而除非是双向链表,否则 ...
- 在O(1)时间删除指定链表结点
#region 在O(1)时间删除指定链表结点 /// <summary> /// 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. /// </summa ...
- 在O(1)时间内删除单链表结点
给定单链表的一个结点的指针,同时该结点不是尾结点,此外没有指向其它任何结点的指针,请在O(1)时间内删除该结点. int deleteNode(LNode **head, LNode **node) ...
- 时间复杂度分别为 O(n)和 O(1)的删除单链表结点的方法
有一个单链表,提供了头指针和一个结点指针,设计一个函数,在 O(1)时间内删除该结点指针指向的结点. 众所周知,链表无法随机存储,只能从头到尾去遍历整个链表,遇到目标节点之后删除之,这是最常规的思路和 ...
- 13:在O(1)时间内删除单向链表中的一个节点
思路:如果从首部开始依次查找,那么时间是O(n). 既然我们知道要删除的结点i,那么我们就知道它指向的下一个结点j,那么我们可以将j的内容复制到i,然后将i的指针指向j的下一个结点,这样虽然看起来我们 ...
- 《剑指offer》面试题13—O(1)时间删除链表结点
题目:给定单向链表的头指针和某结点指针,实现函数在O(1)时间内删除指定节点. 思路:由于没有要删除结点(j结点)的前一个结点(i结点)指针,通常想法是从头开始遍历找到指定结点的前一个结点(i结点), ...
- pta 奇数值结点链表&&单链表结点删除
本题要求实现两个函数,分别将读入的数据存储为单链表.将链表中奇数值的结点重新组成一个新的链表.链表结点定义如下: struct ListNode { int data; ListNode *next; ...
- 【编程题目】在 O(1)时间内删除链表结点
60.在 O(1)时间内删除链表结点(链表.算法).题目:给定链表的头指针和一个结点指针,在 O(1)时间删除该结点.链表结点的定义如下:struct ListNode{int m_nKey;List ...
- (剑指Offer)面试题13:在O(1)时间内删除链表结点
题目: 在给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间内删除该结点.链表结点与函数的定义如下: struct ListNode{ int val; ListNode* next; } ...
随机推荐
- 4月7号周二课堂练习:NABC
团队项目——7-magic 分析特点:游戏简单容易上手 NABC分析: N(needs需求)现在存在的很多游戏操作比较,游戏规则也比较繁琐,用户很难或者不愿意去玩操作难度比较大的游戏,容易上手的游戏比 ...
- 软件工程课堂练习——N层电梯只停一层求乘客爬楼层数最少(基本方法+优化方法)
题目: •石家庄铁道大学基础大楼一共有四部电梯,每层都有人上下,电梯在每层都停.信1201-1班的张一东觉得在每层都停觉得不耐烦. •由于楼层不太高,在上下课高峰期时时,电梯从一层上行,但只允许停在某 ...
- 30道小学生四则运算题C/C++编程
软件工程科课上,老师通过实例讲解什么是程序,程序和软件的区别,要求我们通过短时间写一道编程题, 题目就是编写30道小学生四则运算题.以下就是源代码: #include<iostream.h> ...
- Linux 下的类似Windows下Everything的搜索工具
Windows NTFS有个超级快的搜索工具Everything,非常好用,Linux下有几个类似的命令行工具,太难用了,推荐一个catfish,类似Everything,有GUI,可以自定义一个快捷 ...
- 使用OutputDebugString输出调试信息
在编写控制台程序的时候我们经常会使用printf输出调试信息,使我们了解程序的状态,方便调试,但是当编写非控制台程序的时候这种方法就行不通了,那我们应该怎么办?上网查了一些方法,大致就如下几种 使用L ...
- android 图片缩放抗锯齿
之前用的时候只设置了antialias属性,其实要设置两个flag才行 paint.setFlags(Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG); ...
- Spring MVC 环境搭建(一)
一.建立 JavaWeb 项目 1.建立一个 Java 项目. 2.在项目下新建一个文件夹 webapp (命名可自取,这个目录即是网站根目录),再在该文件夹下新建一个 WEB-INF 文件夹(命名固 ...
- WEB学习总结 +数据结构
HTML5 <h1>会员注册界面</h1><form action="process.aspx" method="post" n ...
- EF 更新条目时出错。有关详细信息,请参见内部异常。
现象:使用EF新增记录时,一直报上述异常,网上说是值为空.主键外键未设等原因导致,但是改正这些情况下问题依然 解决过程:异常中有一句(请参见内部异常),一直都没有当回事,后来实在没办法就静下心来看了看 ...
- 【POJ】【3680】Intervals
网络流/费用流 引用下题解: lyd: 首先把区间端点离散化,设原来的数值i离散化后的标号是c[i].这样离散化之后,整个数轴被分成了一段段小区间. 1.建立S和T,从S到离散化后的第一个点连容量K, ...