矩阵中从左上角到右下角最短路径(五种方法)

原创 2016年05月30日 21:36:21
  题目:给定一个n*m的矩阵,矩阵中元素非负,从左上角到右下角找一条路径,使得路径上元素之和最小,每次只能向右或者向下走一个方格。如下图所示:最短路径是图中绿色部分的元素。

  方法一(转换为图中的最短路径):我们可以把矩阵中的每个方格当做图中的一个顶点,相邻的方格之间有一条边,每个方格最多有两条出边,(当前方格到右侧方格有一条出边,当前方格到下侧方格有一条出边)。我们把矩阵中的最短路径转换为图中的最短路径,使用Dijstra算法来做此题,我们再次使用最简单的Dijstra算法,没有进行优化。图中总共有n*m个点,因为每一次都需要找到一个最小值,找最小值得代价为o(n*m),总共需要找o(n*m)个最小值,所以时间复杂度为o(n*n*m*m)。
struct Node
{
	int val;
	int row;
	int col;
	Node(){}
	Node(int v, int r, int c) :val(v), row(r), col(c)
	{}
	friend bool operator<(const Node &lhs, const Node &rhs);
};

bool operator<(const Node &lhs, const Node &rhs)
{
	return lhs.val > rhs.val;
}

struct Vertex
{
	int dis;
	bool visited;
	Vertex(){}
	Vertex(int d, bool v) :dis(d), visited(v){}
};

class Solution {
public:
    Node findMinVal(vector<vector<Vertex>> ve)
    {
    	Node res = Node(INT_MAX, 0, 0);
    	for (int i = 0; i < ve.size(); ++i)
    	{
    		for (int j = 0; j < ve[0].size(); ++j)
    		{
    			if (!ve[i][j].visited && res.val > ve[i][j].dis)
    			{
    				res.col = j;
    				res.row = i;
    				res.val = ve[i][j].dis;
    			}
    		}
    	}
    	return res;
    }
    
    int minPathSum(vector<vector<int>>& grid)
    {
    	int sum = 0;
    	int rows = grid.size();
    	int cols = grid[0].size();
    	vector<vector<Vertex>> ve;
    	ve.resize(rows);
    	for (int i = 0; i < rows; ++i)
    	{
    		ve[i].resize(cols);
    		for (int j = 0; j < cols; ++j)
    		{
    			ve[i][j] = Vertex(INT_MAX, false);
    		}
    	}
    	ve[0][0].dis = grid[0][0];
    	while (true)
    	{
    		Node res = findMinVal(ve);
    		int row = res.row;
    		int col = res.col;
    		sum = res.val;
    		if (row == rows - 1 && col == cols - 1)
    			break;
    		ve[row][col].visited = true;
    		if (row + 1 < rows && sum + grid[row + 1][col] < ve[row + 1][col].dis)
    		{
    			ve[row + 1][col].dis = sum + grid[row + 1][col];
    		}
    		if (col + 1 < cols && sum + grid[row][col + 1] < ve[row][col + 1].dis)
    		{
    			ve[row][col + 1].dis = sum + grid[row][col + 1];
    		}
    	}
    	return sum;
    }
};
方法二(动态规划):时间复杂为o(n*m)和空间复杂度为o(n*m)。典型的动态规划问题,假设当前已经开始计算s[i][j],那么s[i][j]只可能从s[i-1][j]+grid[i][j]或者s[i][j-1]+grid[i][j]计算得到,也就是s[i][j] = min(s[i-1][j],s[i][j-1])+grid[i][j]。我们需要一个o(n*m)额外空间保存已经计算的s[i][j]的值,我们只需要访问一遍数组即可。因此时间复杂度为o(n*m),空间复杂度为o(n*m)。我们需要特殊处理矩阵中第一行和第一列。因为第一行没有s[i-1][j]元素,只有s[i][j-1]元素。第一列没有s[i][j-1]元素,只有s[i-1][j]元素。代码如下:
int minPathSum(vector<vector<int>>& grid)
{
	//当grid为空时,返回0
	if (grid.empty())
		return 0;
	//获取grid的行数和列数
	int rows = grid.size();
	int cols = grid[0].size();
	//额外数组data,大小和grid一样
	vector<vector<int>> data(rows,vector<int>(cols,grid[0][0]));
	//处理data第一列
	for (int i = 1; i < rows; ++i)
	{
		data[i][0] = data[i - 1][0] + grid[i][0];
	}
	//处理data第一行
	for (int i = 1; i < cols; ++i)
	{
		data[0][i] = data[0][i - 1] + grid[0][i];
	}
	//处理data非第一行和第一列的元素
	for (int i = 1; i < rows; ++i)
	{
		for (int j = 1; j < cols; ++j)
		{
			data[i][j] = min(data[i][j - 1], data[i - 1][j]) + grid[i][j];
		}
	}
	//返回最短路径值
	return data[rows - 1][cols - 1];
}
方法三(动态规划):时间复杂度为o(n*m),空间复杂度为o(m),此方法需要2*m额外空间。当我们求s[i][j]时,s[i-2]行的元素我们就不再需要,我们只需要s[i-1]行中的元素,我们把s[i-1]行中的元素保存在pre数组中,数组的大小为m。我们把s[i]保存在cur数组中,当s[i]行的元素计算完毕以后,我们交换pre和cur数组。因为需要pre数组和cur数组,且数组的大小都为m,所以我们需要2*m大小的额外空间。
int minPathSum(vector<vector<int>>& grid)
{
	//当grid为空时,返回0
	if (grid.empty())
		return 0;
	//获得grid行数和列数
	int rows = grid.size();
	int cols = grid[0].size();
	//保存s[i-1]行中的元素
	vector<int> pre(cols, grid[0][0]);
	//保存当前行的元素
	vector<int> cur(cols);
	//初始化pre
	for (int i = 1; i < cols; ++i)
	{
		pre[i] = pre[i - 1] + grid[0][i];
	}
	//获得s[i][j]
	for (int i = 1; i < rows; ++i)
	{
		cur[0] = grid[i][0] + pre[0];
		for (int j = 1; j < cols; ++j)
		{
			cur[j] = min(cur[j - 1], pre[j]) + grid[i][j];
		}
		swap(cur, pre);
	}
	return pre[cols - 1];
}
方法四(动态规划):时间复杂度为o(n*m),空间复杂度为o(m),需要m大小的额外空间,注意此方法和方法三的区别,方法三需要2*m大小的额外空间,此方法只需要m大小的额外空间,在方法三中我们保存当前行s[i]中的元素,假设我们当前计算s[i][j],我们只需要知道s[i][j-1]的值即可,不需要保存s[i]行中的元素。每次计算s[i][j]时,我们需要更新pre[j]的值。
int minPathSum(vector<vector<int>>& grid)
{
	//当grid为空时,返回0
	if (grid.empty())
		return 0;
	//获得grid行数和列数
	int rows = grid.size();
	int cols = grid[0].size();
	//保存s[i-1]行中的元素
	vector<int> pre(cols, grid[0][0]);
	//保存s[i][j-1]中的元素
	int cur = grid[0][0];
	//初始化pre
	for (int i = 1; i < cols; ++i)
	{
		pre[i] = pre[i - 1] + grid[0][i];
	}
	//获得s[i][j]的最小值
	for (int i = 1; i < rows; ++i)
	{
		cur = grid[i][0] + pre[0];
		pre[0] = cur;
		for (int j = 1; j < cols; ++j)
		{
			cur = min(cur, pre[j]) + grid[i][j];
			pre[j] = cur;
		}
	}
	return pre[cols - 1];
}
方法五:上述四种方法都不要改变原矩阵中元素的值,当我们面试的时候,可以问一下面试官是否可以改变原矩阵中的元素,如果可以改变原矩阵中的值,我们可以再次提高一下空间效率,时间复杂度为o(n*m),空间复杂度为o(1)。每次计算s[i][j]时,我们把grid[i][j]更新为此值,因为再之后的访问过程中不需要再次访问grid[i][j]。
int minPathSum(vector<vector<int>>& grid)
{
	if (grid.empty())
		return 0;
	int rows = grid.size();
	int cols = grid[0].size();
	for (int i = 1; i < cols; ++i)
	{
		grid[0][i] = grid[0][i - 1] + grid[0][i];
	}
	for (int i = 1; i < rows; ++i)
	{
		grid[i][0] = grid[i - 1][0] + grid[i][0];
	}
	for (int i = 0; i < rows; ++i)
	{
		for (int j = 0; j < cols; ++j)
		{
			grid[i][j] = min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j];
		}
	}
	return grid[rows - 1][cols - 1];
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

