真题题目
用单链表保存 m个整数,结点的结构为[data][link],且
∣
d
a
t
a
∣
⩽
n
|data|\leqslant n
∣data∣⩽n(n为正整数)。
现要求设计一个时间复杂度尽可能高效的算法,对于链表中 data 的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。例如,若给定的单链表 HEAD 如下:
HEAD -> [21] -> [15] -> [15] -> [-7] -> [15]^
则删除结点后的 HEAD 为
HEAD -> [21] -> [15] -> [-7]^
要求:
- 给出算法的基本设计思想。
- 使用 C 或 C++语言,给出单链表结点的数据类型定义。
- 根据设计思想,采用C或 C++语言描述算法,关键之处给出注释。
- 说明你所设计算法的时间复杂度和空间复杂度。
(本文重点关注算法实现及思路分析,不含具体答题表述)
分析实现
本题的大部分要求都很容易理解并运用,重点在于 ∣ d a t a ∣ ⩽ n |data|\leqslant n ∣data∣⩽n 这个条件。
与此条件之经常一同使用的是桶计数的思想——建立一个大小为n的数组have
,每个数组元素记录一个数值的存在情况。如果当前数值已经存在,就删除当前结点;如果当前数组未出现过,就更新have
,继续移动链表。
类似于【删除链表中最小结点】,在本题中也定义了指针pre
记录当前节点的前驱结点以便于删除当前结点。
此外为书写简洁,本题指针的初始化和循环更新的逻辑也有一些习惯性的调整,具体解题时逻辑合理即可。
具体实现如下:
#include <vector>
#include <cmath>
// 链表结点的定义
struct LNode {
int data;
LNode *link;
};
// 删除绝对值相同元素
void removeRep(LNode *head, int n){
// 用布尔数组记录数值出现的情况,并初始化为0 - 表示未出现
vector<bool> have(n+1, 0);
LNode *pre = head, *cur;
while(pre->link){
cur = pre->link;
// 当前节点的值已经出现过 - 删除当前节点
if(have[abs(cur->data)]){
pre->link = cur->link;
delete cur;
}
// 当前节点的值未出现过 - 更新have数组,并移动pre指针
else{
have[abs(cur->data)] = 1;
pre = cur;
}
}
}
总结
以上就是删除单链表中绝对值相同的点的代码实现。本题重在理解桶计数的思想,将元素的存在信息存入数组之中。
为方便表示,此处建立的是大小为n+1的数组来记录n个元素出现的情况,这样就可以令have[i]直接表示元素i的出现情况,更加容易将数组下标和元素进行对应。
此外要注意对删除的结点进行内存空间的释放,并理解delete cur;
执行后,只会将指针指向内容的内存释放,但指针变量cur
依旧存在。