重新排列列表的方式与另一个列表相同

时间:2014-08-26 23:06:55

标签: c++ sorting c++11

我碰到了一个页面,其中有很多类别,每个类别旁边都有每个类别中的项目数,用括号括起来。真的很普通。它看起来像这样:

Category 1 (2496)
Category 2 (34534)
Category 3 (1039)
Category 4 (9)
...

所以我很好奇,我想看看哪些类别有更多的项目等,并且由于所有类别都在页面中,我可以只选择它们并将它们复制到文本文件中,使事情变得非常简单。 / p>

我做了一个小程序,读取所有数字,将它们存储在一个列表中并对它们进行排序。为了知道它所属的数字类别,我只需Ctrl + F浏览器中的数字。

但是我觉得在我的文本文件中使用数字旁边的类别名称会很好,我设法在另一个文件中解析它们。但是,显然它们没有订购。

这是我到目前为止所能做的:

bool is_number(const string& s) {
    return !s.empty() && find_if(s.begin(), s.end(), [](char c) { return !isdigit(c); }) == s.end();
}

int main() {
    ifstream file;
    ofstream file_os, file_t_os;

    string word, text; // word is the item count and text the category name
    list<int> words_list; // list of item counts
    list<string> text_list; // list of category names

    file.open("a.txt");
    file_os.open("a_os.txt");
    file_t_os.open("a_t_os.txt");

    while (file >> word) {
        if (word.front() == '(' && word.back() == ')') { // check if it's being read something wrapped in parenthesis
            string old_word = word;
            word.erase(word.begin());
            word.erase(word.end()-1);
            if (is_number(word)) { // check if it's a number (item count)
                words_list.push_back(atoi(word.c_str()));
                text.pop_back(); // get rid of an extra space in the category name
                text_list.push_back(text);
                text.clear();
            } else { // it's part of the category name
                text.append(old_word);
                text.append(" ");
            }
        } else {
            text.append(word);
            text.append(" ");
        }
    }

    words_list.sort();

    for (list<string>::iterator it = text_list.begin(); it != text_list.end(); ++it) {
        file_t_os << *it << endl;
    }

    for (list<int>::iterator it = words_list.begin(); it != words_list.end(); ++it) {
        file_os << fixed << *it << endl;
    }

    cout << text_list.size() << endl << words_list.size() << endl; // I'm getting the same count
}

现在我忘了在号码旁边有名字,因为有些事情对我来说更有趣。我认为有必要找到一种方法来重新排列string中包含类别名称的text_list,其方式与对项目计数的列表进行排序的方式完全相同。

让我用一个例子来解释,假设我们有以下类别:

A (5)
B (3)
C (10)
D (6)

我这样做的方式我将list<int>包含:{10, 6, 5, 3}list<string>,其中包含:{A, B, C, D}

我所说的是我想找到一种方法,我可以跟踪元素在第一个列表中重新排列的方式,并将该模式​​应用于第二个列表。什么是重新排列的模式?它将是:第一项(5)进入第三位,第二项(3)进入第四位,第三项(10)进入第一位,依此类推...... 。然后,这个模式应该应用于另一个列表,这样它最终会像这样:{C, D, A, B}

enter image description here

要做的是跟踪模式并将其应用到下面的列表中。

我有什么方法可以做到这一点?任何可以帮助我的特定功能?任何跟踪sort算法的所有交换和切换的方法都可以将其应用于相同大小的其他列表?不同的排序算法怎么样?

我知道这可能是非常低效和一个坏主意,但这似乎是一个小挑战。

我也知道我可以在stringint之类的容器中将pairmap,类别和项目计数配对,或者创建容器类我自己并根据项目数量对项目进行排序(我猜map将是最佳选择,你怎么看?),但这不是我要问的。

3 个答案:

答案 0 :(得分:0)

执行此操作的最佳方法是创建一个列表,其中包含要在自定义排序函数中排序和提供的两组信息。

例如:

struct Record {
  string name;
  int count;
};

list<Record> myList;

