按升序打印矢量的所有元素直到它为空而没有重复项的最有效方法是什么?

What's the most efficient way to print all elements of vector in ascending order till it's empty without duplicates?

本文关键字:是什么 方法 有效 打印 升序 元素      更新时间:2023-10-16

我应该:

  1. 打印矢量元素排序,不重复。
  2. 删除从矢量打印的元素。
  3. 重复前面的步骤,直到矢量为空。

但是似乎我的代码需要更多时间,因此,我寻求优化。我试图用std::vectorstd::set来完成这项任务。

这是我的方法:

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
int main () {
int n;
cin >> n;
vector<int> v(n);
set<int> st;
for (int i = 0; i < n; i++) {
cin >> v[i];
}
while (!v.empty()) {
for (int i = 0; i < v.size(); i++)
st.insert(v[i]);
for (auto x : st) {
cout << x << ' ';
auto it = find(v.begin(), v.end(), x);
if (it != v.end())
v.erase(it);
}
st.clear();
cout << "n";
}
return 0;
}

例如,输入如下:

7
1 2 3 3 2 4 3

输出将如下所示:

1 2 3 4 
2 3 
3

您可以使用std::map而不是std::vector/std::set来跟踪数字:

#include <iostream>
#include <map>
int main () {
map<int, int> m;
int size;
std::cin >> size;
for (int i = 0; i != size; i++) {
int number;
std::cin >> number;
++m[number];
}
while (!m.empty()) {
for (auto it = m.begin(); it != m.end(); /*Empty*/) {
const auto number = it->first;
auto& count = it->second;
std::cout << number << ' ';
if (--count == 0) {
it = m.erase(it);
} else {
++it;
}
}
std::cout << "n";
}
}

