本系列可作为算法学习系列的笔记,小编会将代码记录下来,大家复制下来就可以练习了,方便大家学习。小编作为新晋码农一枚,会定期整理一些写的比较好的代码,作为自己的学习笔记,会试着做一下批注和补充,如转载或者参考他人文献会标明出处,非商用,如有侵权会删改!欢迎大家斧正和讨论!
系列文章目录
为什么比较排序算法的时间复杂度下界是 Ω(n log n)?
前言
从两个角度实现,但不是直接能运行的代码,是伪代码,需要进行简单修改再运行
一、鞍点是什么?
鞍点(Saddle Point)是数学和优化理论中的一个重要概念,指在多元函数中,某个点在某一方向上是局部极大值点,而在另一方向上是局部极小值点,整体上既不是全局极大值点也不是全局极小值点。其名称来源于其几何形状类似马鞍:在马鞍的顶部(沿一个方向)是最高点,而在侧面(沿另一个方向)是最低点。
我们这里对举例的题目达成一个简单共识,即矩阵A[m][n]中存在某个元素a[i][j],a[i][j]是第i行中最小值且是第j列中最大值,则称该元素为矩阵A的鞍点。(如果使用这个概念,即a[i][j]是第i行中最大值且是第j列中最小值,则称该元素为矩阵A的鞍点,小编认为两种都可以)
下面举例题目:
若矩阵A[m][n]中存在某个元素a[i][j],满足:a[i][j]是第i行中最小值且是第j行列中的最大值,则称该元素为矩阵A的鞍点,试编写算法计算矩阵的鞍点个数。
这里简单举一个图例,图例里45为第一行和第四行最小的数,且为第一列里最大的数值,故45为鞍点
在元素各不相等的矩阵中,鞍点有且仅有一个,甚至可能没有鞍点。这里举例了一个有重复元素的矩阵,且鞍点数为2。
二、实例操作
1.枚举法
基本思路:用枚举法,对矩阵中的每一个元素a[i][j]进行判别。若a[i][j]是第i行的最小数,则继续判别,看它是否也是第j列的最大数,如果成立则是鞍点。当a[i][j]不是第 i行的最小数或者不是第j列的最大数则选择下一个元素继续。
显然,矩阵A可用一个二维数组表示,算法如下所示。
#define m 10
#define n 10
#define true 1
#define false 0
int saddle (int A[m][n])/*求m行n列矩阵的鞍点*/
{ int count=0,i,j,k,rowmin, colmax;
/*rowmin为true时表示A[i][j]是第i行最小数, colmax为true时表示 A[i][j]是第j列的最大数*/
for (i=0;i<m;i +) /*用枚举法对矩阵的每个元素进行判断*/
{ for(j=0;j<n;j++) /*矩阵从0开始排列,最后一个元素下标为A[m-1][n-1]*/
{
k=0;
while (k<n)&& (A[i][k]>=A[i][j]) /*是否是第i行最小数*/
{ k++; }
if(k<n) {rowmin=false;}
else {rowmin=true;}
if (rowmin==true) /*是第i行最小数时继续判断
{
k=0;
while (k<m) && (A[k][j]<=A[i][j]) /*是否是第j列最大数*/
{ k++; }
if(k<m) {colmax=false;}
else {colmax=true; }
}
if (rowmin==true && colmax==true)
{count++; /*鞍点计数*/ }
}
}
return(count);
}
时间效率分析:
双重循环体内有两个并列的while循环语句。第一个while循环执行0(n)次,第二个 while 循环最多执行0(m)次。所以总的时间效率应该是0(mxnx(m+n))。
空间效率分析:
除矩阵A用二维数组存储外,用了几个附加空间存储中间变量,如k,rowmin, colmax
。所以空间效率为O(1)。
注:空间复杂度:程序运行时的内存需求,无论问题的规模怎么变,算法运行所需的内存空间都是固定的常量,算法复杂度为S(n)=O(1)。【算法原地工作,算法所需内存空间为常量,只需关注存储空间大小与问题规模相关的变量】
空间复杂度=递归调用的深度
时空折中:为了改善一个算法的时间开销,可增大空间开销为代价,反之亦然。
2.增加辅助空间
第一种方法采用枚举法,时间效率应该最低。能否设计一个时间效率较优的算法呢?可以通过增加辅助空间来提高时间效率,具体方法如下:先将矩阵每行最小数和每列的最大数求出来,并分别存放在C[m]和B[n]两个一维组中,然后对C[m]和B[n]的每对元素进行比较,假定C[i]和B[j]相等(见下图)
可以证明:因为C[i]是第i行的最小数,所以A[i][j]≥C[i];
又因为B[j]中第j列的最大数,所以A[i][j]≤B[j]。根据B[j]和C[i]相等,得出:
A[i][j]==B[j]==C[i]
即A[i][j]既是第i行的最小数,又是第j列的最大数。具体算法如下所示。
#define m 10
#define n 10
#define true 1
#define false 0
int Saddle (int A(m][n])
{ int count=0, i,j,k;
int B[n],C[m];
for (i=0;i<m;i++) /*求每行的最小数*/
{ C[i]=A[i][0];
for (j=1;j<n;j++)
{ if (C[i]>A[i][j])
{ C[i]=A[i][j]); }
}
for (j=0;j<n;j++) /*求每列的最大数*/
{ B[j]=A[0][j];
for (i=1;i<m;i++)
{ if (B[j]<A[i][j])
{ B[j]=A[i] [j]; }
}
/*求所有鞍点*/
for (i=0;i<m;i++)
{ for (j=0;j<n;j++)
{ if (C[i]==B[j])
{ count++; } /*鞍点计数*/
}
}
return (count);
}
时间效率分析
本算法共有三小段并列的函数。
求每行最小数的时间效率:0(mxn)
求每列最大数的时间效率:0(mXn)
统计所有鞍点的时间效率:0(mxn)
所以总的时间效率为0(mXn)。
空间效率分析
显然空间效率为0(m+n)。
比较这两种算法,显然方法二的时间效率大大优于方法一,但空间效率较差。这是典型的空间换时间算法实例之一。
总结
以上就是今天要讲的内容,本文简单介绍了数组中的鞍点概念,总结了两种解法,对于部分概念进行了更细节的描述和补充,欢迎大家指正和讨论。
文章内容的两种解法来源于:数据结构(C语言版)-秦锋 主编 第一章 绪论