将已排序数组的元素划分为最少数量的组,以使新数组的元素之间的差小于或等于1

时间:2019-07-06 16:02:30

标签: c++ algorithm c++14

如何将数组中的元素划分为最小数量的数组,以使每个形成的数组的元素值之差相差不超过1?

假设我们有一个数组:[4, 6, 8, 9, 10, 11, 14, 16, 17]。 数组元素已排序。

我想将数组的元素划分为最少数量的数组,以使结果数组中的每个元素的相差不超过1。

在这种情况下,分组为:[4], [6], [8, 9, 10, 11], [14], [16, 17]。因此总共将有 5 个组。

我该如何编写相同的程序?或者您也可以建议算法。

我尝试过幼稚的方法: 获得数组的连续元素之间的差,如果差小于(或等于)1,则将这些元素添加到新向量中。但是,此方法非常未优化,并且对于大量输入,直接向上无法显示任何结果。

实际代码实现:

#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;

int main() {

    int num = 0, buff = 0, min_groups = 1;    // min_groups should start from 1 to take into account the grouping of the starting array element(s)
    cout << "Enter the number of elements in the array: " << endl;
    cin >> num;

    vector<int> ungrouped;
    cout << "Please enter the elements of the array: " << endl;
    for (int i = 0; i < num; i++)
    {
        cin >> buff;
        ungrouped.push_back(buff);
    }

    for (int i = 1; i < ungrouped.size(); i++)
    {
        if ((ungrouped[i] - ungrouped[i - 1]) > 1)
        {
            min_groups++;
        }
    }

    cout << "The elements of entered vector can be split into " << min_groups << " groups." << endl;

    return 0;
}

5 个答案:

答案 0 :(得分:4)

受法鲁克答案的启发,如果将值限制为不同的整数,则可能存在亚线性方法。

实际上,如果两个值之间的差等于它们的索引之间的差,则可以保证它们属于同一组,并且无需查看中间值。

您必须按顺序组织数组的递归遍历。在细分子数组之前,您将第一个元素和最后一个元素的索引差与值的差进行比较,并且仅在发生不匹配的情况下才进行细分。当您按顺序工作时,这将允许您以连续的顺序发出组的片段,并检测间隙。必须注意合并各个组。

最坏的情况将保持线性,因为递归遍历可以退化为线性遍历(但不会比线性遍历更糟)。最好的情况可能会更好。特别是,如果数组包含单个组,则会在时间O(1)中找到它。如果我是对的,那么对于长度在2 ^ n和2 ^(n + 1)之间的每个组,您将至少保留2 ^(n-1)个测试。 (实际上,应该可以估算出对输出敏感的复杂度,等于数组长度减去所有组长度的一部分或类似的值。)


或者,您可以通过指数搜索以非递归方式进行工作:从组的开始,以单位步长开始,每次都加倍,直到检测到差距(值差异)太大了);然后以一个单元步骤重新启动。同样,对于大型组,您将跳过大量元素。无论如何,最好的情况只能是O(Log(N))。

答案 1 :(得分:2)

我建议将子集编码为如下定义的偏移数组:

  • 为索引j定义了集合#i的元素,使得offset [i] <= j
  • 子集的数量为offset.size()-1

这仅需要分配一次内存。

这是一个完整的实现:

#include <cassert>
#include <iostream>
#include <vector>

std::vector<std::size_t> split(const std::vector<int>& to_split, const int max_dist = 1)
{
  const std::size_t to_split_size = to_split.size();
  std::vector<std::size_t> offset(to_split_size + 1);
  offset[0] = 0;
  size_t offset_idx = 1;
  for (std::size_t i = 1; i < to_split_size; i++)
  {
    const int dist = to_split[i] - to_split[i - 1];
    assert(dist >= 0);  // we assumed sorted input
    if (dist > max_dist)
    {
      offset[offset_idx] = i;
      ++offset_idx;
    }
  }
  offset[offset_idx] = to_split_size;
  offset.resize(offset_idx + 1);
  return offset;
}

void print_partition(const std::vector<int>& to_split, const std::vector<std::size_t>& offset)
{
  const std::size_t offset_size = offset.size();
  std::cout << "\nwe found " << offset_size-1 << " sets";
  for (std::size_t i = 0; i + 1 < offset_size; i++)
  {
    std::cout << "\n";
    for (std::size_t j = offset[i]; j < offset[i + 1]; j++)
    {
      std::cout << to_split[j] << " ";
    }
  }
}

int main()
{
  std::vector<int> to_split{4, 6, 8, 9, 10, 11, 14, 16, 17};
  std::vector<std::size_t> offset = split(to_split);
  print_partition(to_split, offset);
}

打印:

we found 5 sets
4 
6 
8 9 10 11 
14 
16 17 

答案 2 :(得分:1)

遍历数组。每当2个连续元素之间的差大于1时,将1添加到您的答案变量中。

`

int getPartitionNumber(int arr[]) {
    //let n be the size of the array;
    int result = 1;
    for(int i=1; i<n; i++) {
        if(arr[i]-arr[i-1] > 1) result++;
    }
    return result;
}

`

答案 3 :(得分:1)

因为总是很高兴看到更多的想法并选择最适合您的想法,因此这里提供了直接的6行解决方案。是的,它也是O(n)。但是我不确定,其他方法的开销是否会使它更快。

请参阅:

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>

using Data = std::vector<int>;
using Partition = std::vector<Data>;

Data testData{ 4, 6, 8, 9, 10, 11, 14, 16, 17 };

int main(void)
{
    // This is the resulting vector of vectors with the partitions
    std::vector<std::vector<int>> partition{};  
    // Iterating over source values
    for (Data::iterator i = testData.begin(); i != testData.end(); ++i) {
        // Check,if we need to add a new partition
        // Either, at the beginning or if diff > 1
        // No underflow, becuase of boolean shortcut evaluation
        if ((i == testData.begin()) || ((*i) - (*(i-1)) > 1)) {
            // Create a new partition
            partition.emplace_back(Data());
        }
        // And, store the value in the current partition
        partition.back().push_back(*i);
    }

    // Debug output:  Copy all data to std::cout
    std::for_each(partition.begin(), partition.end(), [](const Data& d) {std::copy(d.begin(), d.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; });

    return 0;
}

也许这可以解决。 。

答案 4 :(得分:-1)

您怎么说您的方法没有优化?如果您是正确的,则根据您的方法,这将花费O(n)的时间复杂度。

但是您可以在此处使用二进制搜索,该搜索可以在一般情况下进行优化。但是在最坏的情况下,这种二进制搜索所花费的时间可能超过O(n)

这里有个提示, 在对数组进行排序时,您将选择一个差异最大为 1 的位置。

二进制搜索可以通过简单的方式做到这一点。

int arr[] = [4, 6, 8, 9, 10, 11, 14, 16, 17];

int st = 0, ed = n-1; // n = size of the array.
int partitions = 0;
while(st <= ed) {
    int low = st, high = n-1;
    int pos = low;
    while(low <= high) {
        int mid = (low + high)/2;
        if((arr[mid] - arr[st]) <= 1) {
            pos = mid;
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    partitions++;
    st = pos + 1;
}
cout<< partitions <<endl;

通常情况下,它比O(n)好。但是在最坏的情况下(答案等于 n )需要O(nlog(n))时间。