【问题标题】:Using std::sort to sort Matrix使用 std::sort 对矩阵进行排序
【发布时间】:2018-05-14 10:01:06
【问题描述】:

有没有一种方法可以使用 std 中的 sort 函数对矩阵元素进行排序?

//Using this matrix
vector<vector<string>> mat;

例如,如果你有

5 2 1
0 0 2
1 4 3

结果是

0 0 1
1 2 2
3 4 5

【问题讨论】:

  • 排序矩阵没有意义。你需要澄清你的意思。
  • 同样的事情。有很多方法可以做到这一点。
  • 如果您的矩阵连续存储而不是vector&lt;vector&gt;,那么是的,std::sort 会起作用。但它会根据存储类型进行排序。如果矩阵以行优先顺序存储,则元素将按照您在示例中给出的排序。如果它们以列优先顺序存储;它们将按列排序。
  • 谢谢,我需要使用vector
  • @UKMonkey 如果我理解正确,这将导致 0 0 2 | 1 2 5 | 1 3 4 但这不是 OP 正在搜索的内容。

标签: c++ sorting stl


【解决方案1】:

要存储一个矩阵,std::vector 的嵌套并不是最好的解决方案。由于任何行都管理自己的大小,因此矩阵类没有固有的唯一列大小。

假设 OP 必须使用现有类型(可能不会更改),我基于此编写了示例。

一种解决方案(实施工作最少)是

  1. 将矩阵 (std::vector&lt;std::vector&lt;std::string&gt; &gt;) 复制到 std::vector&lt;std::string&gt; 类型的临时文件中
  2. 申请std::sort() 到这个临时的
  3. 再次将排序后的向量分配给矩阵。

考虑到元素的移动可能很昂贵,替代方法可能是

  1. 建立一个索引对向量
  2. std::sort() 它带有一个自定义的 less 仿函数(考虑矩阵元素)。

之后,可以使用索引对向量按排序顺序访问原始矩阵元素。

后者显示在我的示例代码中:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

typedef std::vector<std::string> Row;
typedef std::vector<Row> Matrix;
typedef std::pair<size_t, size_t> IndexPair;

struct LessMatrix {
  const Matrix &mat;
  LessMatrix(const Matrix &mat): mat(mat) { }
  bool operator()(const IndexPair &i1, const IndexPair &i2)
  {
    return mat[i1.first][i1.second] < mat[i2.first][i2.second];
  }
};

std::ostream& operator<<(std::ostream &out, const Matrix &mat)
{
  for (const Row row : mat) {
    for (const std::string elem : row) out << ' ' << elem;
    out << '\n';
  }
  return out;
}

int main()
{
  Matrix mat = {
    { "5", "2", "1" },
    { "0", "0", "2" },
    { "1", "4", "3" }
  };
  // print input
  std::cout << "Input:\n" << mat << '\n';
  // indexify matrix
  std::vector<IndexPair> indices;
  for (size_t i = 0, n = mat.size(); i < n; ++i) {
    const std::vector<std::string> &row = mat[i];
    for (size_t j = 0, m = row.size(); j < m; ++j) {
      indices.push_back(std::make_pair(i, j));
    }
  }
  // sort matrix
  LessMatrix less(mat);
  std::sort(indices.begin(), indices.end(), less);
  // print output
  Matrix matOut;
  { size_t i = 0; const size_t nCols = 3;
    for (const IndexPair &index : indices) {
      if (i % nCols == 0) matOut.push_back(Row());
      matOut.back().push_back(mat[index.first][index.second]);
      ++i;
    }
  }
  std::cout << "Output:\n" << matOut << '\n';
  // done
  return 0;
}

输出:

Input:
 5 2 1
 0 0 2
 1 4 3

Output:
 0 0 1
 1 2 2
 3 4 5

Life demo on coliru


OP 抱怨创建一个单独的索引向量。我怀疑自定义随机访问迭代器可能是替代品(直接对矩阵进行排序)。我必须承认我以前没有这样做过,但出于好奇,我试图解开这个谜题:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cassert>

