Day13 2023/01/20
难度:简单
题目
输入一个单向链表和一个节点的值,从单向链表中删除等于该值的节点,删除后如果链表中无节点则返回空指针。
链表的值不能重复。
构造过程,例如输入一行数据为:
6 2 1 2 3 2 5 1 4 5 7 2 2
则第一个参数6表示输入总共6个节点,第二个参数2表示头节点值为2,剩下的2个一组表示第2个节点值后面插入第1个节点值,为以下表示:
1 2 表示为
2->1
链表为2->1
3 2表示为
2->3
链表为2->3->1
5 1表示为
1->5
链表为2->3->1->5
4 5表示为
5->4
链表为2->3->1->5->4
7 2表示为
2->7
链表为2->7->3->1->5->4
最后的链表的顺序为 2 7 3 1 5 4
最后一个参数为2,表示要删掉节点为2的值
删除 结点 2
则结果为 7 3 1 5 4
数据范围:链表长度满足 1 ≤ n ≤ 1000 ,节点中的值满足0 ≤ va ≤ 10000
测试用例保证输入合法
输入描述:
输入一行,有以下4个部分:
1 输入链表结点个数
2 输入头结点的值
3 按照格式插入各个结点
4 输入要删除的结点的值
输出描述:
输出一行
输出删除结点后的序列,每个数后都要加空格
示例
输入:5 2 3 2 4 3 5 2 1 4 3
输出:2 5 4 1
说明:
形成的链表为2->5->3->4->1
删掉节点3,返回的就是2->5->4->1
思路一
本题目的关键在于如何构建链表,利用链表的一些基本操作即可完成,每次插入的时候遍历找到插入节点的前驱节点即可进行节点的插入。至于删除节点操作不清楚的可以看这篇:链接
思路二
在思路一的基础上,可以用数组模拟链表,其中插入(insert, push_bakck)和删除(erase)以及寻找前驱节点(find)都有对应的库函数,可以简化代码。
关键点
- 要理解题目输入数据中所描述的插入节点的要求,例如:1 2 表示为 2->1,链表为2->1,即 1 为插入节点,2为插入节点的前驱节点,每次插入元素前,都要找到该前驱节点,一定要理解这一点,才能看懂代码是如何实现的。插入操作动图:
push_back(val)在数组末尾插入一个元素,val为插入值insert(index, val)在数组指定位置插入一个元素,index为数组下标,val为插入元素。erase(index)删除指定位置的元素。find(begin, end, val)在指定范围内(begin ~ end)查找元素值为val的元素,找到了返回元素下标,没有则返回end值。
算法实现
c++代码实现1-链表的基本操作
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
//方法一:使用链表的基本操作
//定义链表的节点
typedef struct LNode {
int data;
struct LNode* next;
LNode(int data) : data(data), next(nullptr){};
} * LinkList;
int main() {
int num, data; // num节点总数,data头节点数值
int aft, pre; // aft待插入节点数值,pre插入节点的前驱节点数值
cin >> num >> data;
LNode* L = new LNode(data); //创建头节点
while (--num) {
cin >> aft >> pre;
LNode* newNode = new LNode(aft); // 创建待插入节点
// 查找前驱节点,并插入
LNode* cur = L; // 遍历指针,防止污染头指针
while (cur) {
if (cur->data == pre) { //插入节点
newNode->next = cur->next;
cur->next = newNode;
break;
} else
cur = cur->next;
}
}
//删除指定值节点
int del;
cin >> del;
LNode* dummyHead = new LNode(0); //创建虚拟头节点,统一删除操作
dummyHead->next = L; //成为链表第一个节点
LNode* cur = dummyHead;
while (cur->next) {//防止cur->next->data这句报错
if (cur->next->data == del) { //删除操作
LNode* tmp = cur->next; //暂存节点,方便释放
cur->next = cur->next->next;
delete tmp; //释放删除节点
} else
cur = cur->next;
}
delete dummyHead; //释放虚拟头节点
LNode* tra = L;
while (tra) { //遍历打印
cout << tra->data << " ";
tra = tra->next;
}
return 0;
}
- 时间复杂度 --- 插入节点需要遍历n-1次,其中寻找前驱节点也需要遍历指针,其中n为链表长度。
- 空间复杂度 --- 链表空间属于必要空间,不属于额外空间
c++代码实现2-数组模拟
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
//方法二:数组模拟
int main() {
int num, data; // num节点总数,data头节点数值
int aft, pre; // aft待插入节点数值,pre插入节点的前驱节点数值
cin >> num >> data;
vector<int> res; //创建一个数组,模拟链表
res.push_back(data); //将表头数值放在数组开头
while (--num) {
cin >> aft >> pre;
auto index = find(res.begin(), res.end(), pre); //找到前驱节点的下标
if (index == res.end()) //当前驱节点是数组最后一个
res.push_back(aft); //插入到数组末尾
else
res.insert(++index, aft); //插入到前驱节点的下一个位置
}
//删除指定值节点
int del;
cin >> del;
res.erase(find(res.begin(), res.end(), del));
//遍历打印
for (auto it = res.begin(); it != res.end(); it++) {
cout << *it << " ";
}
return 0;
}
- 时间复杂度 --- 插入节点需要遍历n-1次,find函数复杂度为O(n)
- 空间复杂度 --- 数组属于必要空间,无额外的空间
总结
-
一定要熟悉基本数据结构的一些基本操作。
-
对于一些常见库函数的复杂度以及内部实现方式要有所了解。