Использование std :: sort для сортировки матрицы

Есть ли способ отсортировать элементы матрицы с помощью функции сортировки из std?

//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

Матрица сортировки не имеет смысла. Вам необходимо пояснить, что вы имеете в виду.

StaceyGirl 14.05.2018 12:03

Я сказал элементы матрицы, вы должны прочитать, прежде чем писать

polmonroig 14.05.2018 12:05

То же самое. Есть много способов сделать это.

StaceyGirl 14.05.2018 12:06

Если ваша матрица хранится непрерывно, а не как vector<vector>, тогда да, std :: sort будет работать. Но это было бы сортировкой по типу хранилища. Если матрица хранится в строчном порядке, элементы будут отсортированы так, как вы указали в примере. Если они хранятся в порядке следования столбцов; они будут отсортированы по столбцам.

eozd 14.05.2018 12:09

Спасибо, мне нужно использовать vector <vector>

polmonroig 14.05.2018 12:10

В вашем примере каждая строка сортируется по возрастанию; и THEN сортирует столбцы по 2-й строке, чтобы они были инкрементными. Вы можете сделать это с помощью std :: sort; нет проблем.

UKMonkey 14.05.2018 12:11

Я знаю, как сортировать строки инкрементально, но как сортировать столбцы? UKMonkey

polmonroig 14.05.2018 12:13

@UKMonkey Если я правильно вас понял, это приведет к 0 0 2 | 1 2 5 | 1 3 4, но OP ищет не это.

Scheff's Cat 14.05.2018 12:13

Почему бы вам не скопировать vector<vector<string>> в vector<string>, std :: sort его, а затем скопировать обратно в vector<vector<string>>? Если копирование считается слишком дорогостоящим, более сложным решением было бы создать vector<pair<size_t, size_t>> (который хранит индексы элементов матрицы) и std::sort их с настраиваемым функтором less, который считает, что исходная матрица выбирает элементы через индексы.

Scheff's Cat 14.05.2018 12:17

Вы правы - это не отсортировано так - оно отсортировано по столбцу, а затем по строке ... Ого, вау; это странное желание делать. Так что будьте уверены - вы используете std :: sort для сортировки по столбцу; а затем вы используете std :: sort для сортировки каждой строки. В чем проблема?

UKMonkey 14.05.2018 12:23

О, хорошо, я думаю, что знаю, как это сделать, UKMonkey, спасибо!

polmonroig 14.05.2018 12:24

Предполагая, что вы не можете изменить тип, который используете, вам, вероятно, просто нужно написать себе адаптер диапазона для матрицы. Убедитесь, что он удовлетворяет требованиям RandomAccessIterator, и предоставьте пару из них для std::sort(). Работа сделана.

Toby Speight 14.05.2018 18:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
12
213
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Для хранения матрицы вложение std::vector - не лучшее решение. Поскольку любая строка управляет своим размером, не существует уникального размера столбца, присваиваемого классом матрицы.

Предполагая, что OP проклят использовать существующий тип (который не может быть изменен), я написал свой образец на основе этого.

Одним из решений (с наименьшими усилиями по реализации) было бы

  1. скопировать матрицу (std::vector<std::vector<std::string> >) во временную типа std::vector<std::string>
  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


ОП пожаловался на создание отдельного вектора индекса. Я подозревал, что пользовательский итератор произвольного доступа может быть заменой (для сортировки матрицы напрямую). Должен признать, что раньше я этого не делал, но из любопытства попытался разгадать вот что:

#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

как "шпаргалку требований" и реализовал все, что там описано. Некоторые детали я решил догадываться. (В частности, что касается typedef, я не совсем уверен, как их исправить.)

Это хорошее решение, но я не хочу повторять все элементы дважды

polmonroig 14.05.2018 12:59

@polmonroig std::sort() требует двух итераторы произвольного доступа для описания начала и конца диапазона для сортировки. Если вы создаете свой собственный итератор с произвольным доступом (который обеспечивает доступ к элементу во вложенном векторе), вы можете напрямую std::sort() матрицу, и массив индексов устареет. Должен признаться, я никогда не делал этого раньше - так что это скорее академическое заявление ...

Scheff's Cat 14.05.2018 13:06

@polmonroig Начнем: обновление с итератором произвольного доступа. Мне это кажется каким-то жутким. В реальных проектах я бы предпочел class Matrix с одним vector внутри, где индексация строк / столбцов выполняется специальными методами get () / set ().

Scheff's Cat 14.05.2018 15:33

@polmonroig Я только что видел, что кто-то тем временем дал ответ с похожей идеей - моя плохая ...

Scheff's Cat 14.05.2018 15:37
Ответ принят как подходящий

Вы можете отсортировать вложенные векторы без дополнительного копирования данных, создав собственный класс матрицы-оболочки и определив свой собственный итератор:

#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 

Другие вопросы по теме