java 一组数的最小绝对差

osh3o9ms  于 2022-10-30  发布在  Java
关注(0)|答案(1)|浏览(151)

给定一个大小为n的数字集合,以及一个Q输入列表,每个输入将在该集合中删除或添加一个数字,每次输入后,我都要输出该集合中数字的最小绝对差。
限制条件:
2 <= N <= 10^6
1 <= Q <= 10^6
-10^9 <= set[i] <= 10^9
示例:
输入:

set = [2, 4, 7]
ADD 6
REMOVE 7
REMOVE 4
ADD 2

输出:

1
2
4
0

我的任务是使用时间复杂度为**O((N+Q)log(N+Q))**或更好的算法来解决这个问题。
我当前的实现不够快,但如下所示:

TreeSet<Integer> tree = new TreeSet<>();
HashMap<Integer, Integer> numberFreq = new HashMap<>();

int dupeCount = 0;

for (int i : set) {
    tree.add(i);
    if (numberFreq.get(i) > 0) dupeCount++;
    numberFreq.put(i, numberFreq.getOrDefault(i, 0) + 1);
}

void add(int i) {
    if (numberFreq.get(i) > 0) dupeCount++;
    numberFreq.put(i, numberFreq.getOrDefault(i, 0) + 1);
    tree.add(i); // if duplicate nothing gets added anyway

    if (dupeCount > 0) Console.write(0);
    else {
        int smallestBall = ballTree.first();
        int absDiff;
        int maxAbsDiff = Integer.MAX_VALUE;
        while (ballTree.higher(smallestBall) != null) {
            absDiff = Math.abs(smallestBall - ballTree.higher(smallestBall));
            maxAbsDiff = Math.min(absDiff, maxAbsDiff);
            smallestBall = ballTree.higher(smallestBall);
        }
    Console.write(maxAbsDiff);
    }

}

void remove(int i) {
     if (numberFreq.get(i) > 0) dupeCount--;
     else tree.remove(i);
     numberFreq.put(i, numberFreq.get(i) - 1);

     if (dupeCount > 0) Console.write(0);
     else {
         int smallestBall = ballTree.first();
         int absDiff;
         int maxAbsDiff = Integer.MAX_VALUE;
         while (ballTree.higher(smallestBall) != null) {
             absDiff = Math.abs(smallestBall - ballTree.higher(smallestBall));
             maxAbsDiff = Math.min(absDiff, maxAbsDiff);
             smallestBall = ballTree.higher(smallestBall);
        }
     Console.write(maxAbsDiff);
     }
}

我已经试了两天了,我完全迷路了。

chhkpiq4

chhkpiq41#

下面是一个应该可以工作的算法(虽然我不知道这是否是预期的算法):
1.对数字列表进行排序L(如果尚未排序):L = [2, 4, 7]
1.构建“排序的相邻绝对差”的对应列表D(即,排序列表中的相邻对之间的差,以升序排序它们自己):D = [2, 3]
1.对于每个运算......假设运算是ADD 6,作为示例。a)将数字(6)插入L中的正确(排序)位置:L = [2, 4, 6, 7]; B)基于插入它的位置,确定现在废弃的对应相邻绝对差,并将其从D中移除(在这种情况下,差7-4=3不再相关,并且可以移除,因为4和7不再相邻,6将它们分开):D = [2]; c)将两个新的相邻绝对差加到正确的(排序的)位置(在这种情况下,6-4=2和7-6=1):D = [1, 2, 2]; d)打印出D的第一个元素
如果在第3步中遇到remove操作,逻辑是类似的,但略有不同:从L中找到并删除元素,从D中删除两个相邻的差异,这两个差异由于remove操作而变得无关紧要,将新的相关相邻差异添加到D中,并打印D的第一个元素。
正确性的证明是直接的。最小相邻绝对差肯定也是最小绝对差,因为两个不相邻的数字之间的绝对差总是大于或等于两个相邻数字之间的绝对差,这两个数字在排序顺序中位于“它们之间”。该算法在每次运算后输出最小相邻绝对差。
排序列表数据结构有几种选择,但由于您希望能够快速插入、删除和读取排序数据,我建议使用自平衡二叉树,假设我们使用AVL树。
第一步是O(Nlog(N)),如果输入是一个数组或者其他的东西,你可以直接构建一个AVL树;在AVL树中的插入是log(N),并且你必须这样做N次来构建树。
时间复杂度为O(N);您只需要以升序遍历L的AVL树,计算相邻的差值,并将每个差值插入D的新AVL树(同样,N次插入,每次的复杂度为log(N))。
对于单个操作,步骤3a)、3b)、3c)和3d)都是O(log(N+Q)),因为它们每个都涉及插入、删除或阅读大小〈N+Q的AVL树中的一个或两个元素。因此对于单个操作,步骤3是O(log(N+Q))。步骤3在Q个操作上重复此操作,得到O(Q log(N+Q))。
所以最终的算法运行时复杂度是O(N log(N))+ O(Q log(N+Q)),它小于O((N+Q)log(N+Q))。

编辑:

我刚刚意识到“数字列表”(L)实际上是一个集合(至少,它是根据问题标题的,但这可能会引起误解)。集合不允许重复。无论何时插入,只要检查它是否是重复的(在确定插入位置之后)。如果是重复的,整个操作就变成了无操作。这并不会改变复杂性。尽管我认为这是TreeSet所做的。

相关问题