LeetCode 380. O(1) 时间插入、删除和获取随机元素【哈希表 + 动态数组 | C语言详解】

一、题目描述

实现 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)
相关推荐
Gigavision4 小时前
基于BUAA-MIHR数据集的噪声解耦对比学习算法
人工智能·python·深度学习·算法
鹿角片ljp5 小时前
LeetCode 64:最小路径和复盘|二维 DP 与 ACM 模式完整写法
java·数据结构·算法
aramae5 小时前
模拟实现strcmp()(C语言)
c语言·开发语言·后端
彧azz6 小时前
算法设计与分析:贪心与动态规划
数据结构·学习·算法·贪心算法·动态规划
Beyond_System|系统之外7 小时前
【学编程】Python基础编程题100道(21-60)
开发语言·python·算法
景熙55237 小时前
15.Java 8 Stream 流入门到实战
java·开发语言·数据结构
汉克老师8 小时前
GESP2026年9月认证C++六级( 第三部分编程题(1、数组划分))精讲
c++·gesp·小学生·学c++编程
千里码aicood8 小时前
基于PLC与机器视觉的智能分拣控制系统设计
c++·自动化·plc
漂流瓶jz8 小时前
UVA-1442 洞穴 题解答案代码 算法竞赛入门经典第二版
c++·算法·题解·aoapc·算法竞赛入门经典·uva
圣保罗的大教堂8 小时前
leetcode 835. 图像重叠 中等
leetcode