如何在另一个中找到一个未排序数组的元素的不同索引?

时间:2018-02-06 22:08:56

标签: c++ arrays time-complexity

有2个字符数组。两个阵列都具有相同的大小和互相混杂的形式。

例如:

char a[] = {'a', 'b', 'a', 'b', 'c', 'a', 'b', 'a', 'b', 'c' };
char b[] = {'a', 'a', 'b', 'b', 'c', 'c', 'b', 'b', 'a', 'a' };

我想在数组 a 中找到数组 b 元素的不同位置(从1开始索引),即:1,3,2,4, 5,10,7,9,6,8,对于这个例子。

我实施了以下蛮力方法 O n 2 ):

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < n; j++)
    {
        if (b[i] == a[j])
        {
            cout << j + 1 << " ";
            a[j] = '\0';
            break;
        }
    }
}

C ++中是否有任何方法可以将时间复杂度降低到 O n * log( n ))甚至更少?

4 个答案:

答案 0 :(得分:2)

它可以以O(n)存储器的代价减少到O(n)时间。

你应该创建二维数组。它的第一个维度将是所有字符(总共256 = 2 ^ 8个索引,因为sizeof(char)= 1个字节),第二个维度将超过你数组的n个元素。

所以,如果你有

char a[n] = ...;
char b[n] = ...;

你应该分配

int c[256][n]; // O(n) memory
int s[256]; // O(1) memory
int e[256]; // O(1) memory

并用零填充它们。您可以使用e [i]作为 a 中代码 i 的字符数的计数器。在c [i] [0],c [i] [1],...中,您可以在 a 数组中存储代码 i 的字符的实际位置。

第一步是迭代数组 a ,每次

  1. 将字符的位置写入c [a [i]] [m] = i,其中m = e [a [i]]
  2. 增加e [a [i]]
  3. 您可以使用数组 s 来存储已打印的符号位置数(s [j]是代码 j 的打印字符数)。第二步是迭代 b ,每次

    1. 输出c [b [i]] [m],其中m = s [b [i]]
    2. 增加s [b [i]]
    3. 每个步骤消耗O(n)时间,因此总时间复杂度为O(n)。值得注意的是,使用随机方法(如哈希表,你必须考虑命中概率)这种复杂性不是意味着的情况。 最差情况下,这种复杂性将是相同的。

答案 1 :(得分:1)

您可以简单地将索引插入到由元素值键入的多图中,然后迭代另一个数组,找到您需要的第一个索引,然后将其从地图中删除。应该是O(n log n):

std::multimap<char, int> charmap;
for (unsigned i = 0; i < sizeof a; i++) {
    charmap.emplace(a[i], i);
}


for (char c : b) {
    auto it = charmap.find(c);
    std::cout << it->second + 1 << " ";
    charmap.erase(it);
}

答案 2 :(得分:0)

您可以拥有一个哈希表,并且在每个存储桶中都有一个列表。该列表将包含b []的索引,在该索引处出现该字符。

取b []中的每个元素,比如b [i],找到散列表中的索引等于b [i]的列表(在你的情况下将是'a'或'b'或'c' )。如果没有列表,请创建值为i + 1的列表。如果找到列表,请将i + 1添加到列表的末尾

在您的示例中,插入完成后

  • 'a'将列于1,2,9,10
  • 'b'将列为3,4,7,8
  • 'c'将列为5,6

当您处理条目时,使用列表中的第一个元素并从列表中删除第一个元素。  在上面的例子中,

处理完第一个条目'a'后 - 您将给出1,并从索引'a'的列表中删除1。现在

  • 'a'将列为2,9,10
  • 'b'将列为3,4,7,8
  • 'c'将列为5,6

处理完第二个条目'b'后,你将给出3并从索引'b'的列表中删除3。现在

  • 'a'将列为2,9,10
  • 'b'将列为清单4,7,8
  • 'c'将列为5,6

这是O(N)? (假设你有双重链表)

答案 3 :(得分:0)

修改 我更喜欢@ user2079303的答案。相同的概念,更容易实现。

反向查找的具体实现。您将看到前两个循环是开销。 unordered_map基本上是&#34;哈希表&#34;在另一个答案中建议。

auto it是一个迭代器,first是索引,second是值(在我们的例子中是queue)。令人困惑的是,作为反向查找,&#34;索引&#34;也是你考虑的价值(反之亦然,每个&#34;值&#34;在队列上是a的索引。

我使用了一个队列,因为您只想使用每个索引一次(因此pop相当于将值设置为\0)。

我认为理解它的最好方法是在调试器中逐步完成它。

#include <unordered_map>
#include <queue>
#include <memory>
#include <iostream>

using namespace std;
int main()
{
  char a[] = { 'a', 'b', 'a', 'b', 'c', 'a', 'b', 'a', 'b', 'c' };
  char b[] = { 'a', 'a', 'b', 'b', 'c', 'c', 'b', 'b', 'a', 'a' };
  unordered_map<char, shared_ptr<queue<int>>> reverseIndex;
  //create a queue for each unique char
  for (int i = 0; i < 10; ++i)
  {
    auto it = reverseIndex.find(a[i]);
    if (it == reverseIndex.end())
    {
      reverseIndex.emplace(a[i], make_shared<queue<int>>());
    }
  }
  //put the indexes as *values* into our unordered_map vectors
  for (int i = 0; i < 10; ++i)
  {
    auto it = reverseIndex.find(a[i]);
    it->second->push(i);
  }

  //perform the actual work
  for (int i = 0; i < 10; ++i)
  {
    auto it = reverseIndex.find(b[i]);
    cout << it->second->front()+1 << "\n";
  //you'll need to push the value back onto the queue for multiple uses of reverseIndex:  it->second->push(it->second->front());
    it->second->pop();
  }
    return 0;
}