O(n2logn) 的复杂度可以使用以下方法实现。
您最初存储 n 行和列中的每一个的排序版本。(此答案稍后将阐明此存储的行和列将使用的确切结构)请注意,即使您将根据单元格,单元格的值应与它们的初始索引一起存储。删除行和列时,初始索引将有所帮助。 (算法的第 2 步)
经过上面提到的预处理,有一个循环迭代n次。在这个循环内将是你的算法的步骤。每次迭代;
第 1 步:检查每个已排序的行和列中的最大元素,更新全局最大元素的值和地址。
步骤 2.1: 将元素添加到生成的数组/列表/等中,并将其从对应行列的排序行列结构中删除。
步骤 2.2: 遍历每个已排序的行结构。对于每一行,我们需要删除一列。由于我们有矩阵和最大元素的地址,我们还可以访问我们为每一行删除的列的确切值和地址。利用这一点,在每个已排序的行结构中,给定它已排序,执行二进制搜索以找到该元素并将其删除。
事情就是这样!如果将此排序后的行结构存储为一个简单的数组,则删除此元素将花费 O(n) 时间,因为您必须将数组的剩余部分向左移动 1。我能想到的解决方案是使用类似于 STL 的set 的结构。实际上,它可以像二分查找一样在 O(logn) 中查找,并提供 O(logn) 时间的插入和删除。
步骤 2.3: 与步骤 2.2 相同,这次遍历每个列结构。当我们删除一行时,我们正在从每一列中删除一个元素。但是,由于找到了地址(即行和列)和全局最大元素的值,我们知道要删除哪一行以及从每一列中删除的元素的值和地址。因此,对于每个列结构,我们从存储该列的排序版本的 STL 的类似集合的数据结构中找到并删除该元素。
性能
预处理:既然我们知道了存储排序的行和列的数据结构类型,我们可以说它需要 O(n2logn) 因为这些结构有 2n 个,我们在 O(logn) 时间内向每个结构插入 n 个元素。
第 1 步:有 2n 个已排序的结构,这意味着在 O(logn) 时间内访问它们的最大元素将具有组合 O(nlogn) 时间复杂度。
步骤 2.1: 虽然这取决于保存结果数据的数据结构,但假设它是一个数组,向其中添加一个元素需要 O(1) 时间。然而,这一步的总体复杂度为 O(logn),因为我们需要从它所属的已排序行和列结构中删除最大元素。
步骤 2.2: 有 n 个已排序的行结构,我们对其执行查找和删除操作,造成 O(nlogn) 负担。
步骤 2.3: 具有 O(nlogn) 成本,与步骤 2.2 相同。
考虑到步骤2.1、2.2、2.3进行了n次,算法的整体复杂度为O(n2logn)。
实施
您可能会在下面找到我上面提到的算法的完整实现,它似乎正在运行。该算法的要点是相同的,由于实现的微小变化,例如定义负无穷大或使用额外的布尔向量来存储行或列是否被完全删除。(即我们不会费心查看它的排序结构在剩余矩阵中寻找最大元素时)
#include <set>
#include <vector>
#include <iostream>
#define NEGATIVE_INFINITY -1
using namespace std;
vector< set< pair<int, int> > > sortedRows;
vector< set< pair<int, int> > > sortedCols;
vector< int > result;
void solve(const vector< vector< int > > &matrix)
{
int n = matrix.size();
vector<bool> rowNotDeleted(n, true), colNotDeleted(n, true);
// Preprocessing
for(int i=0; i < n; ++i)
{
sortedRows.resize(n);
sortedCols.resize(n);
for(int j=0; j < n; ++j)
{
sortedRows[i].insert( make_pair(matrix[i][j], j) );
sortedCols[j].insert( make_pair(matrix[i][j], i) );
}
}
for(int k=0; k < n; ++k)
{
set< pair<int, int> >::reverse_iterator it;
// STEP 1: Find max. element
int maxVal = NEGATIVE_INFINITY, maxRow = -1, maxCol = -1;
for(int i=0; i < n; ++i)
{
if(rowNotDeleted[i])
{
it = sortedRows[i].rbegin();
if(it->first > maxVal)
{
maxVal = it->first;
maxRow = i;
maxCol = it->second;
}
}
}
for(int i=0; i < n; ++i)
{
if(colNotDeleted[i])
{
it = sortedCols[i].rbegin();
if(it->first > maxVal)
{
maxVal = it->first;
maxRow = it->second;
maxCol = i;
}
}
}
// STEP 2.1: Add max. element to result.
result.push_back(maxVal);
/*
* Due to my implementation, removing it from
* relevant sorted data structures can be done
* in steps 2.2 and 2.3.
*/
// sortedRows[maxRow].erase( make_pair(maxVal, maxCol) );
// sortedCols[maxCol].erase( make_pair(maxVal, maxRow) );
rowNotDeleted[maxRow] = false;
colNotDeleted[maxCol] = false;
// STEP 2.2: Remove cells of deleted col from sorted row structures
for(int i=0; i < n; ++i)
{
sortedRows[i].erase( make_pair(matrix[i][maxCol], maxCol) );
}
// STEP 2.3: Remove cells of deleted row from sorted col structures
for(int i=0; i < n; ++i)
{
sortedCols[i].erase( make_pair(matrix[maxRow][i], maxRow) );
}
}
}
int main()
{
int n;
cin >> n;
// Read matrix
vector< vector< int > > matrix(n);
for(int i=0; i < n; ++i)
{
matrix[i].resize(n);
for(int j=0; j < n; ++j)
{
cin >> matrix[i][j];
}
}
solve(matrix);
// Output results
cout << result[0];
for(int i=1; i < n; ++i)
{
cout << " " << result[i];
}
cout << endl;
return 0;
}