【算法与数据结构】62、LeetCode不同路径

文章目录

所有的LeetCode题解索引,可以看这篇文章------【算法和数据结构】LeetCode题解

一、题目

二、解法

2.1 动态规划解法

  思路分析:机器人只能向下或者向右移动,那么到达(i,j)位置的路径和(i-1,j)以及(i,j-1)有关。那么我们就得到的动态规划的表达式 d p i j = d p i − 1 j + d p i j − 1 dpij=dpi-1j+dpij-1 dpij=dpi−1j+dpij−1。其中,因为到达第一行和第一列位置的路径只有一条,因此dp数组中第一行第一列的元素都为1。根据如上信息,我们写出如下代码。

  程序如下:

cpp 复制代码
class Solution {
public:
	int uniquePaths(int m, int n) {
		vector<vector<int>> dp(m, vector<int>(n, 1));
		for (int i = 1; i < m; i++) {
			for (int j = 1; j < n; j++) {
				dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
			}
		}
		return dp[m - 1][n - 1];
	}
};

复杂度分析:

  • 时间复杂度: O ( m ∗ n ) O(m*n) O(m∗n)。
  • 空间复杂度: O ( m ∗ n ) O(m*n) O(m∗n)。

  上述代码还可以再空间上进行压缩。从二维数组的角度来看,(i,j)位置的路径数等于它上方的元素和左边的元素之和。如果省略掉上方的元素,我们就能用一个一维数组来表示dp数组。迭代公式为 d p i = d p i + d o p i − 1 dpi = dpi+dopi-1 dpi=dpi+dopi−1,其中dopi-1代表左边元素,公式右边旧的dpi代表上方元素。最终输出为dpn-1

cpp 复制代码
class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<int> dp(n);
        for (int i = 0; i < n; i++) dp[i] = 1;
        for (int j = 1; j < m; j++) {
            for (int i = 1; i < n; i++) {
                dp[i] += dp[i - 1];
            }
        }
        return dp[n - 1];
    }
};

复杂度分析:

  • 时间复杂度: O ( m ∗ n ) O(m*n) O(m∗n)。
  • 空间复杂度: O ( n ) O(n) O(n)。

2.2 数论解法

  思路分析:从数学上我们可以知道,要到达终点,每次又只能走一步,那么总共需要的步数是 m + n − 2 m+n-2 m+n−2,那么有 m − 1 m-1 m−1步是要往下走的,那么问题就变成了在 m + n − 2 m+n-2 m+n−2步中, m − 1 m-1 m−1步往下走有多少种组合。这是一个组合问题。因此,问题变成计算 C m + n − 2 m − 1 = ( m + n − 2 ) ! ( m − 1 ) ! ( n − 1 ) ! = ( m + n − 2 ) ∗ . . . ( n + 1 ) ∗ n ( m − 1 ) ! C_{m+n-2}^{m-1}=\frac{(m+n-2)!}{{(m-1)!}{(n-1)!}}=\frac{(m+n-2)*...(n+1)*n}{(m-1)!} Cm+n−2m−1=(m−1)!(n−1)!(m+n−2)!=(m−1)!(m+n−2)∗...(n+1)∗n。结合上述讨论,我们写出如下代码。代码当中,为了防止乘积中分子溢出,我们首先使用long long类型,并在循环中不断除以分母。

  程序如下:

cpp 复制代码
class Solution {
public:
	int uniquePaths(int m, int n) {
		long long numerator = 1; // 分子
		int denominator = m - 1; // 分母
		int num1 = m - 1, num2 = m + n - 2;
		while (num1--) {
			numerator *= (num2--);
			while (denominator != 0 && numerator % denominator == 0) {	// 分母不能为0, 且分子要能整除分母
				numerator /= denominator;
				denominator--;
			}
		}
		return numerator;
	}
};

复杂度分析:

  • 时间复杂度: O ( m ) O(m) O(m)。
  • 空间复杂度: O ( 1 ) O(1) O(1)。

三、完整代码

cpp 复制代码
# include <iostream>
# include <vector>
using namespace std;

// 62、不同路径I
class Solution {
public:
	int uniquePaths(int m, int n) {
		vector<vector<int>> dp(m, vector<int>(n, 1));
		for (int i = 1; i < m; i++) {
			for (int j = 1; j < n; j++) {
				dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
			}
		}
		return dp[m - 1][n - 1];
	}
};

// 滚动数组削减空间复杂度
//class Solution {
//public:
//	int uniquePaths(int m, int n) {
//		vector<int> dp(n);
//		for (int i = 0; i < n; i++) dp[i] = 1;
//		for (int j = 1; j < m; j++) {
//			for (int i = 1; i < n; i++) {
//				dp[i] += dp[i - 1];
//			}
//		}
//		return dp[n - 1];
//	}
//};

// 数论方法
//class Solution {
//public:
//	int uniquePaths(int m, int n) {
//		long long numerator = 1; // 分子
//		int denominator = m - 1; // 分母
//		int num1 = m - 1, num2 = m + n - 2;
//		while (num1--) {
//			numerator *= (num2--);
//			while (denominator != 0 && numerator % denominator == 0) {	// 分母不能为0, 且分子要能整除分母
//				numerator /= denominator;
//				denominator--;
//			}
//		}
//		return numerator;
//	}
//};

int main() {
	int m = 3, n = 2;
	Solution s1;
	int result = s1.uniquePaths(m, n);
	cout << result << endl;
	system("pause");
	return 0;
}

end

相关推荐
用户4978630507311 小时前
(一)小红的数组操作
算法·编程语言
怕浪猫13 小时前
Electron 系列文章封面图
算法·架构·前端框架
徐小夕16 小时前
JitWord 3.0 正式发布,高精度Word异构解析+复杂组件兼容,打造web端协同Word编辑器
前端·vue.js·算法
通信小呆呆1 天前
当算法有了“五感”:多模态数据融合如何向人体感官协同学习?
人工智能·学习·算法·机器学习·机器人
benben0441 天前
强化学习之DQN算法族(基于gymnasium开发)
算法
何以解忧,唯有..1 天前
Go语言循环语句详解:for、range与循环控制
开发语言·算法·golang
想吃火锅10051 天前
【leetcode】88.合并两个有序数组js
算法
生成论实验室1 天前
机器人:一个自主运动的系统
人工智能·算法·语言模型·机器人·自动驾驶·agi·安全架构
Qres8211 天前
算法复键——树状数组
数据结构·算法