【问题标题】:Sorting vector of string pointers字符串指针的排序向量
【发布时间】:2018-03-07 00:02:33
【问题描述】:

很抱歉,如果有人问过这个问题,但我无法找到我正在寻找的答案。

我有一个 std::string 指针向量,我想按字母顺序排序,但我无法弄清楚如何做到这一点。我正在使用 std::sort。

我写了一个quick program 来测试我想要做什么(因为在实际实现中,我的代码是在子进程中运行的,所以调试起来有点困难):

#include <string>
#include <algorithm>
#include <vector>
#include <string.h>

bool cmpStrPtrs(std::string *a, std::string *b) {
  std::string a1 = *a;
  std::string a2 = *b;
  if(a1 == a2) return 0;
  return a1 > a2 ? 1 : -1;
}

int main(int argc, char *argv[]) {
  std::vector<std::string *> vec;
  std::string *str1 = new std::string("AAAAA");
  std::string *str2 = new std::string("aaaaa");
  std::string *str3 = new std::string("xxxxx");
  std::string *str4 = new std::string("bfuen");
  std::string *str5 = new std::string("xylophone");
  vec.push_back(str1);
  vec.push_back(str2);
  vec.push_back(str3);
  vec.push_back(str4);
  vec.push_back(str5);

  std::sort(vec.begin(), vec.end(), cmpStrPtrs);
  for(std::string *str : vec) {
    printf("%s\n", str->c_str());
  }
  return 0;
}

当我运行它时,我得到这个输出:

$ ./strsort
xylophone
bfuen
xxxxx
aaaaa
AAAAA

这似乎根本没有按字母顺序排列,所以我可以假设我使用 sort() 错误或者我的比较器函数有问题。我也尝试过没有比较器功能,我认为这只是根据它们的内存位置从最小到最大对它们进行排序,这实际上并没有改变任何东西。我也尝试过使用

bool cmpStrPtrs(std::string *a, std::string *b) {
  return a->compare(*b);
}

但它给了我同样的结果。

如果相关,我正在使用 c++17 标准使用 g++ 进行编译。

【问题讨论】:

  • 使用 new 是非惯用的 C++。你的比较函数也是错误的(让我们从“你从一个应该返回bool的函数返回一个int”开始)另见en.cppreference.com/w/cpp/concept/Compare
  • 我不建议在标记为返回bool的函数中返回-1、0、+1
  • 您确定要vector&lt;string*&gt; 而不仅仅是vector&lt;string&gt;?我看不出额外的间接增加了什么价值。
  • 另请阅读Strict Weak Ordering

标签: c++ sorting vector


【解决方案1】:

std::string::compare 返回 int,而不是 bool。根据cppreference.com返回值为

如果 *this 按字典顺序出现在参数指定的字符序列之前,则为负值

如果两个字符序列比较相等,则为零

如果 *this 出现在参数指定的字符序列之后,则为正值,按字典顺序强文本

返回的值被转换为bool,对于所有非零值,其计算结果为true。这意味着您的函数会为每对不同的字符串返回 true。

C++ 标准实际上为字符串定义了operator&lt;,因此您可以将函数更改为

bool cmpStrPtrs(std::string *a, std::string *b) {
    return *a < *b;
}

但这仍然会在您的代码中留下一个大问题。您绝对不需要为此提供指针。实际上,您现在正在泄漏内存,因为您忽略了delete 他们。适合这项工作的工具是std::vector&lt;std::string&gt;。这还有一个额外的好处,即在没有额外的间接级别的情况下,std::sort 可以在没有辅助函数的情况下隐式调用operator&lt;,从而导致以下解决方案。

std::vector<std::string> vec;
vec.emplace_back("AAAAA");
vec.emplace_back("aaaaa");
vec.emplace_back("xxxxx");
vec.emplace_back("bfuen");
vec.emplace_back("xylophone");

std::sort(vec.begin(), vec.end());

【讨论】:

  • 哇,我觉得自己像个白痴。我不敢相信我没有注意到我没有返回一个布尔值。去年为我的算法课编写了这么多 java 比较函数,有点像自动驾驶仪。谢谢!
  • 另外,你说得对,我的代码有问题,但我使用字符串指针而不仅仅是字符串的原因是应用程序的底层结构要求它们是指针。这只是一个简单的例子,我想测试我想要做什么,而不是实际的实现。别担心,在实际代码中,它们会被删除。
  • @AndrewGraber 为什么它必须是指针?仅仅是因为它们不应该是副本还是有更严格的原因?
【解决方案2】:

你可以用 lambda 来做到这一点:

std::sort(vec.begin(), vec.end(), [](std::string * a, std::string * b) {
    return *a < *b;    
  });

【讨论】:

    【解决方案3】:

    你的比较函数是meant to simulate the less-than operator——这意味着如果a在b之前它应该返回true。如果 a 不等于 b,您当前的实现将返回 true。

    你有:

    if(a1 == a2) return 0;
    return a1 > a2 ? 1 : -1;
    

    应该是:

    if(a1 == a2) return false;
    return a1 > a2 ? false : true;
    

    或者只是:

    return a1 < a2;
    

    【讨论】:

      【解决方案4】:

      std::sort 期望 Strict Weak Ordering. 它并没有给出关于等于的废话;它只关心之前和之后。

      如果右手边在左手边之前,比较函数应该返回真。不幸的是在

      bool cmpStrPtrs(std::string *a, std::string *b) {
        std::string a1 = *a;
        std::string a2 = *b;
        if(a1 == a2) return 0;
        return a1 > a2 ? 1 : -1;
      }
      

      bool 对任何非 0 的值都为真。这意味着大于和小于都为真。这使得逻辑排序几乎不可能,因为大于和小于之前。

      改进削减 1:根据字典(字母)排序返回 bool。 String 已经实现了一个小于运算符,它完全符合您的要求。让我们使用它。

      bool cmpStrPtrs(std::string *a, std::string *b) {
        std::string a1 = *a;
        std::string a2 = *b;
        return a1 < a2;
      }
      

      改进剪辑 2:std::string a1 = *a; 创建一个全新的字符串,它是原始字符串的副本。由于您有一个指向原件的指针,您可以取消引用该指针并使用原件。不需要副本。

      bool cmpStrPtrs(std::string *a, std::string *b) {
        return *a < *b;
      }
      

      【讨论】:

        猜你喜欢
        • 2012-06-13
        • 2020-03-25
        • 2011-11-18
        • 1970-01-01
        • 1970-01-01
        • 2015-06-22
        • 1970-01-01
        • 2011-09-02
        相关资源
        最近更新 更多