我认为std::set
没有比O(n)
更好的方法来实现这个功能,但是最近我使用了一个集合和一棵二进制索引树来创建这个数据结构。它可以完成std::set
能做的大多数事情,而且可以在O(log n)
时间内获取元素的索引,以及在O((log n) * (log n))
时间内获取特定索引处的元素:
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <bitset>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
using namespace std;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef long long ll;
typedef pair<ll, ll> pll;
#define max(n, m) ((n>m)?n:m)
#define min(n, m) ((n<m)?n:m)
#define f first
#define s second
struct ss
{
int bit[1000010];
set<int> nums;
int mx;
ss(int maxEl)
{
mx = maxEl + 5;
}
int sum(int arr[], int idx)
{
int ans = 0;
idx ++;
if(idx > mx + 5) return -1;
while(idx > 0)
{
ans += arr[idx];
idx -= idx & (-idx);
}
return ans;
}
void update(int arr[], int idx, int val, int size)
{
idx ++;
while(idx <= size)
{
arr[idx] += val;
idx += idx & (-idx);
}
}
int bs(int l, int r, int idx)
{
int mid = (l + r) / 2;
if(l == r) return mid + 1;
if(l == r - 1)
{
if(sum(bit, r) == idx) return r + 1;
return r;
}
if(sum(bit, mid) <= idx) return bs(mid, r, idx);
return bs(l, mid - 1, idx);
}
set<int>::iterator find(int num) { return nums.find(num); }
set<int>::iterator lower_bound(int num) { return nums.lower_bound(num); }
set<int>::iterator upper_bound(int num) { return nums.upper_bound(num); }
int size() { return (int)nums.size(); }
set<int>::iterator begin() { return nums.begin(); }
set<int>::iterator end() { return nums.end(); }
bool empty() { return nums.empty(); }
void insert(int num)
{
if(nums.find(num) == nums.end())
update(bit, num, 1, mx);
nums.insert(num);
}
void erase(int num)
{
if(nums.find(num) != nums.end())
update(bit, num, -1, mx);
nums.erase(num);
}
int idx(int num)
{
if(nums.find(num) == nums.end())
return -1;
return sum(bit, num - 1);
}
set<int>::iterator at(int idx)
{
if(idx < 0 || idx >= nums.size())
return nums.end();
return nums.find(bs(0, mx, idx));
}
};
int main()
{
ss test = ss(1000);
test.insert(1);
test.insert(3);
test.insert(5);
test.insert(1);
test.insert(9);
test.insert(1000);
cout << *test.at(1) << "\n";
test.erase(3);
cout << *test.at(1) << "\n";
cout << test.idx(1) << "\n";
cout << *test.at(-1) << "\n";
}
这个集合存在一些缺陷,因为它标记了二进制索引树中的元素,所以元素不能是负数或非常大的数,除非进行额外的修改,但在某些情况下仍然有用。此外,使用
std :: map
或某种其他类型的映射可以使该集合与负数、大数以及其他数据类型一起工作,但这将使运行时间增加一个
O(log n)
的因子,而且您必须预先知道可能出现在集合中的所有元素,以便可以按正确的顺序将它们存储在映射内。
编辑:我刚刚意识到已经有一个名为ordered-set的基于策略的数据结构具有与集合相同的功能,但可以在
O(log n)
的时间内执行两个操作(获取索引处的元素和获取元素的索引)。在此处阅读更多信息:
https://www.geeksforgeeks.org/ordered-set-gnu-c-pbds/。但这可能不适用于所有编译器。
my_set[0]
不应该被编译。 - chris