【问题标题】:How to implement a sorting comparator which cascades the related objects?如何实现级联相关对象的排序比较器?
【发布时间】:2017-07-09 16:18:35
【问题描述】:

假设,必须对以下类型的连续数组进行排序:

struct X
{
  string id, parent_id;
  int value;

  bool operator< (const X& x) const { return value < x.value; }
};

使用前面提到的operator&lt;,它创建以下排序数组:

{i, p,  v}
----------
{a, "", 1}
{b, "", 2}
{c, "", 3}
{dc, c, 4}
{ea, a, 5}
{fb, b, 6}

编写比较器的最佳方法是什么,以便它创建以下排序数组:

{i, p,  v}
----------
{c, "", 3}  // grouping of 'c'
{dc, c, 4}
{a, "", 1}  // grouping of 'a'
{ea, a, 5}
{b, "", 2}  // grouping of 'b'
{fb, b, 6}

如您所见,数组是专门排序的,其中parent_id 创建了一个分组,然后根据从最低到最高的值排列数组。换句话说,最新的依赖对象(那些X,具有非空parent_id)对象是关键参与者。剩下的父母被拉到他们身边。

我的努力:这样做的自然方法是:

  1. 使用上述比较器执行排序
  2. 从底部/反向迭代,即最高value
  3. 为元素x寻找parent_id;如果有效,则:
    • 搜索parent_id,复制并删除该元素
    • x上方插入
  4. 递归执行步骤 3,直到找不到parent_id

问题:能以更简单的方式实现吗?

注意:此问题并非特定于 C++。

【问题讨论】:

    标签: c++ algorithm sorting grouping comparator


    【解决方案1】:

    您当前的比较器可能如下所示:

    int cmp = a.string_id.compare(b.string_id);
    if(cmp == 0) {
       cmp = a.parent_id.compare(b.parent_id);
       if(cmp == 0) {
          cmp = a.value - b.value;
       }
    }
    return cmp < 0;
    

    您可以通过使用稍微不同的比较器来实现所需的排序:

    auto a_effective_parent_id = a.parent_id.empty() ? a.string_id : a.parent_id;
    auto b_effective_parent_id = b.parent_id.empty() ? b.string_id : b.parent_id;
    
    // compare first by 'effective' parent id
    int cmp = a_effective_parent_id.compare(b_effective_parent_id);
    if(cmp == 0) {
       // here we order items with empty parent_id before children
       cmp = a.parend_id.compare(b.parent_id);
       if(cmp == 0) {
         // then compare by 'string_id'
         cmp = a.string_id.compare(b.string_id);
         if(cmp == 0) {
            // and eventually compare by 'value'
            cmp = a.value - b.value;
         }
       } 
    }
    return cmp < 0;
    

    这意味着我们首先按parent_id 排序,如果parent_id 为空,我们就好像string_idparent_id(我称之为effective_parent_id,在概念上类似于unix 中的有效用户 ID :) )。

    如果您无法修改类比较器方法,您可以实现特定的比较器(例如在 lambda 或仿函数中)并将其与 std::sort 一起使用。

    一些参考资料:

    【讨论】:

    • 感谢您的回答。如果以工作示例的形式提及它会更有帮助。另外,我无法比较strings。不过,我找到了解决方案并更新了答案。
    【解决方案2】:

    这里的逻辑是在struct X 中添加一个额外的int。是的,每个对象的内存会增加 4-8 个字节,但这对我的要求来说很好。

    struct X
    {
      string id, parent_id;
      int value, value_group = 0;
      //         ^^^^^^^^^^^ new indicator to be used while sorting
      bool operator< (const X& x) const 
      { return (value_group == x.value_group)? value < x.value : value_group < x.value_group; }
    
      void print () const { cout << "{" << id << ", " << parent_id << ", " << value << ", " << value_group << "}\n"; }
    };
    

    另一件事是,vector 是按时间顺序更新的,而不是与{} 一起更新(抱歉:我在 Qn 中错过了这个重要的部分)。因此,每当添加一个新元素时,它都会将其父元素拉到靠近自身的位置。下面以类的形式提到了该逻辑,其中包含集合(vector, array, list 等)并添加对象:

    struct MyClass
    {
      vector<X> vx;
    
      void Add (X&& x)
      {
        auto end = vx.end(), it = std::prev(end);
        bool isStandAlone = true;
        for(auto parent_id = x.parent_id;
            not parent_id.empty();
            parent_id = it->parent_id)
          for(auto itPrev = it - 1;
              parent_id != it->id or (isStandAlone = false);
              it = itPrev--)
            if(itPrev == vx.begin())
            {
              it->parent_id.clear();     
              break;
            }
    
        if(isStandAlone)
          x.parent_id.clear();
        else
        {
          auto begin = it;
          do { it->value_group = x.value; }
          while(++it != end and not it->parent_id.empty());
    
          decltype(vx) temp(std::make_move_iterator(begin), std::make_move_iterator(it));
          vx.erase(begin, it);
          vx.insert(vx.end(), temp.begin(), temp.end());
        }
        x.value_group = x.value;
        vx.push_back(std::move(x));
      }
    };
    

    Demo.

    【讨论】:

      猜你喜欢
      • 2017-02-25
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 2014-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多