⭐算法入门⭐《哈希表》中等03 —— LeetCode 380. O 时间插入删除和获取随机元素
Posted 英雄哪里出来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了⭐算法入门⭐《哈希表》中等03 —— LeetCode 380. O 时间插入删除和获取随机元素相关的知识,希望对你有一定的参考价值。
🙉饭不食,水不饮,题必须刷🙉
C语言免费动漫教程,和我一起打卡! 🌞《光天化日学C语言》🌞
LeetCode 太难?先看简单题! 🧡《C语言入门100例》🧡
数据结构难?不存在的! 🌳《画解数据结构》🌳
LeetCode 太简单?算法学起来! 🌌《夜深人静写算法》🌌
一、题目
1、题目描述
设计一个支持在平均 时间复杂度 O ( 1 ) O(1) O(1) 下,执行以下操作的数据结构。
insert(val)
:当元素 v a l val val 不存在时,向集合中插入该项。
remove(val)
:元素 v a l val val 存在时,从集合中移除该项。
getRandom
:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。
样例输入:["RandomizedSet","insert","remove","insert","getRandom","remove","insert","getRandom"] [[],[1],[2],[2],[],[1],[2],[]]
样例输出:[null,true,false,true,2,true,false,2]
2、基础框架
- C语言版本 给出的基础框架代码如下:
typedef struct {
} RandomizedSet;
/** Initialize your data structure here. */
RandomizedSet* randomizedSetCreate() {
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
bool randomizedSetInsert(RandomizedSet* obj, int val) {
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
bool randomizedSetRemove(RandomizedSet* obj, int val) {
}
/** Get a random element from the set. */
int randomizedSetGetRandom(RandomizedSet* obj) {
}
void randomizedSetFree(RandomizedSet* obj) {
}
/**
* Your RandomizedSet struct will be instantiated and called as such:
* RandomizedSet* obj = randomizedSetCreate();
* bool param_1 = randomizedSetInsert(obj, val);
* bool param_2 = randomizedSetRemove(obj, val);
* int param_3 = randomizedSetGetRandom(obj);
* randomizedSetFree(obj);
*/
3、原题链接
( 1 ) (1) (1) 380. O(1) 时间插入、删除和获取随机元素
( 2 ) (2) (2) 剑指 Offer II 030. 插入、删除和随机访问都是 O(1) 的容器
二、解题报告
1、思路分析
1)维护一个哈希表和两个映射数组,这两个数组分别是 哈希地址到数组下标 的映射,和 数组下标到哈希地址 的映射;
2)插入操作:对哈希表执行查找,如果没有找到,则执行插入,插入过程中更新两个映射数组;
3)删除操作:对哈希表执行查找,如果找到,则执行删除,删除过程中对映射数组执行和最后一个元素进行交换,保证删除是
O
(
1
)
O(1)
O(1) 的;
4)随机获取:对 数组下标到哈希地址 这个映射数组进行随机,得到一个哈希地址,再去哈希表中直接通过下标索引找到关键字后返回即可;
2、时间复杂度
- 哈希表的插入查找均摊复杂度为 O ( 1 ) O(1) O(1),总共 n n n 个数字的枚举,时间复杂度为 O ( n ) O(n) O(n)。
3、代码详解
/******************** 哈希表 开放定址法 ********************/
#define maxn (1<<17)
#define mask (maxn-1)
#define DataType int
#define Boolean int
#define NULLKEY (1<<30) /* 空槽标记不能用-1,会导致正常值也为-1的情况*/
typedef struct {
DataType data[maxn];
}HashTable;
void HashInit(HashTable *ht) {
int i;
for(i = 0; i < maxn; ++i) {
ht->data[i] = NULLKEY;
}
}
int HashGetAddr(DataType key) {
return key & mask; // 除留余数法
}
Boolean HashSearchKey(HashTable *ht, DataType key, int *addr) {
int startaddr = HashGetAddr(key);
*addr = startaddr;
while(ht->data[*addr] != key) {
*addr = HashGetAddr(*addr + 1);
if(ht->data[*addr] == NULLKEY) {
return 0; // 遇到了空槽,说明没找到,返回 0
}
if(*addr == startaddr) {
return 0; // 找了一圈都没找到,循环了
}
}
return 1;
}
int HashInsert(HashTable *ht, DataType key) {
int addr = HashGetAddr(key);
int retaddr;
if ( HashSearchKey(ht, key, &retaddr ) ) {
return retaddr;
}
while(ht->data[addr] != NULLKEY)
addr = HashGetAddr(addr + 1);
ht->data[addr] = key;
return addr;
}
int HashRemove(HashTable *ht, DataType key) {
int addr;
if ( !HashSearchKey(ht, key, &addr ) ) {
return NULLKEY;
}
ht->data[addr] = NULLKEY;
return addr;
}
/******************** 哈希表 开放定址法 ********************/
typedef struct {
HashTable ht;
int addr2pos[maxn]; // (1)
int pos2addr[maxn]; // (2)
int pos;
} RandomizedSet;
/** Initialize your data structure here. */
RandomizedSet* randomizedSetCreate() {
RandomizedSet *rs = (RandomizedSet *)malloc( sizeof(RandomizedSet) );
HashInit( &rs->ht );
rs->pos = 0; // (3)
return rs;
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
bool randomizedSetInsert(RandomizedSet* obj, int val) {
int addr;
if (!HashSearchKey(&obj->ht, val, &addr)) {
addr = HashInsert(&obj->ht, val);
obj->addr2pos[ addr ] = obj->pos; // (4)
obj->pos2addr[ obj->pos++ ] = addr; // (5)
return true;
}
return false;
}
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
bool randomizedSetRemove(RandomizedSet* obj, int val) {
int addr, pre;
if (HashSearchKey(&obj->ht, val, &addr)) {
addr = HashRemove( &obj->ht, val );
pre = obj->addr2pos[ addr ]; // (6)
obj->addr2pos[ obj->pos2addr[obj->pos-1] ] = pre; // (7)
swap( &obj->pos2addr[pre], &obj->pos2addr[obj->pos-1] ); // (8)
-- obj->pos;
return true;
}
return false;
}
/** Get a random element from the set. */
int randomizedSetGetRandom(RandomizedSet* obj) {
return obj->ht.data[ obj->pos2addr[ rand() % obj->pos ] ];
}
void randomizedSetFree(RandomizedSet* obj) {
free(obj);
}
/**
* Your RandomizedSet struct will be instantiated and called as such:
* RandomizedSet* obj = randomizedSetCreate();
* bool param_1 = randomizedSetInsert(obj, val);
* bool param_2 = randomizedSetRemove(obj, val);
* int param_3 = randomizedSetGetRandom(obj);
* randomizedSetFree(obj);
*/
- ( 1 ) (1) (1) 哈希表地址到数组下标的映射;
- ( 2 ) (2) (2) 数组下标到哈希表地址的映射;
- ( 3 ) (3) (3) 初始化数组长度为 0;
- ( 4 ) − ( 5 ) (4) - (5) (4)−(5) 插入数据的过程中,同时记录 哈希表地址 和 数组下标的映射关系;
- ( 6 ) − ( 8 ) (6) - (8) (6)−(8) 将数组中需要删除的那个元素,和数组最后一个元素进行交换,这样可以做到删除的时候是 O ( 1 ) O(1) O(1) 的;
三、本题小知识
在一个数组中删除数据,可以将它和最后一个数组元素进行交换,然后再将数组的size减一,这样可以做到 O ( 1 ) O(1) O(1),只不过这样做会改变原有数组顺序,如果对顺序要求不大就可以这么做。
以上是关于⭐算法入门⭐《哈希表》中等03 —— LeetCode 380. O 时间插入删除和获取随机元素的主要内容,如果未能解决你的问题,请参考以下文章
⭐算法入门⭐《哈希表》中等04 —— LeetCode 347. 前 K 个高频元素
⭐算法入门⭐《哈希表》中等02 —— LeetCode 560. 和为K的子数组
⭐算法入门⭐《哈希表》中等05 —— LeetCode 215. 数组中的第K个最大元素