Постановка задачи
Учитывая целое число numRows, вернуть первые numRows треугольника Паскаля.
В треугольнике Паскаля каждое число представляет собой сумму двух чисел непосредственно над ним, как показано на рисунке:
Постановка задачи взята с: https://leetcode.com/problems/pascals-triangle
Пример 1:
Input: numRows = 5
Output: [ [1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1] ]
Пример 2:
Input: numRows = 1
Output: [[1]]
Ограничения:
- 1 <= numRows <= 30
Объяснение
Подход грубой силы
Простой метод состоит в том, чтобы запустить два цикла и вычислить значение биноминального коэффициента во внутреннем цикле.
Например, в первой строке 1, во второй строке 1 1, в третьей строке 1 2 1 и т. д. . Каждая запись в строке является значением биномиального коэффициента. Значение i-й записи в строке с номером строки равно C(line, i). Значение можно рассчитать по следующей формуле.
C(line, i) = line! / ( (line-i)! * i! )
Небольшой фрагмент C++ приведенной выше логики:
void printPascal(int n)
{
for (int line = 0; line < n; line++){
for (int i = 0; i <= line; i++)
cout <<" "<< binomialCoefficient(line, i);
cout <<"\n";
}
}
int binomialCoefficient(int n, int k)
{
int result = 1;
if (k > n - k)
k = n - k;
for (int i = 0; i < k; ++i){
result *= (n - i);
result /= (i + 1);
}
return result;
}
Поскольку мы генерируем коэффициент для каждой итерации, временная сложность вышеуказанной задачи составляет O(N³).
Оптимизированное решение (O(N²) времени и O(N²) дополнительного пространства)
Если мы посмотрим на треугольник Паскаля, то увидим, что каждая запись представляет собой сумму двух значений над ней. Итак, мы создали двумерный массив, в котором хранятся ранее сгенерированные значения.
Небольшой фрагмент C++ приведенной выше логики:
for (int line = 0; line < n; line++) {
for (int i = 0; i <= line; i++) {
if (line == i || i == 0)
arr[line][i] = 1;
else
arr[line][i] = arr[line - 1][i - 1] + arr[line - 1][i];
cout << arr[line][i] << " ";
}
cout << "\n";
}
Оптимизированное решение (O(N²) времени и O(1) дополнительного места)
Этот подход основан на подходе грубой силы. Биномиальный коэффициент i записи может быть представлен как C(line, i), и все строки начинаются со значения 1. Идея здесь состоит в том, чтобы вычислить C(line , i) с помощью C(line, i — 1). Его можно рассчитать за время O(1), используя следующее.
C(line, i) = line! / ( (line - i)! * i! )
C(line, i - 1) = line! / ( (line - i + 1)! * (i - 1)! )
So using the above approach we can change the formula as below:
C(line, i) = C(line, i - 1) * (line - i + 1) / i
C(line, i) can be calculated from C(line, i - 1) in O(1) time.
Проверим алгоритм:
- initialize vector<vector<int>> result
- loop for line = 1; line <= n; line++
- initialize vector<int> temp
- set C = 1
- loop for i = 1; i <= line; i++
- temp.push_back(C)
- C = C * (line - i) / i
- result.push_back(temp)
- return result
Решение C++
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> result;
for (int line = 1; line <= numRows; line++){
vector<int> temp;
int C = 1;
for (int i = 1; i <= line; i++){
temp.push_back(C);
C = C * (line - i) / i;
}
result.push_back(temp);
}
return result;
}
};
Решение Golang
func generate(numRows int) [][]int {
var result [][]int
for line := 1; line <= numRows; line++ {
var temp []int
C := 1
for i := 1; i <= line; i++ {
temp = append(temp, C);
C = C * (line - i) / i;
}
result = append(result, temp)
}
return result
}
Решение для JavaScript
var generate = function(numRows) {
var result = [];
for(let line = 1; line <= numRows; line++){
var temp = [];
let C = 1;
for(let i = 1; i <= line; i++){
temp.push(C);
C = C * (line - i) / i;
}
result.push(temp);
}
return result;
};
Давайте запустим наш алгоритм в пробном режиме, чтобы увидеть, как работает решение.
Input: numRows = 3
Step 1: initialize vector<vector<int>> result
Step 2: loop for line = 1; line <= numRows
1 <= 3
true
initialize vector<int> temp
C = 1
loop for i = 1; i <= line
1 <= 1
true
temp.push_back(C);
temp = [1]
C = C * (line - i) / i;
C = 1 * (1 - 1) / 1
C = 0
i++
i = 2
loop for i <= line
2 <= 1
false
result.push_back(temp)
result = [[1]]
line++
line = 2
Step 3: loop for line <= numRows
2 <= 3
true
initialize vector<int> temp
C = 1
loop for i = 1; i <= line
1 <= 2
true
temp.push_back(C);
temp = [1]
C = C * (line - i) / i
C = 1 * (2 - 1) / 1
C = 1 * 1 / 1
i++
i = 2
loop for i <= line
2 <= 2
true
loop for i <= line
2 <= 2
true
temp.push_back(C);
temp = [1, 1]
C = C * (line - i) / i
C = 1 * (2 - 2) / 1
C = 1 * 0 / 1
C = 0
i++
i = 3
loop for i <= line
3 <= 2
false
result.push_back(temp)
result = [[1], [1, 1]]
line++
line = 3
Step 4: loop for line <= numRows
3 <= 3
true
initialize vector<int> temp
C = 1
loop for i = 1; i <= line
1 <= 3
true
temp.push_back(C);
temp = [1]
C = C * (line - i) / i
C = 1 * (3 - 1) / 1
C = 1 * 2 / 1
C = 2
i++
i = 2
loop for i <= line
2 <= 3
true
temp.push_back(C);
temp = [1, 2]
C = C * (line - i) / i
C = 2 * (3 - 2) / 2
C = 2 * 1 / 2
C = 1
i++
i = 3
loop for i <= line
3 <= 3
true
temp.push_back(C);
temp = [1, 2, 1]
C = C * (line - i) / i
C = 1 * (3 - 3) / 3
C = 1 * 0 / 3
C = 0
i++
i = 4
loop for i <= line
4 <= 3
false
result.push_back(temp)
result = [[1], [1, 1], [1, 2, 1]]
line++
line = 4
Step 5: loop for line <= numRows
4 <= 3
false
Step 6: return result
So the result is [[1], [1, 1], [1, 2, 1]].
Первоначально опубликовано на https://alkeshghorpade.me.