单向链表在O(1)时间内删除一个节点
说删链表节点,第一时间想到就是遍历整个链表,找到删除节点的前驱,改变节点指向,删除节点,但是,这样删除单链表的某一节点,时间复杂度就是O(n),不符合要求;
时间复杂度是O(n)的做法就不说了,看看O(1)的写法,看图:
删除节点,需要找到被删节点的前驱,上面的说明,交换数据后,要删的节点转换为被删节点的后一个节点,此时被删节点前驱可以知道,删除就很简单了
给出被删节点指针,O(1)时间内就可以有此方法删除节点,但是,如果,被删节点是链表最后一个节点,以上方法明显不在适用,不得不从头遍历:
这时就得从头遍历,只为找最后一个节点的前驱,就这唯一一个节点,找它的时间复杂度就是O(n),但平均时间复杂度为:
((n-1)*O(1)+O(n))/n
结果还是O(1),复合要求的,又但是,这里没有考虑要删除的节点是否在链表中,如果要判断有没有在链表中,又得遍历,结果时间复杂度有不在是O(1),
为了保证时间,被删的节点有没有在链表中,只能由人为的去控制;以下是代码段:
//在O(1)时间内,删除一个节点,函数如下
void DeleteNodeNumone(ListNode** phead,ListNode* pToBeDelete)
{
if (*phead == nullptr || pToBeDelete == nullptr)
return; //删除非尾节点
if (pToBeDelete->_next != nullptr)
{
ListNode* temp = pToBeDelete->_next;
pToBeDelete->_data = temp->_data;
pToBeDelete->_next = temp->_next; delete temp;
temp = nullptr;
} //只有一个节点
else if (*phead == pToBeDelete)
{
delete pToBeDelete;
pToBeDelete = nullptr;
*phead = nullptr;
} //最后一种,删除节点是尾节点
else
{
ListNode* cur = *phead;
while (cur->_next != pToBeDelete)
{
cur = cur->_next;
}
delete pToBeDelete;
pToBeDelete = nullptr;
cur->_next = nullptr;
}
}
完整测试代码:
#pragma once
typedef int DataType; class ListNode
{
public:
ListNode(const DataType& x)
:_data(x)
, _next(NULL)
{} public:
DataType _data;
ListNode* _next;
}; class Slist
{
public:
Slist()
:_head(NULL)
, _tail(NULL)
{}
~Slist()
{
//析构函数,删除节点,删除全部
Destory();
} void Destory()
{
ListNode* begin = _head;
while (begin)
{
ListNode* del = begin;
begin = begin->_next;
delete del;
}
} public:
//尾插
void PushBack(const DataType& x)
{
if (_head == NULL)
{
_head = new ListNode(x); _tail = _head;
}
else
{
_tail->_next = new ListNode(x);
_tail = _tail->_next;
}
} //查找
ListNode* Find(const DataType& x)
{
ListNode* tmp = _head;
while (tmp)
{
if (tmp->_data == x)
return tmp;
else
{
tmp = tmp->_next;
}
}
return NULL;
} // //在O(1)时间内,删除一个节点,函数如下
void DeleteNodeNumone(ListNode** phead,ListNode* pToBeDelete)
{
if (*phead == nullptr || pToBeDelete == nullptr)
return; //删除非尾节点
if (pToBeDelete->_next != nullptr)
{
ListNode* temp = pToBeDelete->_next;
pToBeDelete->_data = temp->_data;
pToBeDelete->_next = temp->_next; delete temp;
temp = nullptr;
} //只有一个节点
else if (*phead == pToBeDelete)
{
delete pToBeDelete;
pToBeDelete = nullptr;
*phead = nullptr;
} //最后一种,删除节点是尾节点
else
{
ListNode* cur = *phead;
while (cur->_next != pToBeDelete)
{
cur = cur->_next;
}
delete pToBeDelete;
pToBeDelete = nullptr;
cur->_next = nullptr;
}
} void print()
{
ListNode* begin = _head;
while (begin)
{
cout << begin->_data << "->";
begin = begin->_next;
}
cout << "NULL" << endl;
} public:
ListNode* _head;
ListNode* _tail;
};
void Test()
{
Slist s1;
s1.PushBack(5);
s1.PushBack(2);
s1.PushBack(3);
s1.PushBack(2);
s1.PushBack(1);
s1.PushBack(6);
s1.PushBack(7);
s1.PushBack(9);
s1.print(); ListNode* num =s1.Find(9); s1.DeleteNodeNumone(&s1._head, num); s1.print();
}
测试结果:
赐教!
单向链表在O(1)时间内删除一个节点的更多相关文章
- LeetCode-450 二叉搜索树删除一个节点
二叉搜索树 建树 删除节点,三种情况,递归处理.左右子树都存在,两种方法,一种找到左子树最大节点,赋值后递归删除.找右子树最小同理 class Solution { public: TreeNode* ...
- Linus:利用二级指针删除单向链表
Linus大神在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是他所喜好的,大婶表述了自己一些观点之后,举了一个指针的例子,解释了什么才是core low-level codi ...
- 【转】Linus:利用二级指针删除单向链表
原文作者:陈皓 原文链接:http://coolshell.cn/articles/8990.html 感谢网友full_of_bull投递此文(注:此文最初发表在这个这里,我对原文后半段修改了许多, ...
- 转:Linus:利用二级指针删除单向链表
感谢网友full_of_bull投递此文(注:此文最初发表在这个这里,我对原文后半段修改了许多,并加入了插图) Linus大婶在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是 ...
- 在O(1)的时间内删除链表节点
题目: 在O(1)的时间内删除链表节点.给定链表的头指针和待删除的节点指针,定义一个函数在O(1)的时间内删除该节点. 剑指offer的思路,顿时觉得极妙.删除节点node1,先把其下一个节点node ...
- 用Python写单向链表和双向链表
链表是一种数据结构,链表在循环遍历的时候效率不高,但是在插入和删除时优势比较大. 链表由一个个节点组成. 单向链表的节点分为两个部分:存储的对象和对下一个节点的引用.注意是指向下一个节点. 而双向链表 ...
- Reverse Linked List II 单向链表逆序(部分逆序)
0 问题描述 原题点击这里. 将单向链表第m个位置到第n个位置倒序连接.例如, 原链表:1->2->3->4->5, m=2, n =4 新链表:1->4->3-& ...
- 数据结构——求单向链表的倒数第K个节点
首先,对于链表来说,我们不能像数组一样直接访问,所以我们想到要求倒数第K个节点首先要知道最后一个节点. 然后从最后一个节点往前数K个. 最后得到想要的值. 但是这是不对的,为什么呢?因为题目给出的是单 ...
- 剑指offer25:复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),结果返回复制后复杂链表的head。
1 题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用 ...
随机推荐
- 浏览器之window对象--javascript
window对象代表打开的浏览器窗口,是Web浏览器所有内容的主容器.window对象是整个对象链条结构的最高层,是其他对象的父对象,在调用window对象的方法和属性时,可以省略window对象的引 ...
- mfc100u.dll下载和使用方法
当运行软件或游戏时,系统提示"丢失mfc100u.dll"."没有找到mfc100u.dll"等类似错误信息. 请下载本站提供的dll文件,使用它可以帮助用 ...
- 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏
简介 这是我自己的一点理解,可能写的不好 点分治都学过吧.. 点分治每次找重心把树重新按重心的深度重建成了一棵新的树,称为分治树 这个树最多有log层... 动态点分治:记录下每个重心的上一层重心,这 ...
- anguar-select2
<!DOCTYPE html> <html ng-app="App"> <head> <meta charset="UTF-8& ...
- Java 多线程之线程池的使用
一. 使用背景 谈到Java多线程,我们很自然的会想到并发,在编写多线程代码时,我们一般会创建多个线程,如果并发的线程数量很多,而且每个线程都是执行一个时间很短的任务就结束了,这样频繁的进行线程的创建 ...
- java 关于性别的处理
运用数结构的思想(在数据库中把性别的值设置为 男 1 女 0) //数据结构思想应用 public static final Integer EMP_GENDER_OF_MAN = 1; public ...
- 洛谷 P1146 【硬币翻转】题解
很久很久之前做过的一道题 翻n-1枚硬币,就是有一枚不翻,也可以理解为翻一枚 直接上程序,看程序说话 #include<iostream> using namespace std; ; b ...
- Spring @AfterReturning 总是返回null
在学习Spring Aop时,遇到一个问题,当 @Around(环绕通知)与 @AfterReturning(后置通知)共存 时,@AfterReturning 通过属性 returning = &q ...
- C#中的多线程与线程互斥
通过多线程,C#可以并行地执行代码. 每一个线程都有它独立的执行路径,所有线程都能访问共有变量. 这就引发了线程竞争 这时就需要使用线程安全的处理方式使得线程互斥 先来看一段多线程代码 using S ...
- php提交表单时判断 if($_POST[submit])与 if(isset($_POST[submit])) 的区别
if(isset($_POST['submit'])) 它的意思是不是判断是否配置了$_POST['submit'] 这个变量呢?如果有这个变量 在执行其它代码 应该这样用if(isset($_POS ...