【问题标题】:Find words that are unique for 1 set out of 3 C++查找 3 个 C++ 中的 1 个集合唯一的单词
【发布时间】:2020-03-13 13:23:46
【问题描述】:

我有 3 个包含单词的集合。

a: car, boat, table, ball

b: car, goat, helicopter

c: square, car, goat, boat

我需要创建一个向量或集合,其中包含仅包含在集合 a 中的单词。

所以答案是:

result: table, ball

我尝试使用 set_difference 和 set_intersection 来实现它,但到目前为止没有运气。你能给我一些建议吗?

我试过了

set_difference(a.begin(), a.end(), b.begin(), b.end(), res.begin()); 
set_difference(res.begin(), res.end(), c.begin(), c.end(), res.begin());

但是结果是空的

【问题讨论】:

  • std::set_difference 是此作业的标准工具。您可以使用它显示您的代码,我们可以帮助您修复它吗?
  • 您在使用std::set_difference之前是否对数组进行了排序?
  • 实际上我无法理解如何将 3 套而不是 2 套放入std::set_difference
  • 你没有。您对ab 运行差异创建输出d。然后你对 dc 运行差异创建你的结果集。
  • 第二行写错了。您在迭代 res 时正在修改它。您应该使用final_res 变量来存储第二个结果。

标签: c++ algorithm stl


【解决方案1】:

你的错误在这里:

set_difference(res.begin(), res.end(), c.begin(), c.end(), res.begin());
//             ^            ^                              ^

您遍历res 并将结果写入同一个集合中。您需要另一组来存储结果。

解决方案是:

std::set<std::string> a {"car", "boat", "table", "ball"};
std::set<std::string> b {"car", "goat", "helicopter"};
std::set<std::string> c {"square", "car", "goat", "boat"};

std::set<std::string> tmp;
std::set<std::string> res;

// Difference between a and b --> stored in tmp
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(tmp, tmp.begin()));

// Difference between tmp and c --> stored in res
std::set_difference(tmp.begin(), tmp.end(), c.begin(), c.end(), std::inserter(res, res.begin()));

for(const std::string & s : res)
    std::cout << s << '\n';

输出:


Live example


注意:如果我们查看std::set_difference的文档,我们可以看到:

将排序范围 [first1, last1) 中未在排序范围 [first2, last2) 中找到的元素复制到从 d_first 开始的范围内。

结果范围也被排序。 等价元素被单独处理,即如果某个元素在[first1,last1)中找到m次,在[first2,last2)中找到n次,则将其准确复制到d_first std::max(m-n, 0 ) 次。结果范围不能与任何一个输入范围重叠。

强调我的

所以如果你想使用另一个不保证其元素唯一性的容器(例如std::vector),你需要自己确保每个元素不会在你的容器中出现多次。


注意2:如果你不想打扰tmp set(拿到res set后就没用了),你可以把它放在一个bloc中,这样它之后会被销毁:

std::set<std::string> res;

{
    std::set<std::string> tmp;
    std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(tmp, tmp.begin()));
    std::set_difference(tmp.begin(), tmp.end(), c.begin(), c.end(), std::inserter(res, res.begin()));
} // tmp destroyed here

Live example

【讨论】:

    【解决方案2】:

    如果不共享您的代码,我们只能猜测您的代码做错了什么。

    这就是我所做的。我将差异逻辑包装在一个助手 operator- 中。我故意使用std::unordered_set,因为它们不能直接在std::set_difference中使用。

    #include <algorithm>
    #include <iostream>
    #include <iterator>
    #include <string>
    #include <unordered_set>
    #include <vector>
    
    using std::cout;
    using std::inserter;
    using std::ostream;
    using std::set_difference;
    using std::sort;
    using std::string;
    using std::unordered_set;
    using std::vector;
    
    namespace {
    
    unordered_set<string> operator-(unordered_set<string> const& minuend, unordered_set<string> const& subtrahend) {
        vector<string> m(minuend.begin(), minuend.end());
        vector<string> s(subtrahend.begin(), subtrahend.end());
        sort(m.begin(), m.end());
        sort(s.begin(), s.end());
        unordered_set<string> diff;
        set_difference(m.begin(), m.end(), s.begin(), s.end(), inserter(diff, diff.begin()));
        return diff;
    }
    
    ostream& operator<<(ostream& out, unordered_set<string> const& container) {
        char const* sep = " ";
        out << "{";
    
        for (auto const& s : container) {
            out << sep << "\"" << s << "\"";
            sep = ", ";
        }
        out << " }";
        return out;
    }
    
    }
    
    int main() {
        auto a = unordered_set<string>{ "car", "boat", "table", "ball" };
        auto b = unordered_set<string>{ "car", "goat", "helicopter" };
        auto c = unordered_set<string>{ "square", "car", "goat", "boat" };
        auto d = a - b - c;
        cout << d << "\n";
    }
    

    更新回答 Fareanor 的问题

    你为什么使用 std::unordered_set(而不是 std::set)?

    我选择了 unordered_set 来证明 set_difference 需要一个有序的容器。 unordered_set 缺少该功能。

    原始海报,在原始未经编辑的问题中,没有提供使用哪种容器的详细信息。

    为什么要将它转换成需要排序的 std::vector(而不是转换成 std​​::set)?

    向量是一个非常有效的容器,因为其中的元素具有局部性,因此具有良好的缓存。这是我的首选容器。

    一个集合有更多的内存分配,因为它是一个节点网格,并且缺乏局部性。

    无论如何,包含的字符串对象可能缺少局部性,因为它基本上是一个指向字符数组的智能指针。但由于小字符串优化 (SSO) 和这些都是小字符串,它也不会在堆外分配。

    在原发帖人的场景中,每个容器中只有几件物品,因此效率问题可以忽略不计。但值得考虑的是,如果问题域扩大了。

    我认为你应该使用 std::set (至少没有来自 OP 的任何澄清),如果用户得到一个 std::unordered_set ,则由他将其转换为适当的 std ::set 然后打电话给你的operator-()

    这是一个可行的选择。由于当时缺乏上下文,我认为这是“更糟糕的情况”,因为 unordered_set 容器不满足 set_difference 算法的要求。

    【讨论】:

    • 我喜欢你的想法,它非常优雅。但是有几件事我不明白:你为什么使用std::unordered_set(而不是std::set)?为什么将其转换为需要排序的std::vector(而不是转换为std::set)?我认为您应该使用std::set(至少没有任何来自OP的澄清),如果用户得到std::unordered_set,则由他将其转换为正确的std::set,然后致电您的@987654333 @.
    猜你喜欢
    • 2017-04-03
    • 2019-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多