【数据结构】求矩阵中马鞍数的几种方法

本文介绍了三种求解矩阵马鞍点的算法,包括时间复杂度为O(N^3)的直接判断法,O(N^2)的空间换时间法,以及O(N)的优化比对法。通过证明,作者展示了在严格定义下,矩阵中马鞍点至多一个,并提供了相应的代码实现。

【数据结构】求矩阵中马鞍数的几种方法


题目如下:

请写一个程序,找出给定矩阵的马鞍点。
注 :若一个矩阵中的某元素在其所在行最小而在其所在列最大,则该元素为矩阵的一个马鞍点。

针对这道题,我们常用也最常见到的有两种方法,除了这两种方法之外,这里笔者还会给出自己证明推导后得出的第三种方法。
首先列出比较常见的两种方法。

思路一

时间复杂度 : O(N^3)
思路一是最容易想到的, 即对矩阵中的每一个元素点做判断, 若该元素既是其所在行的最小值, 同时也是其所在列的最大值, 则该点为矩阵中的一个马鞍点
下面贴出代码,不做注释

//@author:MER
void saddlePoint_one(vector<vector<int>> &matrix) {
    for (int i = 0; i != ROW; i++) {
        for (int j = 0; j != COL; j++) {
            cout << matrix[i][j] << "\t" << ends;
        }
        cout << endl;
    }
    bool flag = true;
    for (int i = 0; i != ROW; i++) {
        for (int j = 0; j != COL; j++) {
            int k = 0, temp = matrix[i][j];
            flag = true;
            for (k = 0; k != COL; k++) {
                if (temp > matrix[i][k]) {
                    flag = false;
                    break;
                }
            }
            for (k = 0; flag == true && k != ROW; k++) {
                if (temp < matrix[k][j]) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                cout << "matrix[" << i << "][" << j << "] :" << matrix[i][j] << " 为马鞍数" << endl;
            }
        }
    }
}

思路二

时间复杂度:O(N^2)
这种思路采取了以空间换时间的思路,用两个一维数组row_min[ROW] 与 col_max[COL] 分别保存 每行的最小值 与 每列的最大值, 之后再比对两个数组中的值来找出符合条件的马鞍数

//@author:MER
void saddlePoint_two(vector<vector<int>> &matrix) {
    int row_min[ROW], col_max[COL];
    for(int i = 0; i != ROW; i++)
        row_min[i] = matrix[i][0];
    for(int i = 0; i != COL; i++)
        col_max[i] = matrix[0][i];
    for (int i = 0; i != ROW; i++)
    {
        for (int j = 0; j != COL; j++) {
            if (row_min[i] > matrix[i][j]) row_min[i] = matrix[i][j];
            if (col_max[j] < matrix[i][j]) col_max[j] = matrix[i][j];
        }
    }
    for (int i = 0; i != ROW; i++){
        for (int j = 0; j != COL; j++) {
            if (row_min[i] == col_max[j])  
            {
                cout << "matrix[" << i << "][" << j << "] :" << matrix[i][j] << " 为马鞍数" << endl;
            }
        }
    }
}

思路三

时间复杂度 : O(N)
第二种思路基本上已经把问题在统计方面的复杂度压缩在了一个可控的范围内,而思路三正是在思路二的基础上,对row_min与col_max的比对过程做了进一步的优化。
这种思路是我偶然间思索得到的。在网上查找类似的方法时并没有找到,因此对于这种思路涉及到的过程,我将在此给予证明及详细注释,如果有不恰当甚至错误的地方,请各位指证,以免误导他人。
首先在矩阵的处理上,依旧同思路二一样,用一维数组row_min[ROW]保存 每行的最小值, 用col_max[COL]保存 每列的最大值。
接下来是一系列简单的证明过程,我将证明出在严格意义上,一个矩阵中马鞍点的数量至多只能有一个, 并且 若马鞍点存在,其一定是row_min中的最大值, 同时是col_max中的最小值
首先证明一个矩阵中至多只有一个马鞍点:
给出一个矩阵matrix[M][N]如下

0010M00111M10N1NMN(1)(1)0001⋯0N1011⋯1N⋮⋮⋱⋮M0M1⋯MN

