实现满射数据结构?

问题描述

我有兴趣对一组数据执行以下操作。

首先,我们给出一组键,例如:

vector<int> keys{1,2,3,4,5,6};

这些键中的每一个都被理解为指向一个唯一的条目(具体说明并不重要,重要的是每个键是指向一个单独的条目还是某些键指向同一个条目的关系)。最初,我们不知道是否有任何键指向同一个条目,因此我们从一个数据结构开始,将所有条目视为每个键的单独条目:

surjectiveData<int> data;
data.populateUnique(keys.begin(),keys.end());

以图形方式,我们可以将 data 的当前状态说明为

enter image description here

我们使用标签 a,b,c,d,e,f 来跟踪 data 中的唯一条目。现在,考虑添加有关哪些键指向同一条目的附加信息。例如:

vector<pair<int,int>> identifications{make_pair(1,2),make_pair(3,4),make_pair(2,make_pair(5,6)};
data.couple(identifications.begin(),indentifications.end());

couplesurjectiveData 方法遍历提供的对,并使它们指向相同的唯一条目。在图形上,四个 identifications 将依次更改 data 如下:

enter image description here

enter image description here

enter image description here

enter image description here

现在在 data 中只有两个唯一的条目,这里我们表示 abcdef。请注意,一旦两个或多个键指向同一个条目,这些键中的哪一个被哪个单独的键标识并不重要,它们在标识后都指向同一个条目。

既然我们已经完成了指定密钥标识,我们可以考虑使用 data 如下。例如,我们可以询问唯一剩余条目的有效数量是多少

cout<<data.size()<<endl; // 2

或者,我们可以遍历条目并检查有多少键指向每个条目

for(auto it=data.begin();it!=data.end();it++){
    cout<<it->size()<<" ";// 4 2
}

理想情况下,如果可能,结构在内部应该为每个标识花费恒定的时间。

我试图在标准库中搜索这样的数据结构,但没有找到。我错过了吗?也许有一种聪明的方法可以基于更基本的对象来实现它?如果是这样,整数的最小示例是什么?

解决方法

不相交集数据结构可以支持您描述的操作:https://en.wikipedia.org/wiki/Disjoint-set_data_structure

这是一个支持3种操作的链接数据结构:

  1. makeSet() 创建一个新的单例集合并返回它的元素
  2. union(a,b) 给定两个元素,合并包含它们的集合。每个集合的一个元素将是该集合的“代表”
  3. find(a) 返回包含 a 的集合的代表。

所有操作几乎都需要固定的摊销时间。

我通常在单个向量中实现这个数据结构,其中每个数组索引表示一个集合元素。如果它的值>0,则它是一个集合代表,该值是集合的大小。如果它的值

记录套数并不难。

在 C++ 中,我通常的实现如下所示:

class DijointSets {
    unsigned num_sets;
    std::vector<int> sets;

    public:

    // Create a new singleton set and return its element
    unsigned make_set() {
        unsigned ret = (unsigned)sets.size();
        sets.push_back(1);
        ++num_sets;
        return ret;
    }

    // Find the representative element of an element's set
    unsigned find(unsigned x) {
        int p = sets[x];
        if (p>=0) {
            return x;
        }
        p = find(~p);
        sets[x] = ~p;  //might be the same
        return p;
    }
    
    // Merge the sets that contain two elements
    // returns true if a merge was done
    boolean union(unsigned a,unsigned b) {
        a = find(a);
        b = find(b);
        if (a==b) {
            return false;
        }
        if (sets[a] > sets[b]) {
            sets[a] += sets[b]; //add sizes
            sets[b] = ~(int)a;
        } else {
            sets[b] += sets[a]; //add sizes
            sets[a] = ~(int)b;
        }
        --num_sets;
        return true;
    }

    // get the size of an element's set
    unsigned set_size(x) {
        return sets[find(x)];
    }

    // get the number of sets 
    unsigned set_count() {
        return num_sets;
    }
}