矩阵上寻找最短路径 Minimum Path Sum

题目源自于leetcode。图问题。 题目:Given a m x n grid filled with non-negative numbers, find a path from top left...

数据结构.图.无向带权&邻接矩阵.最短路径Dijkstra算法

图的应用实在很广,课堂所学实为皮毛 考虑基于邻接矩阵的无向带权图,边的权值的典型意义就是路途的长度,从顶点u到顶点v的边权值为w,可以表示城市u到城市v之间路长为w。 最短路径问题考虑的就是从某个顶点...

第25章:每对顶点间的最短路径—基于矩阵乘法的动态规划算法

下面给出一个基于动态规划的算法,void graph::shortestWeightsOfAllPairs() { const int NOT_A_VERTEX=-1; ...

求任意大小矩阵两点之间的最短路径(回溯)

任意给定两个正半轴坐标点,求最短路径。 给定起点和终点,求最短路径,一共有八个方向
  • BTnode
  • BTnode
  • 2016-04-09 03:14
  • 1115

基于矩阵实现的最短路径算法

1.最短路径 图中最短路径是指:寻找图(由结点和路径组成的)中两结点之间的权重最小的路径。Wikipedia上最短路径(Shortest Path)的定义如下: In graph theory, t...

笔试面试算法经典--矩阵的最短路径和(Java)

题目 给定一个矩阵m,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有路径中最小的路径和。 例子: 给定m如下: 1 3 5 9 ...

如何用MyEclipse调试JAVA程序

1.在你觉得有错的地方设置断点2.点击甲壳虫一样的按钮下面的子按钮,也是甲壳虫一样的,叫Debug3.运行程序,当程序运行到刚才设置断点的位置就会停下来,并且那行代码底色会高亮显示。4.接着你在如下界...

spwm波

今天把spwm波的数据用MATLAB做出来了,但是还不是很好,从恢复sin波的波形来看,还有些许的畸变。过几天把spwm波的频域分析看看,希望对数据有所改善。

Spyder Ski Jacket The end of the ...

I like a feather, floating stumbled pulse in the beating,Spyder Ski Jacket, the heart is dead. colo...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)