更加复杂的数据结构
1.并查集
并查集(union-find,or disjoint set)可以动态的连通两个点,并且可以非常快速的判断两个点是否连通。假设存在n个节点,我们将所有节点的父亲标为自己;每次要连接节点i和j时,我们可以将i的父亲标为j;每次要查询两个节点是否相连时,我们可以查找i和j的祖先是否最终为一个人。
题目一:Redundant Connection(Medium)
在无向图找出一条边,移除它之后该图能够成为一棵树(即无向无环图)。如果有多个解,返
回在原数组中位置最靠后的那条边。
因为需要判断是否两个节点被重复连通,可以使用并查集来解决此类问题。具体实现算法如下所示。
class UF
{
vector<int>id;
public:
UF(int n): id(n){
id.begin(),id.end(),0);//iota函数可以把数组初始化为0到n-1
}
int find(int p)
{
while(p!-id[p])
{
p=id[p];
}
return p;
}
void connect(int p,int q)
{
id[find(p)]=find(q);
}
bool isConnected(int p,int q)
{
retrun find(p)==find(q);
}
};
class Solution{
public:
vector<int> findRedundantConnection(vector<vector<int>>&edges)
{
int n=dege.size();
UF uf(n+1);
for(auto e:edges)
{
int u=e[0],v=e[1];
if(uf.isConnected(u,v))
{
return e;
}
uf.connect(u,v);
}
return vector<int>{-1,-1};
}
};
为了加速查找,我们可以使用路径压缩和按秩合并来优化并查集,具体下发如下:
class UF
{
vector<int>id,size;
public:
UF(int n):id(n),size(n,1)
{
iota(id.begin(),id.end(),0);//iota函数可以把数组初始化为0到n-1
}
int find(int p)
{
while(p!=id[p])
{
id[p]=id[id[p]];//路径压缩,使得下次查找更快
p=id[p];
}
return p;
}
void connect(int p,int q)
{
int i=find(p),j=find(q);
if(i!=j)
{
//按秩合并,每次合并都把深度较小的集合合并在深度较大的集合下边
if(size[i]<size[j])
{
id[i]=j;
size[j]+=size[i];
}
else{
id[j]=i;
size[i]+=size[j];
}
}
}
bool isConnected(int p,int q)
{
return find(p)==find(q);
}
};
2.复杂数据结构
这一类题通常采用 unordered_map 或 map 辅助记录,从而加速寻址;再配上 vector 或者 list进行数据储存,从而加速连续选址或删除值。
LRU Cache(Medium)
设计一个固定大小的,最少最近使用缓存 (least recently used cache, LRU)。每次将信息插入未满的缓存的时候,以及更新或查找一个缓存内存在的信息的时候,将该信息标为最近使用。在缓存满的情况下将一个新信息插入的时候,需要移除最旧的信息,插入新信息,并将该信息标为最近使用。
我们采用一个链表 list<pair<int, int>> 来储存信息的 key 和 value,链表的链接顺序即为最近使用的新旧顺序,最新的信息在链表头节点。同时我们需要一个嵌套着链表的迭代器的 unordered_map<int, list<pair<int, int>>::iterator> 进行快速搜索,存迭代器的原因是方便调用链表的splice 函数来直接更新查找成功(cash hit)时的信息,即把迭代器对应的节点移动为链表的头节点。
class LRUCache
{
unordered_map<int,list<pair<int,int>>::iterator>>hash;
list<pair<int,int>>cache;
int size;
public:
LRUCache(int capacity):size(capacity){}
int get(int key)
{
auto it=hash.find(key);
if(it==hash.end())
{
return -1;
}
cache.splice(cache.begin(),cache,it->second);
return it->second-second;
}
void put(int key,int value)
{
auto it=hash.find(key);
if(it!=hash.end())
{
it->second->second=value;
return cache.splice(cache.begin(),cache,it->second);
}
cache.insert(cache.begin(),make_pair(key,value));
hash[key]=cache.begin();
if(cache.size()>size)
{
hash.erase(cache.back().first);
cache.pop_back();
}
}
};