给定Q查询和一个空列表。
查询可以有两种类型:
- addToList(x):将x添加到列表中。
- removeFromList(x):从列表中删除x。
任务是,在每次查询之后,您必须打印abs(list [i] -list [j])的最小值,其中0 <= i <= n,0 <= j <= n且i≠j和n是列表中存在的元素总数。例子:
Input : Q = 4
addToList(1), insert 1 in our set
addToList(5), insert 5 in our set
addToList(3), insert 3 in our set
removeFromList(3), delete 3 in our set
Output :
0, as we have only one element {1} in our set.
4, as we have {1, 5} minimum difference between all possible pairs is 4 (5-1)
2, as we have {1, 3, 5} minimum difference between all possible pairs is 2 (3-1)
4, as we have {1, 5} minimum difference between all possible pairs is 4 (5-1)
方法1:想法是使用C++中的set来存储所有元素,以便可以在O(log(n))中完成插入或删除操作,同时保持元素的排序顺序。现在,要找到最小差异,我们必须遍历整个集合并找到仅相邻元素之间的差异,因为元素按排序顺序排列,即,最小差异将始终由相邻元素贡献。这可以以O(n)时间复杂度来完成。因此,此方法的总体时间复杂度将为(q * log(n)+ q * n)。
方法2:
我们可以使用多重集来存储和维护所有元素,并使用map来按排序顺序存储相邻元素之间的差异,其中map的键表示相邻元素之间的差异,而对应的map的值表示出现这种差异的频率。
下面是完整的算法:
首先,在多组中插入两个前哨值。假设(-10 ^ 9 + 7和10 ^ 9 + 7)并使用这些哨兵值的差(即2 * 10 ^ 7 + 7)将地图初始化,并将其频率设置为1。
现在,我们有两种类型的查询:
- 插入查询:对于插入查询,请首先在集合中插入值。插入元素后,我们还必须更新地图中相邻元素的差异。要做到这一点,发现在一组新插入的元素的左,右的值。早在此新值未插入到集合中时,abs(right-left)会影响地图中的差分项。要在插入新元素后更新地图,请先删除先前的差异abs(右-左),因为在元素之间左右插入了新值,并向地图添加了两个差异。即abs(right-x)和abs(x-left)。
- 删除查询:从集合中删除值的情况。首先,需要找到要删除的元素的左,右的元素,例如x。然后根据上述算法,降低abs(右x)和abs(左x)的频率,并增加abs(右-左)的频率。
因此,可以以log(n)时间复杂度解决每个查询。因此,总体复杂度将为O(q * log(n))。
// C++ implementation of above approach
#include
#define ll long long int
const ll MOD = 1e9 + 7;
using namespace std;
// Function to add an element into the list
void addToList(int x, multiset& s, map& mp)
{
// firstly insert value in set
s.insert(x);
// find left and right value of inserted value.
ll left, right;
// now here is logic explained below
left = *(--s.find(x));
right = *(++s.find(x));
mp[abs(left - x)]++;
mp[abs(right - x)]++;
mp[abs(left - right)]--;
if (mp[abs(left - right)] == 0)
mp.erase(abs(left - right));
}
// Function to remove an element from the list
void removeFromList(int x, multiset& s, map& mp)
{
ll left, right;
// Find left element of number that we want to delete.
left = *(--s.find(x));
// Find right element of number that we want to delete.
right = *(++s.find(x));
// remove x from set
s.erase(s.find(x));
// Again this will explain below in article.
mp[abs(left - x)]--;
if (mp[abs(left - x)] == 0)
mp.erase(abs(left - x));
mp[abs(right - x)]--;
if (mp[abs(right - x)] == 0)
mp.erase(abs(right - x));
mp[abs(left - right)]++;
}
// Driver code
int main()
{
int q = 4;
// define set to store all values.
multiset s;
// define map to store difference in sorted
map mp;
// initialize set with sentinel values
s.insert(-MOD);
s.insert(MOD);
// maintain freq of difference in map so, here intially
// difference b/w sentinel value and its freq is one.
mp[2 * MOD] = 1;
// 1st query 1 1 i.e, include 1 in our set
addToList(1, s, mp);
// As now we have only one element {-10^9+7, 1, 10^9+7}
// (except two are sentinel values)
// so minimum among all pair is zero
cout << 0 << endl;
// 2nd query 1 5 i.e, include 5 in our set.
addToList(5, s, mp);
// find smallest difference possible
// as it should be in beginning of map
cout << mp.begin()->first << endl;
// 3rd query 1 3 i.e, include 3 in our set.
addToList(3, s, mp);
// find smallest difference possible
// as it should be in beginning of map
cout << mp.begin()->first << endl;
// 4th query 2 3 i.e, remove 3 from our list
removeFromList(3, s, mp);
cout << mp.begin()->first << endl;
return 0;
}
0
4
2
4
每个查询的说明:
- addToList(1):现在我们只有一个元素{-10 ^ 9 + 7,1,10 ^ 9 + 7}(除了两个是前哨值),因此所有对中的最小值为零。
- addToList(5):在集合中插入1和5后,集合变为{-10 ^ 9 + 7,1,5,10 ^ 9 + 7}并映射:mp [5-1] = 1,其中key表示差异,它的值代表频率。
- addToList(3):在这里我们要插入3。因此,首先,我们在插入后在集合中找到3的左元素和右元素,即left = 1,right =5。由于3在1到5之间,所以我们删除了地图[5-1] –。 (因为3介于1和5之间,所以5-1将不再最小)并且我们包括了这些差异map [3-1] ++和map [5-3] ++(因为3介于1和5之间,所以可能的最小值有两种情况)。
- removeFromList(3):与上面的查询类似,这里我们要删除元素。因此,首先找到左元素和右元素为3,即left = 1,right =5。由于我们要删除1和5之间的3,因此最小差异仅会影响3和5,
before deleting ==>{1, 3, 5}, after deleting ==> {1, 5}. So, map[3-1]--, map[5-3]-- and add map[5-1]++.
如果您希望与行业专家一起参加现场课程,请参阅《 Geeks现场课程》和《 Geeks现场课程美国》。