这里借用leetcode的一道例题,来说一下动态规划和递归的区别
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
说明:
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
这种问题,一看就知道递归最简单了,但是这种递归方法其实就是一种暴力的全遍历破解,代码如下
//!递归函数
int recursive(vector<vector<int>>& triangle, int iVecIndex, int iInnerIndex)
{
if (iVecIndex == triangle.size()-1)
{
return triangle[iVecIndex][iInnerIndex];
}
return min(recursive(triangle, iVecIndex + 1, iInnerIndex),
recursive(triangle, iVecIndex+1, iInnerIndex + 1)) + triangle[iVecIndex][iInnerIndex];
}
//!调用主函数
int minimumTotalrec(vector<vector<int>>& triangle) {
int iVecSize = triangle.size();
if (iVecSize < 1 || triangle[0].size() < 1)
{
return 0;
}
return recursive(triangle, 0, 0);
}
递归的方式既简单,又容易理解,recursive参数为数组,一级数组的序列,二级数组中的序列。每调用一次就求得此位置到最终位置的最小和,终止条件为
递归条件为:sum(a[m][n]) = min(sum(a[m+1][n]), sum(a[m+1][n+1])) + a[m][n];
终止条件:n == triangle.size时(到达最底层时),直接返回此值。
当然递归带来的副作用很明显,效率太低,多次重复递归,
例如:
sum(a[4][2]) = min(sum(a[5][2]), sum(a[5][3)) + a[4][2];
sum(a[4][3]) = min(sum(a[5][3]), sum(a[5][4)) + a[4][3];
sum(a[4][4]) = min(sum(a[5][4]), sum(a[5][5)) + a[4][4];
上面时递归过程,很明显sum(a[5][3])和sum(a[5][4])重复递归两次,很明显我们递归了一些多余的信息。
这种情况如何去除冗余呢?
动态规划
动态规划应运而生,当子问题是父问题的解时,父问题由一个个子问题组成时,就用到了动态规划,与递归从顶到下的逻辑不同的时,动态规划是自底向上求解问题,每求得一个结果又可以组成上一级的结果,最终求得最优解,由于其自底向上,就保证了不会有多余的操作产生。那么下面就看一下解决此问题的动态规划算法:
//!动态规划
int minimumTotal(vector<vector<int>>& triangle) {
int iVecSize = triangle.size();
if (iVecSize < 1 || triangle[0].size() < 1)
{
return 0;
}
vector<int> vecToTal(iVecSize, 0);
vecToTal[0] = triangle[0][0];
//!正向规划
for (int iIndex = 1; iIndex < iVecSize; iIndex++)
{
int iInnerVecSize = triangle[iIndex].size();
//!初始值和最后值是固定的
vecToTal[iInnerVecSize -1] = vecToTal[iInnerVecSize - 2] + triangle[iIndex][iInnerVecSize - 1];
//!其余比较特殊
for (int iInnerIndex = iInnerVecSize - 2; iInnerIndex > 0; iInnerIndex--)
{
vecToTal[iInnerIndex] = min(vecToTal[iInnerIndex - 1], vecToTal[iInnerIndex])
+ triangle[iIndex][iInnerIndex];
}
vecToTal[0] += triangle[iIndex][0];
}
int iMinValue = vecToTal[0];
for (int iLastIndex = 1; iLastIndex < vecToTal.size(); iLastIndex++)
{
if (iMinValue > vecToTal[iLastIndex])
{
iMinValue = vecToTal[iLastIndex];
}
}
return iMinValue;
}
上述算法,每次迭代的对应着从第一行到第n行各个点的最小距离,而到第n+1行的距离又可以通过到第n行的最短距离求得,依次迭代,知道迭代到最后一行,此时就求的从第1行到最后一行的各个点最短距离,此时只要求出最小值就可以得出所求解。