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)
相关推荐
来自远方的老作者2 分钟前
第7章 运算符-7.2 赋值运算符
开发语言·数据结构·python·赋值运算符
A.A呐3 分钟前
【C++第二十四章】异常
开发语言·c++
wanderist.5 分钟前
算法模板-字符串
数据结构·算法·哈希算法
xiaoye-duck5 分钟前
《算法题讲解指南:动态规划算法--子序列问题》--29.最长递增子序列的个数,30.最长数对链,31.最长定差子序列
c++·算法·动态规划
森G8 分钟前
39、拓展知识---------事件系统
c++·qt
Yzzz-F9 分钟前
Problem - 2180D - Codeforces
算法
moonsea020310 分钟前
2023.9.25
算法
汀、人工智能13 分钟前
[特殊字符] Python基础语法速成教程
算法·链表·均值算法·哈希表·lru缓存·python基础语法速成教程
tankeven17 分钟前
HJ164 太阳系DISCO
c++·算法
来自远方的老作者20 分钟前
第7章 运算符-7.1 算术运算符
开发语言·数据结构·python·算法·算术运算符