typedef std::vector<std::string> Row;
typedef std::vector<Row> Matrix;

struct MatrixIterator {
  typedef size_t difference_type;
  typedef std::string value_type;
  typedef std::string* pointer;
  typedef std::string& reference;
  typedef std::random_access_iterator_tag iterator_category;

  // accessed matrix
  Matrix &mat;
  // index of row, index of column
  size_t i, j;

  MatrixIterator(Matrix &mat, size_t i = 0, size_t j = 0): mat(mat), i(i), j(j) { }

  MatrixIterator& operator =(const MatrixIterator &iter)
  {
    assert(&mat == &iter.mat);
    i = iter.i; j = iter.j;
    return *this;
  }

  size_t nCols() const { return mat.front().size(); }

  std::string& operator *() { return mat[i][j]; }
  const std::string& operator *() const { return mat[i][j]; }

  MatrixIterator& operator ++() { return *this += 1; }
  MatrixIterator operator ++(int) { MatrixIterator iter(*this); ++*this; return iter; }

  MatrixIterator& operator --() { return *this -= 1; }
  MatrixIterator operator --(int) { MatrixIterator iter(*this); --*this; return iter; }

  MatrixIterator& operator += (size_t n)
  {
    j += i * nCols() + n; i = j / nCols(); j %= nCols(); return *this;
  }

  MatrixIterator operator + (size_t n) const
  {
    MatrixIterator iter(*this); iter += n; return iter;
  }
  friend MatrixIterator operator + (size_t n, const MatrixIterator &iter)
  {
    MatrixIterator iter2(iter); iter2 += n; return iter2;
  }

  MatrixIterator& operator -= (size_t n)
  {
    j += i * nCols() - n; i = j / nCols(); j %= nCols();
    return *this;
  }

  MatrixIterator operator - (size_t n) const
  {
    MatrixIterator iter(*this); iter -= n; return iter;
  }
  size_t operator - (const MatrixIterator &iter) const
  {
    return (i * nCols() + j) - (iter.i * iter.nCols() + iter.j);
  }

  std::string& operator[](size_t i) { return mat[i / nCols()][i % nCols()]; }
  const std::string& operator[](size_t i) const { return mat[i / nCols()][i % nCols()]; }

  bool operator == (const MatrixIterator &iter) const
  {
    return i == iter.i && j == iter.j;
  }
  bool operator != (const MatrixIterator &iter) const { return !(*this == iter); }

  bool operator < (const MatrixIterator &iter) const
  {
    return i * nCols() + j < iter.i * iter.nCols() + iter.j;
  }
  bool operator > (const MatrixIterator &iter) const { return iter < *this; }
  bool operator <= (const MatrixIterator &iter) const { return !(iter > *this); }
  bool operator >= (const MatrixIterator &iter) const { return !(*this < iter); }
};

MatrixIterator begin(Matrix &mat) { return MatrixIterator(mat, 0, 0); }
MatrixIterator end(Matrix &mat) { return MatrixIterator(mat, mat.size(), 0); }

std::ostream& operator<<(std::ostream &out, const Matrix &mat)
{
  for (const Row row : mat) {
    for (const std::string elem : row) out << ' ' << elem;
    out << '\n';
  }
  return out;
}

int main()
{
  Matrix mat = {
    { "5", "2", "1" },
    { "0", "0", "2" },
    { "1", "4", "3" }
  };
  // print input
  std::cout << "Input:\n" << mat << '\n';
  // sort matrix
  std::sort(begin(mat), end(mat));
  // print output
  std::cout << "Output:\n" << mat << '\n';
  // done
  return 0;
}

输出:

Input:
 5 2 1
 0 0 2
 1 4 3

Output:
 0 0 1
 1 2 2
 3 4 5

Life demo on coliru

注意事项:

我使用了 cppreference.com 上的描述

作为“需求备忘单”并实现了其中描述的所有内容。一些细节,我凭猜测决定。 (特别是关于typedefs,我不太确定如何正确处理它们。)

