leetcode977有序数组的平方
用暴力算法,先把所有数平方,再排序,在八种排序算法中,快速排序的时间复杂度为O(nlogn),采用快速排序比较好,C++的sort()函数是一种类似快排的方法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
for(int i = 0; i < n; i++)
nums[i] *= nums[i];
sort(nums.begin(), nums.end());
return nums;
}
};
双指针法
本题更好的方法是双指针法
解析里有一句话很关键,数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
这里复习了一下for语句格式:
- for 语句中的三个表达式可部分或全部省略,但两个分号不能省略。
- 常见的是:将表达式1放在了 for 语句之前,或表达式3放在了循环体中,在 for 语句中就可相应地省略表达式1或表达式3。
同时这里需要注意到i<=j还是i<j的问题
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(), 0);
int k = nums.size()-1;
for(int i = 0, j = nums.size()-1; i <= j;){ // i++和j++放在了循环体中 // 必须是i<=j,否则最后剩下的元素会处理不到
if(nums[i]*nums[i] < nums[j]*nums[j]){
result[k--] = nums[j]*nums[j];
j--;
}
else{
result[k--] = nums[i]*nums[i];
i++;
}
}
return result;
}
};
209长度最小的子数组
我的思路是这样的,第一层循环指定窗口大小,第二层循环指定窗口起始下标,第三层循环就是相加,结果,超时,三层循环,复杂度得有O(n^3)了吧,超时是应该的。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int minlen = 0;
for(int i = n; i > 0; --i) // 长度
{
for(int j = 0; j <= n-i; ++j) // 起始下标
{
int sum = 0;
for(int k = j; k <= j+i-1; ++k) // 求和
{
sum += nums[k];
}
if(sum >= target) minlen = i;
}
}
return minlen;
}
};
看了解析的暴力解法,发现我想的过于复杂了,两层循环的思路是,第一层循环指示起始位置,第二层循环是从起始位置开始加,只要加到值大于target就break,break之前比较子序列长度,如果此时子序列长度小于上一次的子序列长度,就更新,复杂度为O(n^2)。
不过暴力算法还是超时了,leetcode要求为O(n)或O(nlogn)
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX; // 最终结果
int sublength = 0; // 子序列长度
int sum = 0; // 子序列数值之和
for(int i = 0; i < nums.size(); ++i)
{
sum = 0;
for(int j = i; j < nums.size(); ++j)
{
sum += nums[j];
if(sum >= target)
{
sublength = j - i + 1;
result = result < sublength ? result : sublength;
break;
}
}
}
return result == INT32_MAX ? 0 : result;
}
};
滑动窗口
这是本题的重要解法,只需一个for循环,我猜想之所以里面有一个while循环但是最终复杂度为O(n)的原因是while其实只循环了一次,因为sum减去起始值后必定小于target,退出循环
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX;
int sum = 0;
int sublength = 0;
int i = 0; // 要先给出滑动窗口起始位置,后面才好算窗口大小并且改变起始位置
for(int j = 0; j < nums.size(); ++j){
sum += nums[j];
while(sum >= target){
sublength = j - i + 1;
result = result < sublength ? result : sublength;
sum -= nums[i++]; // 这里是两个语句,先让sum减去起始值,再让起始位置后移
}
}
return result == INT32_MAX ? 0 : result;
}
};
59螺旋矩阵||
上图是我的分析过程,然后写出了一个不忍直视的代码,并且结果执行出错
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int k = n;
int r = 1, c = 0; // row=1代表行数递增,col=0代表列数递减
int row = 0, col = 0;
int num = 1; // 第一个存入的数字
vector<vector<int>> nums(n, vector<int>(n));
while(k > 0){
if(k == n){
for(int i = 0; i < k; i++){
nums[0][col] = num++; // 每存一个数字,num加一
col++;
}
col--;
k--;
}
else{
if(row == 1){
for(int i = 0; i < k; i++){
row++;
nums[row][col] = num++;
}
}
else{
for(int i = 0; i < k; i++){
row--;
nums[row][col] = num++;
}
}
if(col == 1){
for(int i = 0; i < k; i++){
col++;
nums[row][col] = num++;
}
}
else{
for(int i = 0; i < k; i++)
col--;
nums[row][col] = num++;
}
k--;
}
}
return nums;
}
};
看了一下解析,我的思路是每个循环填充一个行一个列,解析里面是每个循环填充一下四周,我想我的代码或许还可以再改改,今天来不及了,等周末查漏补缺补上。
看了解析,我发现一定要注意左右闭开的问题,解析里采用的是左闭右开,如图
代码如下
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> nums(n, vector<int>(n, 0));
int startx = 0, starty = 0; // 每一圈的起始位置
int loop = n/2; // 圈数
int mid = n/2; // 矩阵最中间的位置
int count = 1; // 每次填充的数字
int offset = 1; // 限制每一圈每个边的长度
int i, j;
while(loop--){
i = startx;
j = starty;
// 每次填充都是左闭右开
for(j = starty; j < n-offset; j++){
nums[startx][j] = count++; // 这里也可以写nums[i][j]
}
for(i = startx; i < n-offset; i++){
nums[i][j] = count++; //比如上一个for循环最后一个是nums[0][n-2],因为左闭右开,nums[0][n-1]是这里才填充上的
}
for(; j > starty; j--){
nums[i][j] = count++;
}
for(; i > startx; i--){
nums[i][j] = count++;
}
// 每一圈的起始都在对角线上
startx++;
starty++;
// 控制每一圈里每一条边遍历的长度
offset++;
}
// 当n为奇数,需要单独个矩阵最中间的位置赋值
if(n % 2){
nums[mid][mid] = count;
}
return nums;
}
};