复杂性现在O(n log(n))而不是O(n²)(有大量内部分配(。

由于它覆盖了预期删除的元素,因此std::unique对此问题没有多大用处。我的解决方案:

std::sort(v.begin(), v.end());
while (!v.empty())
{
int last = v.front();
std::cout << last << " ";
v.erase(v.begin());
for (auto it = v.begin(); it != v.end(); /* no-op */)
{
if (*it == last)
{
++it;
}
else
{
last = *it;
std::cout << last << " ";
it = v.erase(it);
}
}
std::cout << std::endl;
}

您可以通过反转向量的排序,然后向后迭代(因为从靠近向量的背面删除更便宜(来进一步提高性能,但这会使代码进一步复杂化,所以我会说"留给读者练习"。

您可以使用std::map

auto n = 0;
std::cin >> n;
std::map<int, int> mp;
while (--n >= 0) {
auto i = 0;
std::cin >> i;
mp[i] += 1;
}
while (!mp.empty()) {
for (auto& it: mp) {
std::cout << it.first << " ";
it.second--;
}
for (auto it = mp.begin(); it != mp.end(); ++it) {
if (it->second == 0) mp.erase(it);
}
std::cout << "n";
}

没有任何擦除

auto n = 0;
std::cin >> n;
std::map<int, int> mp;
while (--n >= 0) {
auto i = 0;
std::cin >> i;
mp[i] += 1;
}
auto isDone = false;
while (!isDone) {
isDone = true;
for (auto& it: mp) {
if (it.second > 0) std::cout << it.first << " ";
if (--it.second > 0) isDone = false;
}
std::cout << "n";
}

这是使用sortvector的解决方案。 它使用第二个矢量来保存唯一项目并打印它们。

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> v{1,2,3,3,2,4,3};
std::sort(v.begin(), v.end());
std::vector<int>::iterator vit;
while(!v.empty()){
std::vector<int> printer;
std::vector<int>::iterator pit;
vit = v.begin();
while (vit != v.end()){
pit = find(printer.begin(), printer.end(), *vit);
if (pit == printer.end()){
printer.push_back(*vit);
vit = v.erase(vit);
} else {
++vit;
}
}
std::copy(printer.begin(), printer.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << 'n';
}
}

输出:

1 2 3 4
2 3
3

当你提到"效率"时,不清楚(至少对我来说(你在说什么。有些人用它来指代计算复杂性。其他人主要考虑程序员的时间,而另一些人则考虑整体执行速度,无论这是通过计算复杂性的变化获得的,还是(例如(改进的引用位置导致更好的缓存利用率。

所以,有了这个警告,我不确定这是否真的改善了你关心的东西,但无论如何,这就是我认为我会做这项工作的方式:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
// preconditions: input range is sorted
template <class BidiIt>
BidiIt partition_unique(BidiIt begin, BidiIt end) {
auto pivot = end;
for (auto pos = begin; pos != pivot; ++pos) {
auto mid = std::next(pos);
for ( ; mid < pivot && *mid == *pos; ++mid, --pivot)
;
std::rotate(std::next(pos), mid, end);
}
return pivot;
}
template <class It>
void show(It b, It e, std::ostream &os) {
while (b != e) {
os << *b << ' ';
++b;
}
os << 'n';
}
int main() {
std::vector<int> input{ 1, 2, 3, 3, 2, 4, 3 };
std::sort(input.begin(), input.end());
auto begin = input.begin();
auto pos = begin;
while ((pos = partition_unique(begin, input.end())) != input.end()) {
show(begin, pos, std::cout);
begin = pos;
}
show(begin, input.end(), std::cout);
}

我真的不确定是否有可能提高计算复杂性(但它可能是 - 我还没有考虑足够确定一种或另一种方式(。与我已经看到的一些版本相比,这很有可能提高整体速度(例如,由于它只是在同一向量内移动东西,因此它可能比将数据从一个向量复制到另一个向量。

代码是用java编写的,但想法保持不变。

  • 首先,我对数组进行排序。现在,我们的想法是创建存储桶。
  • 这意味着每行排序的元素就像一个桶。因此,找到每个元素的计数。现在,把这个element放进each bucketcount次数。如果碰巧存储桶大小较小,请创建一个新存储桶并向其中添加当前元素。

  • 最后,打印所有存储桶。

  • 时间复杂度对于存储桶的排序和O(n)O(nlog(n)),因为您必须访问每个元素才能打印它。所以,它是渐近O(nlog(n)) + O(n) = O(nlog(n))的。

法典:

import java.util.*;
public class GFG {
public static void main(String[] args){
int[] arr1 = {1,2,3,3,2,4,3};
int[] arr2 = {45,98,65,32,65,74865};
int[] arr3 = {100,100,100,100,100};
int[] arr4 = {100,200,300,400,500};
printSeries(compute(arr1,arr1.length));
printSeries(compute(arr2,arr2.length));
printSeries(compute(arr3,arr3.length));
printSeries(compute(arr4,arr4.length));
}
private static void printSeries(List<List<Integer>> res){
int size = res.size();
for(int i=0;i<size;++i){
System.out.println(res.get(i).toString());
}
}
private static List<List<Integer>> compute(int[] arr,int N){
List<List<Integer>> buckets = new ArrayList<List<Integer>>();
Arrays.sort(arr);
int bucket_size = 0;
for(int i=0;i<N;++i){
int last_index = i;
if(bucket_size > 0){
buckets.get(0).add(arr[i]);
}else{
buckets.add(newBucket(arr[i]));
bucket_size++;
}
for(int j=i+1;j<N;++j){
if(arr[i] != arr[j]) break;
if(j-i < bucket_size){
buckets.get(j-i).add(arr[i]);
}else{
buckets.add(newBucket(arr[i]));
bucket_size++;
}
last_index = j;
}
i = last_index;   
}
return buckets;
}
private static List<Integer> newBucket(int value){
List<Integer> new_bucket = new ArrayList<>();
new_bucket.add(value);
return new_bucket;
}
}

输出

[1, 2, 3, 4]
[2, 3]
[3]
[32, 45, 65, 98, 74865]
[65]
[100]
[100]
[100]
[100]
[100]
[100, 200, 300, 400, 500]

这就是我想出的: http://coliru.stacked-crooked.com/a/b3f06693a74193e5

关键思想:

  1. 排序向量
  2. 通过迭代打印。如果值与上次打印的值不同,只需打印该值
  3. 删除唯一元素。 我已经用我称之为inverse_unique做到了这一点。 STD 库带有一种称为 Unique 的算法,它将删除所有重复项。 我颠倒了这个,以便它只保留所有都柏林人。

所以我们根本没有内存分配。 我看不出如何使算法更有效率。 我们只是在做最低限度的事情,它完全按照人类的思维方式完成。

我用几种组合测试了它。 希望它没有错误;-P

法典:

#include <iostream>
#include <algorithm>
#include <vector>
template<class ForwardIt>
ForwardIt inverse_unique(ForwardIt first, ForwardIt last)
{
if (first == last)
return last;
auto one_ahead = first+1; 
auto dst = first;
while(one_ahead != last)
{
if(*first == *one_ahead)
{
*dst = std::move(*first);
++dst;
}
++first;
++one_ahead;
}
return dst;
}
void print_unique(std::vector<int> const& v)
{
if(v.empty()) return;
// print first
std::cout << v[0] << ' ';
auto last_printed = v.cbegin();            
// print others
for(auto it = std::next(std::cbegin(v)); it != std::cend(v); ++it)
{
if(*it != *last_printed)
{
std::cout << *it << ' ';
last_printed = it;            
}
}
std::cout << "n";
}
void remove_uniques(std::vector<int> & v)
{
auto new_end = inverse_unique(std::begin(v), std::end(v));
v.erase(new_end, v.end()); 
}
int main () 
{
std::vector<int> v = {1, 2, 3, 3, 2, 4, 3};
std::sort(std::begin(v), std::end(v));
while (!v.empty()) 
{
print_unique(v);
remove_uniques(v);
}
return 0;
}

编辑:更新inverse_unique功能。 现在应该很容易理解。

半生不熟 http://coliru.stacked-crooked.com/a/c45df1591d967075

略微修改了计数排序。

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <map>
int main() {
std::vector<int> v{1,2,3,3,2,4,3};
std::map<int, int> map;
for (auto x : v)
++map[x];
while(map.size()) {
for(auto pair = map.begin(); pair != map.end(); ) {
std::cout << pair->first << ' ';
if (!--pair->second)
pair = map.erase(pair);
else
++pair;
}
std::cout << "n";
}
return 0;
}