【讨论】:

  • 这是一个很好的解决方案,但我不想重复所有元素
  • @polmonroig std::sort() 需要两个 random access iterators 来描述要排序的范围的开始和结束。如果您创建自己的随机访问迭代器(它提供对嵌套向量中元素的访问),那么您可以直接std::sort() 矩阵,索引数组就会过时。我必须承认我以前从未这样做过 - 所以这是一个学术声明......
  • @polmonroig 我们开始了:随机访问迭代器的更新。这在我看来有点令人毛骨悚然。在现实世界的项目中,我会坚持使用 class Matrix 和单个 vector,其中行/列索引是在专用的 get()/set() 方法中完成的。
  • @polmonroig 我刚刚看到其他人同时提供了一个类似想法的答案-我的错...
【解决方案2】:

您可以通过创建自定义包装矩阵类并定义自己的迭代器来对嵌套向量进行排序而无需额外的数据复制:

#include <algorithm>
#include <iterator>
#include <string>
#include <vector>

class MyMatrix {
 public:
  using DataStore = std::vector<std::vector<std::string> >;

  // Note: Make sure MyMatrix DO NOT out-live its `data` argument!
  MyMatrix(DataStore& data) : data_(data) {
    // Check that
    //  1. data.size() > 0;
    //  2. data[i].size() > 0 and same for all valid i.
  }

  class Iterator : public std::iterator<std::random_access_iterator_tag,
                                        std::string, int> {
   public:
    Iterator(DataStore& data) : data_(&data), index_(0) {}
    Iterator(DataStore& data, int index) : data_(&data), index_(index) {}
    Iterator(const Iterator& it) : data_(it.data_), index_(it.index_) {}

    Iterator& operator=(const Iterator& it) {
      data_ = it.data_;
      index_ = it.index_;
    }
    operator bool() const {
      return index_ >= 0 && index_ < data_->size() * (*data_)[0].size();
    }

    bool operator==(const Iterator& it) const {
      return data_ == it.data_ && index_ == it.index_;
    }
    bool operator!=(const Iterator& it) const {
      return data_ != it.data_ || index_ != it.index_;
    }

    Iterator& operator++() { ++index_; return *this; }
    Iterator& operator--() { --index_; return *this; }
    Iterator operator++(int) { return Iterator(*data_, index_++); }
    Iterator operator--(int) { return Iterator(*data_, index_--); }

    Iterator& operator+=(int offs) { index_ += offs; return *this; }
    Iterator& operator-=(int offs) { index_ -= offs; return *this; }
    Iterator operator+(int offs) { return Iterator(*data_, index_ + offs); }
    Iterator operator-(int offs) { return Iterator(*data_, index_ - offs); }
    int operator-(const Iterator& it) { return index_ - it.index_; }

    std::string& operator*() {
      return (*data_)[index_ / data_->size()][index_ % (*data_)[0].size()];
    }
    const std::string& operator*() const { return operator*(); }

   private:
    DataStore* data_;
    int index_;
  }; // class Iterator

  Iterator iterator() { return Iterator(data_); }
  Iterator begin() { return Iterator(data_, 0); }
  Iterator end() { return Iterator(data_, data_.size() * data_[0].size()); }

 private:
  DataStore& data_;
};  // class MyMatrix

然后sort 可以应用到MyMatrix 如下:

#include <iostream>

int main()
{
  std::vector<std::vector<std::string> > store = {
    { "5", "2", "1" },
    { "0", "0", "2" },
    { "1", "4", "3" }
  };

  MyMatrix matrix(store);

  for (const auto& row : store) {
    for (const auto& item : row) { std::cout << item <<' '; }
    std::cout <<'\n';
  }
  std::cout <<'\n';

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

  for (const auto& row : store) {
    for (const auto& item : row) { std::cout << item <<' '; }
    std::cout <<'\n';
  }
  std::cout <<'\n';
}

运行上面的代码会产生如下输出:

5 2 1 
0 0 2 
1 4 3 

0 0 1 
1 2 2 
3 4 5 

【讨论】:

    猜你喜欢
    • 2012-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多