sort(myList, [](Record a, Record b){
    return a.count < b.count;
  });

在一般情况下,管理复杂数据类型的一个列表总是比尝试单独管理两个或更多简单数据类型列表更好,特别是当它们是可变的时。

答案 1 :(得分:0)

更多改进方式:

首先注意一些事项:

  • 为了清晰起见,易于阅读代码等,建议将类别名称和项目存储在一起......
  • 最好使用std::vector代替std::list(请参阅Bjarne Stroustrup opinion
  • 代码使用问题中指定的格式加载文件,存储在信息对的矢量​​中。
  • 使用std::sort函数仅按项目编号排序(具有相同项目的类别将按任何顺序排序,如果您要对类别名称进行排序,则具有相同项目的类别会将lambda正文更改为{ {1}}。
  • 在一个集合项目和索引中添加了一个带有信息拆分的版本(用于将项目与名称相关联),以及其他名称信息。

代码:

return std::tie(left.items, left.name) > std::tie(right.items, right.name);

获得的输出:

#include <iostream>
#include <fstream>
#include <algorithm>
#include <vector>

bool is_number(const std::string& s) {
    return !s.empty() &&
           find_if(s.begin(), s.end(), [](char c) { return !isdigit(c); }) ==
               s.end();
}

struct category_info {
    std::string name;
    int items;
};

struct category_items_info {
    int items;
    size_t index;
};

int main() {
    std::ifstream file("H:\\save.txt");

    std::vector<category_info> categories;
    std::vector<category_items_info> categories_items;
    std::vector<std::string> categories_names;

    std::string word;
    std::string text;
    while (file >> word) {
        if (word.front() == '(' && word.back() == ')') {
            std::string inner_word = word.substr(1, word.size() - 2);
            if (is_number(inner_word)) {
                std::string name = text.substr(0, text.size() - 1);
                int items = atoi(inner_word.c_str());

                categories.push_back(category_info{name, items});
                categories_names.push_back(name);
                categories_items.push_back(
                    category_items_info{items, categories_items.size()});

                text.clear();
            } else { // it's part of the category name
                text.append(word);
                text.append(" ");
            }
        } else {
            text.append(word);
            text.append(" ");
        }
    }

    std::sort(categories.begin(), categories.end(),
              [](const category_info& left, const category_info& right) {
        return left.items > right.items;
    });

    std::sort(
        categories_items.begin(), categories_items.end(),
        [](const category_items_info& left, const category_items_info& right) {
            return left.items > right.items;
        });

    std::cout << "Using the same storage." << std::endl;
    for (auto c : categories) {
        std::cout << c.name << " (" << c.items << ")" << std::endl;
    }

    std::cout << std::endl;
    std::cout << "Using separated storage." << std::endl;
    for (auto c : categories_items) {
        std::cout << categories_names[c.index] << " (" << c.items << ")"
                  << std::endl;
    }
}

答案 2 :(得分:0)

列表不支持随机访问迭代器,因此这将成为一个问题,因为无法根据索引的向量(或数组)对列表进行置换,而无需进行大量的列表遍历来回模拟随机访问迭代。 NetVipeC的解决方案是使用向量而不是列表来解决这个问题。如果使用向量,则可以为要排序的向量生成索引的向量(或数组),然后使用自定义比较运算符对向量索引进行排序。然后,您可以根据排序索引的向量复制向量。也可以根据索引对矢量进行重新排序,但该算法也会对索引的矢量进行排序,因此您不得不复制已排序的索引(以排序第二个矢量),或者复制每个矢量的排序索引顺序。

如果你真的想使用列表,你可以实现自己的std :: list :: sort,它将在两个列表上执行相同的操作。 Microsoft版本的std :: list :: sort使用一个列表数组,其中array [i] = 2 ^ i中的节点数,并且它将节点一次合并到数组中,然后在处理所有节点时,它合并数组中的列表以生成排序列表。您需要两个数组,每个数组用于排序。如果需要,我可以为这种类型的列表排序发布示例C代码。