最重要的一点就是杨辉三角的每个数字等于上一行的左右两个数字之和,即第 n 行的第 i 个数等于 第 n - 1 行的第 i - 1 个数 + 第 n - 1 行第 i 个数。如第 3 行的第 2 个数 = 第 2 行的第 1 个数 + 第 2 行的第 2 个数。这个特点也是我们代码中使用的一个公式。
题目描述:给定一个非负整数 numRows
,生成杨辉三角的前 numRows
行。
示例:
输入: numRows = 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
思路分析:如上图所示,红色单元格的值全部为 1,这是我们需要在代码中额外处理的,即每一行的第一个元素和最后一个元素的值为 1。图中绿色单元格标记的地方就是需要我们利用公式计算的。由于第一行只有一个元素且找不到它的上一行,所以我们对第一行的数值特殊处理,从第二行开始,就可以使用公式进行处理了。整个代码的逻辑遵循以下公式:
- F(n) = [1] n == 1
- F(n, w) = F(n - 1, w - 1) + F(n - 1, w) n >= 2,n - 2 >= w >= 1
其中 n 代表第几行,w 代表第几个元素。公式二中 w >= 1 且 w <= n - 2 的原因是:第 n 行有 n 个元素,下标范围是 [0, n - 1],而 F(n, 0) 和 F(n, n - 1) 就是图中使用红色单元格标记的、需要设置默认值 1 的地方,那么剩下的范围 [1, n - 2] 才是需要使用公式的地方。
完整代码如下:
public class Generate {
public static List<List<Integer>> generate(int numRows) {
List<List<Integer>> resultList = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
// 第一行只有一个元素 [1]
if (numRows == 1) {
list1.add(1);
resultList.add(list1);
}
// 从第二行开始使用杨辉三角的公式,即每个数是它左上方和右上方的数之和
if (numRows >= 2) {
// 第一行的值需要首先 add 到 resultList 中
list1.add(1);
resultList.add(list1);
System.out.println(resultList);
// i 用来循环需要输出多少行,因为 i = 0 (即第一行)已经计算,所以此处的循环范围是 [1, numRows - 1]
for (int i = 1; i < numRows; i++) {
// 内层循环用变量 j 来控制每行需要输出多少个
List<Integer> tempList = new ArrayList<>(numRows);
// 设置第一个元素(即 j = 0 时) = 1
tempList.add(1);
// 因为已经设置了 j = 0 时的值,所以此处 j 的循环范围是 [1, i - 2]
for (int j = 1; j < i; j++) {
// 使用公式进行计算
// 当 i == 1(即第二行) 时,其实是没有用到此公式的
tempList.add(resultList.get(i - 1).get(j - 1) + resultList.get(i - 1).get(j));
}
// 最后一个元素(即 j = i - 1 时) = 1
tempList.add(1);
resultList.add(tempList);
// 输出每一行的值
System.out.println(resultList);
}
}
return resultList;
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
List<List<Integer>> resultList = generate(10);
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
}
}
其实发现杨辉三角使用的公式和斐波那契数列使用到的公式几乎一样,所以此题也算作一个简单的动态规划。还有一种使用二维数组作为中间量的方法,基本上和上面的代码一致。
public static List<List<Integer>> generate2(int numRows) {
List<List<Integer>> resultList = new ArrayList<>();
// 二维数组
int[][] array = new int[numRows][numRows];
for (int i = 0; i < numRows; i++) {
List<Integer> tempList = new ArrayList<>();
// 以二维数组的对角线为界,为其下半部分赋值
for (int j = 0; j <= i; j++) {
if (i == j || j == 0) {
// 将对角线和第一列初始化为 1
array[i][j] = 1;
} else {
array[i][j] = array[i - 1][j - 1] + array[i - 1][j];
}
tempList.add(array[i][j]);
}
System.out.println(tempList);
resultList.add(tempList);
}
return resultList;
}
既然和斐波那契数列一样,那肯定也可以使用递归来解,但是耗时太长,不建议尝试。以下展示此问题的三种解法:generate() 是使用的 List<> 来开辟的空间,generate2() 是使用的 int[][] 来开辟的空间,generate3() 是使用的递归方法。完整代码和各个方法的耗时结果展示如下:
public class Generate {
public static List<List<Integer>> generate(int numRows) {
System.out.println("使用 List<> 开辟空间:");
List<List<Integer>> resultList = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
// 第一行只有一个元素 [1]
if (numRows == 1) {
list1.add(1);
resultList.add(list1);
}
// 从第二行开始使用杨辉三角的公式,即每个数是它左上方和右上方的数之和
if (numRows >= 2) {
// 第一行的值需要首先 add 到 resultList 中
list1.add(1);
resultList.add(list1);
// System.out.println(resultList);
// i 用来循环需要输出多少行,因为 i = 0 (即第一行)已经计算,所以此处的循环范围是 [1, numRows - 1]
for (int i = 1; i < numRows; i++) {
// 内层循环用变量 j 来控制每行需要输出多少个
List<Integer> tempList = new ArrayList<>(numRows);
// 设置第一个元素(即 j = 0 时) = 1
tempList.add(1);
// 因为已经设置了 j = 0 时的值,所以此处 j 的循环范围是 [1, i - 2]
for (int j = 1; j < i; j++) {
// 使用公式进行计算
// 当 i == 1(即第二行) 时,其实是没有用到此公式的
tempList.add(resultList.get(i - 1).get(j - 1) + resultList.get(i - 1).get(j));
}
// 最后一个元素(即 j = i - 1 时) = 1
tempList.add(1);
resultList.add(tempList);
// 输出每一行的值
// System.out.println(resultList);
}
}
System.out.println(resultList);
return resultList;
}
public static List<List<Integer>> generate2(int numRows) {
System.out.println("使用 int[][] 开辟空间:");
List<List<Integer>> resultList = new ArrayList<>();
// 二维数组
int[][] array = new int[numRows][numRows];
for (int i = 0; i < numRows; i++) {
List<Integer> tempList = new ArrayList<>();
// 以二维数组的对角线为界,为其下半部分赋值
for (int j = 0; j <= i; j++) {
if (i == j || j == 0) {
// 将对角线和第一列初始化为 1
array[i][j] = 1;
} else {
array[i][j] = array[i - 1][j - 1] + array[i - 1][j];
}
tempList.add(array[i][j]);
}
System.out.println(tempList);
resultList.add(tempList);
}
return resultList;
}
public static List<List<Integer>> generate3(int numRows) {
System.out.println("递归:");
List<List<Integer>> resultList = new ArrayList<>();
for (int i = 1; i <= numRows; i++) {
List<Integer> tempList = everyRows(i);
System.out.println(tempList);
resultList.add(tempList);
}
return resultList;
}
public static List<Integer> everyRows(int rows) {
List<Integer> tempList = new ArrayList<>();
if (rows == 1) {
tempList.add(1);
return tempList;
}
if (rows == 2){
tempList.add(1);
tempList.add(1);
return tempList;
}
for (int i = 0; i < rows; i++) {
if (i == 0 || i == rows - 1) {
tempList.add(1);
} else {
tempList.add(everyRows(rows - 1).get(i - 1) + everyRows(rows - 1).get(i));
}
}
return tempList;
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
List<List<Integer>> resultList = generate3(10);
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
}
}