【问题标题】:How to decide if an array is a permutation of 1-N in O(log N)?如何确定一个数组是否是 O(log N) 中 1-N 的排列?
【发布时间】:2020-04-03 16:13:19
【问题描述】:

长度为 n 的序列如果包含从 1 到 n 的所有整数恰好一次,则称为置换。

你能判断一个数组是否是 O(log N) 的排列吗?

【问题讨论】:

  • 欢迎来到 Stack Overflow。请阅读the help pages,接受SO tour,阅读How to Ask,以及this question checklist。还请了解如何创建您自己的尝试的minimal reproducible example 和edit 您的问题以显示它,以及您遇到的问题的描述。最后,请实际上作为问题中的一个问题(并且标题是问题/问题的简短摘要)。
  • 您的意思是“如何判断 if 数组 is 排列”吗?
  • 我的第一个想法是,考虑到你必须至少观察每个数组元素一次,它应该很难击败 O(N)。但是,也许,我从错误的角度看待这一点。我很好奇这将如何结束。 ;-)
  • @DanielChristianMandolang:我编辑了您的问题正文以包含包含实际问题的标题。如果这不是您的意思,请自行编辑并澄清。
  • 您确定O(log N) 吗?如果它要求O(N log N),这将是一个很好的练习,因为那样你可以尝试不同的事情只是为了意识到排序就是你所需要的,但是检查小于O(N)的N个数字的属性似乎是不可能的

标签: c++ permutation


【解决方案1】:

你的意思是说,一个数组是否包含一个排列? O(log N) 是不够的:您需要 O(N) 才能读取所有元素。 O(N*log N) 足以对数组进行排序,然后判断它是否是 O(N) 中的排列是微不足道的。

您可以在每次写入数组期间更新直方图,还可以更新一个计数器,即有多少直方图条目恰好为 1。每次更新的成本为 O(1),实际测试的成本为 O(1)。

constexpr int N = 1000;
std::array<int, N> arr = {};      // zero-init
std::array<int, N + 1> hist = {}; // zero-init, we ignore element 0 and use elements 1..N
int counter = 0;

// writes  arr[i] = newv  in O(1)
void write(int i, int newv) {
    if(i < 0 || i > N-1)          // invalid index
        return;

    const int oldv = arr[i];      // read previous array entry

    if(oldv > 0 && oldv <= N) {   // decrease histogram
        if(hist[oldv] == 1)
            --counter;
        --hist[oldv];
        if(hist[oldv] == 1)
            ++counter;
    }

    arr[i] = newv;                // set array

    if(newv > 0 && newv <= N) {   // increase histogram
        if(hist[newv] == 1)
            --counter;
        ++hist[newv];
        if(hist[newv] == 1)
            ++counter;
    }
}

// tests for permutation in O(1)
bool testPermutation() {
    return counter == N;
}

【讨论】:

  • 我会考虑你的直方图方法 O(N)。
  • O(N) 如果你事后做,那是对的,O(1) 如果你在每次写的时候都做。
  • 从数组是一个排列的假设开始,你甚至可以在线性时间内对它进行排序,因为你只需要将每个元素交换到它的目标位置。一旦你发现一个越界元素或者你想用一个相等的元素交换一个元素,你就知道这不是一个排列。
  • O(1) 进行实际测试?我不明白直方图对这种情况有什么变化,您仍然需要检查直方图的每个 N 箱,这使其与以前的 O(N) 相同
  • 任务是在 O(log N) 内完成。因此,您需要一个增强的数组,它不必查看每个元素来测试是否包含排列。每次写入操作都会更新计数器,即有多少直方图 bin 为 1。实际测试只是读取计数器并与 N 进行比较。
【解决方案2】:

如果不查看数组的每个条目,您就无法判断一个数组是否是一个排列,因此您的算法中至少需要n 个步骤。

一个简单的线性时间解决方案是尝试计算逆排列(假设基于 0 的索引):

std::vector<int> inverse(n, -1);

for (int i = 0; i < n; ++i) {
    if (array[i] < 0 || array[i] >= n || inverse[array[i]] != -1) {
        break; // not a permutation!
    }
    inverse[array[i]] = i;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多