引言
杨辉三角是计算机科学中一个经典的数学问题,广泛应用于组合数学和动态规划等领域。生成杨辉三角的过程看似简单,但在实际编码中,往往会遇到各种问题,尤其是当输入的 numRows
较大时,可能会遇到整数溢出的问题。本文将详细介绍如何通过调试和优化代码来解决这些问题,并最终生成正确的杨辉三角。
1. 问题描述
给定一个整数 numRows
,生成杨辉三角的前 numRows
行。杨辉三角的每一行的第一个和最后一个元素都是1,中间的元素是上一行相邻两个元素之和。
以 numRows = 5
为例,程序的输出结果为:
[
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1]
]
2. 初始代码与问题分析
根据多项式展开定理,用数学公式输出,主要考察二维数组的插入和输出。

int combination( int x,int y){
long long int num_1 =1;
if(y ==0 || y==x){
return 1;
}
int num_2 = 1;
for(int i =x;i>x-y;i--){
num_1 = num_1 * i;
num_1 = num_1/num_2;
num_2 ++;
}
return num_1 ;
}
对于主程序:
-
定义一个
generate
函数,该函数接受一个整数numRows
作为参数,表示要生成的行数。 -
函数内部首先检查
numRows
是否小于 1,如果是,则返回一个空的结果向量。 -
接着,使用一个循环遍历从 0 到
numRows - 1
的每一行。 -
在每一行中,如果是第一行(即
i == 0
),则将其初始化为{1}
并添加到结果中。 -
对于其他行,首先清空
hang
向量,然后通过另一个循环计算当前行的每个元素,这些元素是通过调用combination
函数计算得到的。combination
函数用于计算组合数,使用公式C(x, y) = x! / (y! * (x - y)!)
,返回组合数的值。
class Solution {
public:
vector<vector<int> > generate(int numRows) {
vector<vector<int> > result;
if(numRows < 1){
return result;
}
vector<int> hang;
for(int i=0; i<numRows;i++){
if (i ==0 ) {
hang = {1};
result.push_back(hang);
}else{
hang.clear();
for(int j=0;j<=i;j++){
long int a = combination(i,j);
hang.push_back(a);
}
result.push_back(hang);
}
}
return result;
}
int combination( int x,int y){
long long int num_1 =1;
if(y ==0 || y==x){
return 1;
}
int num_2 = 1;
for(int i =x;i>x-y;i--){
num_1 = num_1 * i;
num_1 = num_1/num_2;
num_2 ++;
}
return num_1 ;
}
};
语法/功能 | 代码示例 | 说明 |
---|---|---|
二维向量声明 | vector<vector<int> > result; | 声明一个二维整数向量 result ,用于存储生成的杨辉三角。 |
条件判断 | if(numRows < 1) | 如果 numRows 小于 1,直接返回空的 result 。 |
一维向量声明 | vector<int> hang; | 声明一个一维整数向量 hang ,用于存储每一行的数据。 |
向量初始化 | hang = {1}; | 初始化 hang 向量为 {1} 。 |
向量插入 | result.push_back(hang); | 将 hang 向量插入到 result 中。 |
循环结构 | for(int i=0; i<numRows;i++) | 使用 for 循环生成每一行的数据。 |
组合数计算 | long int a = combination(i,j); | 调用 combination 函数计算组合数。 |
组合数函数 | int combination( int x,int y) | 定义一个计算组合数的函数。 |
条件判断 | `if(y ==0 | |
循环结构 | for(int i =x;i>x-y;i--) | 使用 for 循环计算组合数。 |
变量声明 | long long int num_1 =1; | 声明一个长整型变量 num_1 并初始化为 1。 |
变量声明 | int num_2 = 1; | 声明一个整型变量 num_2 并初始化为 1。 |
数组插入的语法扩展
在C++中,vector
是一种动态数组,提供了多种插入元素的方法。以下是一些常用的插入语法:
-
push_back
方法:- 语法:
vector.push_back(value);
- 示例:
hang.push_back(a);
- 说明:将
value
插入到vector
的末尾。
- 语法:
-
insert
方法:- 语法:
vector.insert(position, value);
- 示例:
hang.insert(hang.begin() + 2, 5);
- 说明:在
position
位置插入value
。
- 语法:
在初始代码中,我们使用了组合数公式来计算杨辉三角中的每个元素。然而,当 numRows
较大时(例如 numRows = 20
),组合数的计算会导致整数溢出,即使使用了 long long int
也无法避免,阶乘爆炸对于计算机而言是常见的事情。
对于数组元素的插入,push_back还是insert
更多内容请参考:C++插入元素全攻略:字符串、数组、链表、队列,一文搞定![特殊字符]
3. 调试过程
相比较于算法的改进,如何通过debug
寻找到这个问题更为重要。
- 发现问题:当
numRows = 15
时,生成的杨辉三角出现了错误。最开始的combination
函数简单粗暴,为阶乘相乘除。不难发现,第15行数组显然不对称,且数据不对
。
-
稍微修改代码 :让循环直接从第十四行开始:
-
通过打断点调试:在关键地方打上断点,逐步运行,发现
num_1
变量溢出。
-
第一次优化:将
int
改为long int
,但问题依然存在,当numRows = 20
时,仍然出现溢出。 -
第二次优化:将
long int
改为long long int
,但问题依然没有解决。数值溢出导致返回的值a
不对,插入数组元素不符合预期,导致运行失败。
- 最终解决方案:通过优化组合数计算的算法,避免了直接计算大数组合数,从而解决了溢出问题。
int combination( int x,int y){
long long int num_1 =1;
if(y ==0 || y==x){
return 1;
}
int num_2 = 1;
for(int i =x;i>x-y;i--){
num_1 = num_1 * i;
num_1 = num_1/num_2;
num_2 ++;
}
return num_1 ;
}
4. 其他解法 —— 不依靠二项式公式
4.1 代码
vector<vector<int> > generate(int numRows)
{
vector<vector<int>> res;
if(numRows==0)
return res;
vector<int> first;
first.push_back(1);
res.push_back(first);
if(numRows==1)
return res;
for(int i=1; i<numRows; i++)
{
vector<int> tmp;
tmp.push_back(1);
for(int j=i-1; j>=1; j--)
tmp.push_back(res[i-1][j]+res[i-1][j-1]);
tmp.push_back(1);
res.push_back(tmp);
}
return res;
}
4.2 编程思路
-
初始化:
- 创建一个二维向量
res
用于存储最终的结果。 - 如果
numRows
为 0,直接返回空的res
。
- 创建一个二维向量
-
处理第一行:
- 创建一个一维向量
first
,并将1
插入其中。 - 将
first
插入到res
中。 - 如果
numRows
为 1,直接返回res
。
- 创建一个一维向量
-
生成后续行:
- 从第二行开始(
i = 1
),逐行生成杨辉三角。 - 对于每一行:
- 创建一个临时向量
tmp
,并在开头插入1
。 - 通过上一行的数据(
res[i-1]
)计算当前行的中间值,公式为:res[i-1][j] + res[i-1][j-1]
。 - 在
tmp
的末尾插入1
。 - 将
tmp
插入到res
中。
- 创建一个临时向量
- 从第二行开始(
-
返回结果:
- 最终返回生成的杨辉三角
res
。
- 最终返回生成的杨辉三角
语法/功能 | 代码示例 | 说明 |
---|---|---|
临时向量声明 | vector<int> tmp; | 声明一个临时一维向量 tmp ,用于存储当前行的数据。 |
向量插入 | tmp.push_back(1); | 将 1 插入到 tmp 的开头和末尾。 |
中间值计算 | tmp.push_back(res[i-1][j]+res[i-1][j-1]); | 通过上一行的数据计算当前行的中间值,并插入到 tmp 中。 |
二维向量插入 | res.push_back(tmp); | 将 tmp 插入到 res 中。 |
4.3 关键点解析
-
杨辉三角的性质:
- 每一行的第一个和最后一个元素都是
1
。 - 中间的元素是上一行相邻两个元素之和。
- 每一行的第一个和最后一个元素都是
-
向量的动态性:
vector
是动态数组,可以随时插入元素,非常适合用于存储杨辉三角的每一行。
-
循环与索引:
- 外层循环控制行数,内层循环控制每一行的元素计算。
- 通过
res[i-1]
访问上一行的数据,利用索引j
和j-1
计算当前行的值。
-
代码简洁性:
- 代码逻辑清晰,通过逐行生成的方式避免了复杂的数学计算,直接利用上一行的结果生成当前行。
这段代码通过逐行生成的方式高效地构建了杨辉三角,充分利用了 vector
的动态性和索引操作。代码逻辑清晰,适合初学者理解和掌握二维向量的基本操作以及杨辉三角的生成原理。
结论
通过本次调试和优化,我们不仅解决了杨辉三角生成中的溢出问题,还学习了如何通过优化算法来提高代码的效率和稳定性。希望本文能帮助你在日常编程中更好地应对类似的问题。
关注专栏:C++基础语法与操作指南,一起学习C++。
关注专栏:每日一题:C++ LeetCode精讲,我们一起进步
点个赞吧
