题目描述(1)
118. 杨辉三角
给定一个非负整数 numRows
,生成「杨辉三角」的前 numRows
行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:
输入: numRows = 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
示例 2:
输入: numRows = 1 输出: [[1]]
提示:
-
1 <= numRows <= 30
相关知识点
动态规划(Dynamic Programming,简称 DP)是一种解决复杂问题的优化方法,通过将问题拆解为一系列子问题,解决每个子问题,并保存其解,以避免重复计算。动态规划主要适用于具有重叠子问题和最优子结构性质的问题。以下是动态规划算法的主要特征和步骤:
-
重叠子问题(Overlapping Subproblems):原始问题可以被分解为若干个重叠的子问题。通过解决每个子问题一次,并将其解存储在表格中,可以避免对同一子问题的重复计算。
-
最优子结构(Optimal Substructure):原始问题的最优解可以通过子问题的最优解来构建。这意味着可以通过解决子问题,逐步构建出整个问题的最优解。
-
状态转移方程(State Transition Equation):对于给定问题,需要确定它的状态以及各个状态之间的关系。状态转移方程描述了问题的状态如何从一个阶段转移到下一个阶段。
-
自底向上或自顶向下的解决方式:
-
自底向上(Bottom-Up):先解决最小的子问题,然后逐步解决更大的问题,直至解决原始问题。
-
自顶向下(Top-Down):从原始问题开始,逐步分解为子问题,然后解决子问题,直至解决最小的子问题。
-
-
记忆化(Memoization):在解决子问题时,将已经计算的结果保存在数据结构中,以便后续直接使用,避免重复计算。
动态规划经常应用于解决以下类型的问题:
-
最优化问题:如最短路径、最长子序列等。
-
计数问题:如组合数、排列数等。
-
决策问题:如背包问题、切割问题等。
一个经典的动态规划问题是斐波那契数列,其状态转移方程为 dp[i] = dp[i-1] + dp[i-2]
,其中 dp[i]
表示第 i
个斐波那契数。通过计算并保存每个子问题的解,可以避免重复计算,提高效率。
解题结果
Java
通过外层循环控制行数,内层循环控制每一行的元素生成。对于每一行的元素,判断是否为该行的第一个或最后一个,如果是,直接添加1,否则,通过上一行对应位置和前一个位置的数字之和得到当前位置的数字。将每一行的列表添加到最终结果列表中,最终得到杨辉三角的列表形式。
这个算法的时间复杂度为 O(numRows^2),其中 numRows 是要生成的杨辉三角的行数。
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < numRows; i++) {
List<Integer> row = new ArrayList<>();
for (int j = 0; j <= i; j++) {
if (j == 0 || j == i) {
row.add(1);
} else {
// 杨辉三角的每个数字是上一行对应位置和前一个位置的数字之和
row.add(result.get(i - 1).get(j - 1) + result.get(i - 1).get(j));
}
}
result.add(row);
}
return result;
}
}
C语言
通过动态分配内存,创建一个二维数组,其中每一行的列数即为行数。对于每一行的元素,将首尾元素设置为1,并通过上一行对应位置和前一个位置的数字之和计算得到中间位置的数字。通过指针返回结果数组的大小和每行的列数。
这个算法的时间复杂度为 O(numRows^2),其中 numRows 是要生成的杨辉三角的行数。记得在使用完结果后释放动态分配的内存。
int** generate(int numRows, int* returnSize, int** returnColumnSizes) {
// 初始化返回结果的变量
int** result = NULL;
*returnSize = numRows;
// 分配存储每个结果的内存
result = (int**)malloc(sizeof(int*) * numRows);
*returnColumnSizes = (int*)malloc(sizeof(int) * numRows);
for (int i = 0; i < numRows; i++) {
(*returnColumnSizes)[i] = i + 1; // 每行的列数即为行数
result[i] = (int*)malloc(sizeof(int) * (i + 1));
result[i][0] = result[i][i] = 1; // 每行的第一个和最后一个元素为1
// 计算每行的其他元素
for (int j = 1; j < i; j++) {
result[i][j] = result[i - 1][j - 1] + result[i - 1][j];
}
}
return result;
}
C++
通过循环生成杨辉三角的每一行,对于每一行的元素,将首尾元素设置为1,并通过上一行对应位置和前一个位置的数字之和计算得到中间位置的数字。将每一行的向量添加到最终结果向量中,最终得到杨辉三角的二维向量形式。
这个算法的时间复杂度为 O(numRows^2),其中 numRows 是要生成的杨辉三角的行数。
class Solution {
public:
std::vector<std::vector<int>> generate(int numRows) {
std::vector<std::vector<int>> result;
for (int i = 0; i < numRows; i++) {
std::vector<int> row(i + 1, 1);
for (int j = 1; j < i; j++) {
row[j] = result[i - 1][j - 1] + result[i - 1][j];
}
result.push_back(row);
}
return result;
}
};
Python
通过循环生成杨辉三角的每一行,对于每一行的元素,将首尾元素设置为1,并通过上一行对应位置和前一个位置的数字之和计算得到中间位置的数字。将每一行的列表添加到最终结果列表中,最终得到杨辉三角的二维列表形式。
这个算法的时间复杂度为 O(numRows^2),其中 numRows 是要生成的杨辉三角的行数。
class Solution:
def generate(self, numRows: int) -> List[List[int]]:
result = []
for i in range(numRows):
row = [1] * (i + 1)
for j in range(1, i):
row[j] = result[i - 1][j - 1] + result[i - 1][j]
result.append(row)
return result
题目描述(2)
977. 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]
提示:
-
1 <= nums.length <= 104
-
-104 <= nums[i] <= 104
-
nums
已按 非递减顺序 排序
相关知识点
双指针算法是一种常用于数组或链表等数据结构的技巧,它涉及到使用两个指针来解决问题。这两个指针可以同时遍历数组,也可以分别位于数组的不同位置,根据问题的需要而变化。以下是双指针算法的一些常见应用和解释:
-
Two Pointers (同向双指针):
-
应用场景:两个指针同时从数组的同一端移动,通常用于找到满足某个条件的一对元素。
-
例子:有序数组中找到两个元素使它们的和等于目标值。
-
-
Two Pointers with Different Speed (快慢指针):
-
应用场景:两个指针从数组的同一端出发,但移动的速度不同,通常用于检测循环或查找中间节点等。
-
例子:判断链表是否有环,找到链表的中间节点。
-
-
Two Pointers on Different Ends (对撞指针):
-
应用场景:两个指针分别位于数组的两端,向中间移动,通常用于在有序数组中查找一对元素。
-
例子:在有序数组中找到两个元素使它们的和等于目标值。
-
-
Sliding Window (滑动窗口):
-
应用场景:通过维护一个窗口,在数组或字符串上滑动两个指针,通常用于找到符合条件的子数组或子字符串。
-
例子:找到数组中和为定值的最短连续子数组。
-
-
Two Pointers for Merge (合并有序数组):
-
应用场景:两个有序数组合并成一个有序数组,通常用于对两个有序列表或数组进行合并。
-
例子:将两个有序数组合并为一个有序数组。
-
在这个具体的问题中,通过维护左右两个指针,分别指向有序数组的两端,比较左右两端元素的绝对值大小,选择较大的平方值放入结果列表,并移动相应的指针,直到两指针相遇。这样可以保证结果列表按照非递减顺序排列。
解题结果
Java
通过双指针方法,初始化左右两端指针,比较左右两端的平方值,将较大的平方值放在结果数组的最后,然后更新相应指针和结果数组的索引。最终得到按非递减顺序排列的平方数组。
这个算法的时间复杂度为 O(n),其中 n 是数组的长度。
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int[] result = new int[n];
int left = 0, right = n - 1, index = n - 1;
while (left <= right) {
int leftSquare = nums[left] * nums[left];
int rightSquare = nums[right] * nums[right];
if (leftSquare > rightSquare) {
result[index] = leftSquare;
left++;
} else {
result[index] = rightSquare;
right--;
}
index--;
}
return result;
}
}
C语言
这个算法的时间复杂度为 O(n),其中 n 是数组的长度。
int* sortedSquares(int* nums, int numsSize, int* returnSize) {
int* result = (int*)malloc(sizeof(int) * numsSize);
*returnSize = numsSize;
int left = 0, right = numsSize - 1, index = numsSize - 1;
while (left <= right) {
int leftSquare = nums[left] * nums[left];
int rightSquare = nums[right] * nums[right];
if (leftSquare > rightSquare) {
result[index] = leftSquare;
left++;
} else {
result[index] = rightSquare;
right--;
}
index--;
}
return result;
}
C++
这个算法的时间复杂度为 O(n),其中 n 是向量的长度。
class Solution {
public:
std::vector<int> sortedSquares(std::vector<int>& nums) {
int n = nums.size();
std::vector<int> result(n);
int left = 0, right = n - 1, index = n - 1;
while (left <= right) {
int leftSquare = nums[left] * nums[left];
int rightSquare = nums[right] * nums[right];
if (leftSquare > rightSquare) {
result[index] = leftSquare;
left++;
} else {
result[index] = rightSquare;
right--;
}
index--;
}
return result;
}
};
Python
通过维护左右两个指针,分别指向数组的两端,比较左右两端元素的绝对值大小,将较大的平方值加入结果列表,然后移动指针。循环直到左右指针相遇,最后将最后一个元素的平方值加入结果列表。最终得到按非递减顺序排列的平方列表,并通过反转结果列表得到正确顺序。
这个算法的时间复杂度为 O(n),其中 n 是列表的长度。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
left = 0
right = len(nums) - 1
res = []
while left < right:
if abs(nums[left]) >= abs(nums[right]):
res.append(nums[left] ** 2)
left += 1
else:
res.append(nums[right] ** 2)
right -= 1
res.append(nums[right] ** 2)
return res[::-1]