首先利用反证法,
对其中任意的m, n, i, j, 若matrix[m][n]为马鞍点,并且同时有matrix[i][j]也是马鞍点
ijmjinmn(2)(2)ij⋯in⋮⋱⋮mj⋯mn

因为 matrix[i][j] 为第i行中的最小值,所以有 ij < in
而 matrix[m][n] 为第n列中的最大值, 因此 mn > in
同理得 mj > mn, ij > mj, 显然不成立。
因此我们得到, 在严格意义上,一个矩阵中至多只能有一个马鞍点

接着我们列出经过处理的row_min 与 col_max 数组:
row_min[M] = {r1_min, r2_min, …, rM_min}
col_max[N] = {c1_max, c2_max, …, cN_max}
同样利用反证法
对任意的 i, j, 设 ri_min 为矩阵的马鞍点,若ri_min != max(row_min),既ri_min 不是row_min数组中的最大值, 取而代之的 rj_min == max(row_min).
则在矩阵

ri_mintemprj_min(3)(3)ri_min⋮⋱temp⋯rj_min

中有 ri_min > temp, temp > rj_min, 不成立
**因此可得 若ri_min为矩阵在严格意义上的马鞍点,则ri_min一定是row_min数组中的最大值。
同理可证 若ci_max为矩阵在严格意义上的马鞍点,则ci_max一定是col_max数组中的最小值。**

由上述结论我们得到一个验证马鞍点的新思路,代码如下

//@author:MER
void saddlePoint_three(vector<vector<int>> &matrix) {
    vector<int> row_min(ROW);
    vector<int> col_max(COL);       //用row_min保存行最小值,用col_max保存列最大值
    //初始化
    for (int i = 0; i != ROW; i++)
        row_min[i] = numeric_limits<int>::max();
    for (int i = 0; i != COL; i++)
        col_max[i] = numeric_limits<int>::min();
    for (int i = 0; i != ROW; i++) {
        for (int j = 0; j != COL; j++) {
            cout << matrix[i][j] << "\t" << ends;
        }
        cout << endl;
    }
    //将矩阵中行最小值与列最大值分别保存在两个数组中
    for (int i = 0; i != ROW; i++)
    {
        for (int j = 0; j != COL; j++) {
            if (row_min[i] > matrix[i][j]) row_min[i] = matrix[i][j];
            if (col_max[j] < matrix[i][j]) col_max[j] = matrix[i][j];
        }
    }

    //找到row_min中的最大值 与col_max中的最小值
    auto rmax = max_element(row_min.begin(), row_min.end());
    auto cmin = min_element(col_max.begin(), col_max.end());

    //若rmax与cmin相等, 则表示找到马鞍点
    if (*rmax == *cmin)
        cout << "matrix[" << rmax - row_min.begin() << "][" << cmin - col_max.begin() << "] :" << *rmax << " 为马鞍数" << endl;
    else
        cout << "马鞍点不存在" << endl;

}

特别需要指出的是,思路三所适用的情况为:矩阵中存在严格意义上遵循马鞍点定义的马鞍点
举个例子,诸如

555555555(1)(1)55⋯555⋯5⋮⋮⋱⋮55⋯5

这样的矩阵,在严格意义上讲是不存在任何一个数符合马鞍点的定义的,因此在实际编码中我们需要分辨剔除这种情况,请读者注意。

下面列出上述代码所用到的头文件及测试用例

#include <ctime>
#include <vector>
#include <iostream>
#include <limits>
#include <algorithm>
using namespace std;
#define ROW 6
#define COL 5
/*所有的求马鞍点函数应都包含在Solution类中*/
class Solution{};/*<-->*/
int main(void) {
    srand(time(NULL));
    vector<vector<int>> matrix(ROW, vector<int>(COL));
    for (int i = 0; i != ROW; i++) {
        for (int j = 0; j != COL; j++) {
            matrix[i][j] = rand() % 100;
        }
    }
    for (int i = 0; i != COL; i++) {
        matrix[3][i] = rand() % 30 + 100;
    }

    Solution().saddlePoint_three(matrix);
    system("pause");
    return 0;
}

文章到此结束,如有错误,恳请指正。
MER

评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值