这是一个面试问题:Set
课程包含get
,put
和getRandom
。
我会考虑以下选项:
get
- O(N),put
- O(N),getRandom
- O(N)get
- O(N),put
- ?,getRandom
- O(1)get
- O(logN),put
- ?,getRandom
- O(1)get
- O(1),put
- O(1),getRandom
- O(表格大小)get
- O(logN),put
- O(logN),getRandom
- O(N)看起来最好的候选人是:
get/put
比getRandom
getRandom
比get/put
更频繁,则排序的矢量(可调整大小的数组)现在我想知道我们是否可以以某种方式组合向量和散列表来构成更好的集合。
答案 0 :(得分:5)
您可以将get
,put
和getRandom
的平均时间设为O(1)
。
您所做的是存储2个数据结构。一个是哈希。另一个在可扩展数组中以随机顺序列出元素。
当你put
时,你将它放在哈希中,将元素添加到数组的末尾,然后将数组的末尾交换为随机数组元素。
当你get
时,你会查看元素的哈希值。
当你getRandom
时,你取出数组的最后一个元素,然后将最后一个元素与数组中的另一个点交换。
如果您愿意,可以添加delete
,只需删除哈希即可。现在getRandom
是通过获取元素来实现的,检查它是否在散列中,如果不是,则缩小数组,然后重复。此时getRandom
偶尔O(n)
但所有操作的摊销平均成本可以证明为O(1)
。
答案 1 :(得分:1)
如果你保留一个单独的结构,告诉你哈希表的每个桶中有多少项,你可以使用二进制搜索来找到第n个元素,这将给你所有三个元素的O(log n)操作
一个平衡的二进制搜索树,每个节点增加一个“count”(告诉有多少个节点的子树根植于这个节点)对这些边界也有效。
对上述内容进行了一些更正:您无法在链表中进行随机访问,因此所有操作都是O(N)。另外,两个向量中的put都是O(n),因为必须替换排序版本中的元素并检查未排序版本中的重复项。
答案 2 :(得分:0)
#include <stdio.h>
#include <vector>
#define MOD 666013
using namespace std;
int N;
vector<int> G[MOD];
vector<int>::iterator find(int x)
{
int list=x%MOD; // f(i) = i % MOD this is my hash function
vector<int>::iterator it;
for (it=G[list].begin();it!=G[list].end();++it)
if (*it==x)
return it;
return G[list].end();
}
void add(int x)
{
int list=x%MOD; //again this is my hash function that gives me the key
if (find(x)==G[list].end())
G[list].push_back(x);
}
void delete(int x)
{
int list=x%MOD;
vector<int>::iterator it=find(x);
if (it!=G[list].end())
G[list].erase(it);
}