如何将数组中的元素划分为最小数量的数组,以使每个形成的数组的元素值之差相差不超过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;
}
答案 0 :(得分:4)
受法鲁克答案的启发,如果将值限制为不同的整数,则可能存在亚线性方法。
实际上,如果两个值之间的差等于它们的索引之间的差,则可以保证它们属于同一组,并且无需查看中间值。
您必须按顺序组织数组的递归遍历。在细分子数组之前,您将第一个元素和最后一个元素的索引差与值的差进行比较,并且仅在发生不匹配的情况下才进行细分。当您按顺序工作时,这将允许您以连续的顺序发出组的片段,并检测间隙。必须注意合并各个组。
最坏的情况将保持线性,因为递归遍历可以退化为线性遍历(但不会比线性遍历更糟)。最好的情况可能会更好。特别是,如果数组包含单个组,则会在时间O(1)中找到它。如果我是对的,那么对于长度在2 ^ n和2 ^(n + 1)之间的每个组,您将至少保留2 ^(n-1)个测试。 (实际上,应该可以估算出对输出敏感的复杂度,等于数组长度减去所有组长度的一部分或类似的值。)
或者,您可以通过指数搜索以非递归方式进行工作:从组的开始,以单位步长开始,每次都加倍,直到检测到差距(值差异)太大了);然后以一个单元步骤重新启动。同样,对于大型组,您将跳过大量元素。无论如何,最好的情况只能是O(Log(N))。
答案 1 :(得分:2)
我建议将子集编码为如下定义的偏移数组:
这仅需要分配一次内存。
这是一个完整的实现:
#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))
时间。