Day14 2023/03/14
难度:简单
题目
给定一个已排序的链表的头heād,删除所有重复的元素使每个元素只出现一次。返回已排序 的链表。
提示:
链表中节点数目在范围(0,300)内
-100<=Node.va<=100
题目数据保证链表已经按升序排列
示例
输入:head = [1, 1, 2]
输出:[1,2]
运行实例
思路
本题是对链表进行去重操作,核心步骤就是判断某节点是不是重复元素,可以类比为-快速判断某个元素在该集合中是否存在,像这样的题目最常规的,最好用的做法就是哈希法,即使用一个哈希表作为辅助数组,用来记录每个元素出现的次数,对出现次数大于1的节点,进行删除去重即可。
具体步骤:
- 遍历链表,从虚拟头节点开始,每次判断下一个节点,是否为首次出现,(其中
recorde[val] == 0表示首次出现,否则不是),如果是首次出现就将该节点记录在辅助数中record中,否则删除该节点。
关键点
- 每次判断下一个节点,是否为首次出现,而不是判断当前节点,是为了方便删除操作,这样每次删除一个节点的时候,我们都可以快速的拿到该节点的前驱节点。
- 辅助数组中
record[key] = val,其中key为节点的数据,val为该节点出现的次数。
算法实现
c++代码实现-删除排序链表中的重复元素
#include <iostream>
#include <unordered_map>
using namespace std;
//定义链表节点
typedef struct LNode {
int val; //数据域
LNode *next; //指针域
LNode(int val) : val(val), next(nullptr){}; //构造函数
} * LinkList, LNode;
//创建链表(带虚拟头节点的尾插法)
LinkList ListTailInsert(int n) { // num为链表节点个数
LNode *dummyHead = new LNode(-1), *r = dummyHead,
*s; //虚拟头节点 尾指针,待插入节点指针
int val; //带插入节点的数据
while (n-- && cin >> val) {
s = new LNode(val);
r->next = s;
r = s;
}
cout << "链表创建完毕!!!" << endl;
return dummyHead; // 返回虚拟头节点,方便操作
}
//对链表数据域相同的节点进行去重操作
void DelDupNode(LinkList &L) {
LNode *cur = L, *tmp; // 遍历指针,临时指针
unordered_map<int, int> record; // 辅助数组,记录各个节点出现的次数
while (cur->next) {
int val = cur->next->val; // 数据域
if (record[val] == 0) { // 对于第一次出现的节点,把它记录到辅助数组中
record[val] = 1;
cur = cur->next;
} else {
tmp = cur->next; // 重复节点
cur->next = cur->next->next; // 防止断链
delete tmp; // 删除重复节点
}
}
}
int main() {
//创建链表 ,测试数据[1,1,2]
LinkList L = ListTailInsert(3); //创建链表
DelDupNode(L); //去重
LNode *cur = L->next;
cout << "去重后的链表:";
while (cur) {
cout << cur->val << ' ';
cur = cur->next;
}
return 0;
}
- 时间复杂度 --- 不考虑创建链表所占用的时间,仅遍历整个链表,其中n为链表节点个数
- 空间复杂度 ---最坏情况下,该链表无重复节点,record辅助数组所占用的空间就等于链表节点个数n
总结
- 小Tip: 当遇到快速判断某个集合中是否含有该元素的时候,一般可以尝试一下哈希法!!!(😏)