977、有序数组的平方
双指针法
思路:平方最大值在左右两端,控制左右指针找绝对值最大,添加到定义好的数组当中。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int size = nums.size();
int l = 0;
int r = size - 1;
std::vector<int> res(size);
int n = size - 1;
while (l <= r){
int numl = std::abs(nums[l]);
int numr = std::abs(nums[r]);
if (numl > numr){
res[n] = nums[l]*nums[l];
l++;
}
else{
res[n] = nums[r]*nums[r];
r--;
}
n--;
}
return res;
}
};
暴力法(O(nlongn)),计算后排序。
class Solution {
public:
vector<int> sortedSquares(vector<int>& A) {
for (int i = 0; i < A.size(); i++) {
A[i] *= A[i];
}
sort(A.begin(), A.end()); // 快速排序
return A;
}
};
209、长度最小的子数串
对滑动窗口的误解:
以j为终止,以为每次i要从头开始,这样的复杂度确实是n的平方,时间过不了。
卡哥的方法,每次循环时,i和j都在不断向前推进。j利用for循环作为终止向前推进,i利用while循环,找到以j为终点时,当前满足大于等于目标的最小长度,遍历一遍之后即找到最小长度。
是否是贪心的思路?(gpt)
其实你提供的方案并没有明确的贪心策略,而是实现了滑动窗口(也称为双指针)的方法来解决问题。然而,这种方法也具有一定的贪心思想,因为它在找到符合条件的子数组时尽可能收缩窗口,试图找到最小长度。
贪心算法通常是通过在每一步选择满足剩余条件的最佳局部解决方案,从而试图达到全局最优解。对于最小长度子数组的问题,滑动窗口的方法也在某种程度上利用了一些贪心的策略,但它并不是纯粹的贪心算法。
下面我将简要说明你的滑动窗口方法是如何工作的:
- 初始化两个指针
i
和j
以表示当前窗口的左右边界,同时初始化sum
以表示窗口中元素的和。 - 外层循环移动右指针
j
,将每个新元素添加到sum
中。 - 内层循环当
sum
大于等于target
时,计算当前窗口的长度并尝试更新最小长度res
。然后通过移动左指针i
收缩窗口,并从sum
中减去nums[i]
。 - 重复上述步骤,直到右指针遍历完数组。
- 最后,检查
res
是否更新过,如果没有更新则返回 0,否则返回res
。
这种方法在每一步都尽可能缩短窗口长度,从而达到寻找最短子数组的目标,具备一定的贪心特性,但具体来说,它更像是滑动窗口的方法。
代码:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int res = nums.size() + 1;
int sum = 0;
int i = 0;
for (int j = 0;j<nums.size();j++){
sum += nums[j];
while (sum >= target){
int curl = j - i + 1;
if(res>curl){
res = curl;
}
sum -= nums[i];
i++;
}
}
if (res == nums.size()+1){
return 0;
}
else{
return res;
}
}
};
59.螺旋矩阵
模拟过程没想出来。
当然,这里是你代码的注释版本:
#include <vector>
#include <iostream>
class Solution {
public:
std::vector<std::vector<int>> generateMatrix(int n) {
// 初始化 n x n 矩阵,所有元素设置为 0
std::vector<std::vector<int>> res(n, std::vector<int>(n, 0));
int x = 0; // 初始化 x 坐标
int y = 0; // 初始化 y 坐标
int num = 1; // 初始化填入矩阵的数字
int loop = n / 2; // 完整的圈数(每一个圈代表一层外圈矩形)
int mid = n / 2; // 当 n 为奇数时,矩阵中心的索引
// 按层次填充外圈矩形
while (loop--) {
int i = x;
int j = y;
// 从左到右填充顶边
for (j; j < n - 1 - x; j++) {
res[i][j] = num++;
}
// 从上到下填充右边
for (i; i < n - 1 - x; i++) {
res[i][j] = num++;
}
// 从右到左填充底边
for (; j > x; j--) {
res[i][j] = num++;
}
// 从下到上填充左边
for (; i > x; i--) {
res[i][j] = num++;
}
// 更新 x 和 y 坐标,进入下一层矩形
x++;
y++;
}
// 如果 n 为奇数,填充矩阵的中心
if (n % 2) {
res[mid][mid] = num;
}
return res; // 返回最终生成的螺旋矩阵
}
};
int main() {
Solution sol;
int n = 3;
std::vector<std::vector<int>> matrix = sol.generateMatrix(n);
// 打印生成的矩阵(用于测试和验证)
for (const auto& row : matrix) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << std::endl;
}
return 0;
}
注释说明
- 初始化矩阵和变量值:
- 初始化
res
矩阵为 n x n,所有元素初始化为 0。 x
和y
分别代表当前层次矩形的左上角坐标。num
为当前填入的数字,初始值为 1。loop
为完整的圈数(每一圈代表一层外圈矩形)。mid
为当 n 为奇数时矩阵中心的索引。
- 初始化
- 填充矩阵:
- 外层 while 循环以
loop
次数为准,每执行一次循环填充一层矩矩阵。 - 四个 for 循环分别从左到右、从上到下、从右到左、从下到上填充当前矩形的四个边。
- 更新
x
和y
坐标以进入内层矩形。
- 外层 while 循环以
- 处理奇数矩阵中心:
- 当 n 为奇数时,填充矩阵的中心。
- 展示结果:
- 在
main
函数中测试该算法并打印生成的矩阵。
希望这些注释可以帮助你更好地理解代码的工作原理!
- 在
C++中数组的增删改查的实现,普通数组和vector使用上的区别。
(chatgpt回答)
好的,下面我们来总结一下在C++中数组(包括普通数组和std::vector
)的增删改查操作,并简要讨论它们的区别。
普通数组
普通数组在定义时需要指定大小(编译时确定),一旦定义,大小就不能改变。数组没有直接的方法支持增删元素,只能通过覆盖现有元素来修改内容。
增删改查操作
- 增:普通数组无法动态增加大小,只能在界限内赋值。
- 删:普通数组不能删除元素,只能覆盖或标记为空。
- 改:通过索引直接修改元素。
- 查:通过索引访问元素。
#include <iostream>
using namespace std;
void displayArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
const int SIZE = 5; // 数组大小固定
int arr[SIZE] = {1, 2, 3, 4, 5};
// 查(访问数据)
cout << "Array elements: ";
displayArray(arr, SIZE);
// 改(修改元素)
arr[2] = 10;
cout << "After modifying element at index 2: ";
displayArray(arr, SIZE);
return 0;
}
vector
std::vector
是C++标准模板库(STL)中的动态数组类,可以动态调整大小并提供了丰富的成员函数用于操作元素。
增删改查操作
- 增:使用
push_back
方法增加元素,使用insert
方法在指定位置插入元素。 - 删:使用
pop_back
方法删除最后一个元素,使用erase
方法删除任意位置的元素。 - 改:通过索引直接修改元素,或使用迭代器。
- 查:通过索引访问元素,或使用迭代器。
#include <iostream>
#include <vector>
using namespace std;
void displayVector(const vector<int>& vec) {
for (int num : vec) {
cout << num << " ";
}
cout << endl;
}
int main() {
vector<int> vec = {1, 2, 3, 4, 5};
// 查(访问数据)
cout << "Vector elements: ";
displayVector(vec);
// 增(添加元素)
vec.push_back(6);
cout << "After adding element at the end: ";
displayVector(vec);
// 改(修改元素)
vec[2] = 10;
cout << "After modifying element at index 2: ";
displayVector(vec);
// 删(删除元素)
vec.erase(vec.begin() + 1); // 删除索引为1的元素
cout << "After erasing element at index 1: ";
displayVector(vec);
return 0;
}
普通数组和vector的区别
大小:
1. 普通数组的大小在定义时确定,无法动态改变。
2. `std::vector`可以动态调整大小,支持扩展和缩减。
内存管理:
1. 普通数组内存管理较为手动,需要开发者自行管理,如需要复制或扩展数组时需要显式操作。
2. `std::vector`自动管理内存,内存增长和释放均由其内部实现,无需开发者操心。
功能:
1. 普通数组功能有限,只支持基本的元素访问和修改操作。
2. `std::vector`功能丰富,提供了一系列方法如`push_back`, `pop_back`, `insert`, `erase`等用于操作元素。
性能:
1. 普通数组性能固定,不涉及内存分配和释放开销。
2. `std::vector`由于需要动态分配和释放内存,可能会有额外的性能开销,但在现代C++库和编译器优化下,性能已相当不错。
异常处理:
1. 普通数组不提供内置的检查,越界访问会导致未定义行为。
2. `std::vector`提供了`at`方法,可以抛出异常进行边界检查。
总结
- 普通数组适用于大小固定且没有复杂增删需求的场景,优点是简单高效,但缺点是缺乏灵活性。
std::vector
适用于大小需要动态调整的场景,提供了丰富的操作方法,安全性和灵活性更高,是C++程序员更常用的选择。
希望这能帮助你理解在C++中数组和std::vector
的使用以及它们的区别!如果有任何进一步的问题,请随时告诉我。