一、题目描述
实现 RandomizedSet 类:
-
RandomizedSet()初始化对象 -
insert(val):当元素val不存在时插入,并返回true -
remove(val):当元素存在时删除,并返回true -
getRandom():随机返回集合中的一个元素,每个元素被返回的概率相同
要求:所有操作平均时间复杂度 O(1)。
示例:
输入:
["RandomizedSet","insert","remove","insert","getRandom","remove","insert","getRandom"]
[[],[1],[2],[2],[],[1],[2],[]]
输出:
[null,true,false,true,2,true,false,2]
二、题目分析
题目要求:
| 操作 | 时间复杂度 |
|---|---|
| insert | O(1) |
| remove | O(1) |
| getRandom | O(1) |
普通数据结构:
| 数据结构 | 问题 |
|---|---|
| 数组 | 删除 O(n) |
| 哈希表 | 无法随机访问 |
因此需要 结合两种结构:
动态数组 + 哈希表
三、核心数据结构
我们维护两个结构:
1️⃣ 动态数组 nums
存储所有元素
nums = [1,2,3]
用于:
O(1) 随机访问
2️⃣ 哈希表 map
存储:
value -> index
例如:
nums: [1,2,3]
map:
1 -> 0
2 -> 1
3 -> 2
这样可以 O(1) 找到元素位置。
四、操作实现
1 插入 insert
步骤:
1 判断 val 是否存在
2 若存在 return false
3 nums尾部插入
4 map[val] = index
示例:
插入 4
nums: [1,2,3,4]
map:
1->0
2->1
3->2
4->3
时间复杂度:
O(1)
五、删除操作(核心)
删除是本题 最关键技巧。
如果直接删除:
nums = [1,2,3,4]
删除 2
数组需要移动:
[1,3,4]
时间复杂度变成:
O(n)
因此我们使用 覆盖删除法:
删除步骤
1 找到 val 的 index
2 取最后一个元素 last
3 nums[index] = last
4 更新 map[last]
5 删除最后元素
示例:
nums = [1,2,3,4]
删除 2
步骤:
index = 1
last = 4
nums[1] = 4
结果:
nums = [1,4,3]
map:
1->0
4->1
3->2
时间复杂度:
O(1)
六、随机获取元素
由于数组支持随机访问:
rand() % size
即可随机返回。
示例:
nums = [1,4,3]
rand()%3 -> 0~2
七、完整 C 语言实现
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#define SIZE 200003
typedef struct Node {
int key;
int val;
struct Node* next;
} Node;
typedef struct {
int* nums;
int size;
int capacity;
Node* hash[SIZE];
} RandomizedSet;
int hashFunc(int key) {
long long k = key;
if (k < 0) k = -k;
return k % SIZE;
}
Node* findNode(Node* head, int key) {
while (head) {
if (head->key == key)
return head;
head = head->next;
}
return NULL;
}
RandomizedSet* randomizedSetCreate() {
RandomizedSet* obj = malloc(sizeof(RandomizedSet));
obj->capacity = 200000;
obj->size = 0;
obj->nums = malloc(sizeof(int) * obj->capacity);
for (int i = 0; i < SIZE; i++)
obj->hash[i] = NULL;
srand(time(NULL));
return obj;
}
bool randomizedSetInsert(RandomizedSet* obj, int val) {
int h = hashFunc(val);
if (findNode(obj->hash[h], val))
return false;
Node* node = malloc(sizeof(Node));
node->key = val;
node->val = obj->size;
node->next = obj->hash[h];
obj->hash[h] = node;
obj->nums[obj->size++] = val;
return true;
}
bool randomizedSetRemove(RandomizedSet* obj, int val) {
int h = hashFunc(val);
Node* cur = obj->hash[h];
Node* prev = NULL;
while (cur && cur->key != val) {
prev = cur;
cur = cur->next;
}
if (!cur)
return false;
int index = cur->val;
int last = obj->nums[obj->size - 1];
obj->nums[index] = last;
int h2 = hashFunc(last);
Node* node = findNode(obj->hash[h2], last);
node->val = index;
obj->size--;
if (prev)
prev->next = cur->next;
else
obj->hash[h] = cur->next;
free(cur);
return true;
}
int randomizedSetGetRandom(RandomizedSet* obj) {
int r = rand() % obj->size;
return obj->nums[r];
}
void randomizedSetFree(RandomizedSet* obj) {
for (int i = 0; i < SIZE; i++) {
Node* cur = obj->hash[i];
while (cur) {
Node* tmp = cur;
cur = cur->next;
free(tmp);
}
}
free(obj->nums);
free(obj);
}
八、复杂度分析
| 操作 | 时间复杂度 |
|---|---|
| insert | O(1) |
| remove | O(1) |
| getRandom | O(1) |
空间复杂度:
O(n)
九、总结
本题核心思想:
动态数组 + 哈希表
关键技巧:
删除元素时
用最后一个元素覆盖被删除元素
这样可以避免数组移动,从而保证:
删除操作 O(1)