Day2|数组双指针、

DAY2积压了两天没发

977 有序数组的平方

一开始只能想到暴力解法,就是先算出所有平方再统一sort
但是这样的时间复杂度有O(NlogN)
(看了一下可以在原数组nums的基础上做修改,这样空间复杂度是O(1))

看了点题解————双指针法
原理就是绝对值最大的肯定在两端其中之一,所以能先找到最大的平方值。
但是看了一下要求输出从小到大,先找到最大的放在哪啊(这里卡了一下,还是菜,没忍住又看了眼题解)
可以在初始化的时候就给vector数组前size个空间置0(先开辟出这些空间,后面可以直接在这些空间上修改值)
明白了,这样空间复杂度是O(N),时间复杂度是O(N)
于是上代码

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int size=nums.size();
        int L=0;
        int R=size-1;
        int k=R;
        vector<int> result(size, 0);
        while(R>=L){
            if(abs(nums[L])>abs(nums[R])){
                result[k]=nums[L]*nums[L];
                ++L;
            }
            else{
                result[k]=nums[R]*nums[R];
                --R;
            }
            --k;
        }
        return result;
    }
};

第一次在while的判定条件里出了点小小的问题,没加等于号导致在相等的时候直接跳出循环,差一位数没存。

第二道题

209 长度最小的子数组

看见题目的第一思路,可以先用sort排个序然后从后往前找
然后就写出了如下的:(错的)

//这是错的
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int size=nums.size();
        int R=size-1;
        int L=R;
        int count=nums[R];
        while(count<target){
            --L;
            if(L<0) return 0;
            count+=nums[L];
        }
        return R-L+1;

    }
};

(第一次犯了两个特别特别低级的错误:忘写sort了,加上了以后发现位置放反了,干脆直接放在第一句了。)
除去上面两个低级错误,第一个过不去的测试用例出现了:

你倒是看看题啊

看了半天寻思这不是七个吗。。。啊原来题目说要连续的!排序完不再连续了!!

寸不己。。。于是重写。。。

首先想到依次以每个元素作为开头往后计数。。。时间复杂度可能不小
遂写暴力法:
C++:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int size=nums.size();
        int L;
        int R;
        int len=size;
        for(int i=0;i<size;++i){
            int count=nums[i];
            L=i;
            R=L;
            while(count<target){
                ++R;
                if(R>=size){
                    if(L==0) return 0;
                    return len;
                }
                count+=nums[R];
            }
            len=min(len,R-L+1);
            if(len==1) return len;
        }
        return len;
    }
};

蒽。。。时间复杂度很大的一坨,还有一些一时兴起添加的莫名其妙的跳出循环的条件,不过AC了
时间复杂度O(N^2)

看了官方题解,
解法二给出了前缀和+二分法时间复杂度O(NlogN)
遍历复杂度是O(N),二分查找复杂度O(logN)
解法三滑动窗口,时间复杂度O(N)
解法二第一遍没看懂,先来写解法三
设置一个窗口头start和窗口尾end,初始化都指向0位置
end向右移动,直到窗口内的和大于等于target,将start也一步步向右移动,如果窗口内的和小于target了,停止移动start,开始移动end。
记录过程中每次窗口内和大于等于target时的最小窗口长度。
上代码

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int size=nums.size();
        int start=0;
        int end=0;
        //int end=0;
        int sum=0;
        int len=size+1;
        while(end<size){
            sum+=nums[end];
            while(sum>=target){
                len=min(len,end-start+1);
                sum-=nums[start];
                ++start;
                
            }
            ++end;
        }
        return len==size+1 ? 0 : len;
    }
};

一开始想不出来第一个循环的判断条件应该是什么,总觉得应该同时考虑start和end的位置才能保证遍历的时候不遗漏。
看了一下题解发现可以保证当前窗口下的end不溢出就可以了,如果end溢出了,证明当前start的位置下(无论这个start在哪)都无法满足窗口内的sum大于target,就没必要再移动start了(因为start只能向右移动,sum只会越来越小)就可以跳出循环了。

一个小问题:

sum+=nums[end];

这个语句我一开始写的是

sum+=nums[end++];

执行完这个语句end自动加1,但是第二个while语句中还需要用到end加1之前的值计算len。
老老实实把++end;放最后吧。

来看解法二:前缀法+双指针
本来看到解法二感觉很不好想,时间复杂度也不如解法三,想放弃来着。翻评论区的时候看到一句话:连续数组问题通常就是前缀法和滑窗。行吧,那我把解法二也搞懂。
解法二首先发现了解法一中存在重复计算的问题:
于是把数组中每个位置对应的前缀和都算出来存在数组中,这样计算sum可以直接下标索引和不用遍历。
满足条件的一段数组可能是任何一个位置开头的数组,比如从a位置到b位置。想要计算这一段数组的sum可以直接取b的前缀和与a的前缀和相减,再与target比较。
就记作满足sum[b]-sum[a] >= target的最小a到b的长度。

然后 ,我们可以很容易得改良问题为 求s[j] - s[i] >=target ,可是这种做法如果不加改变,就是在前缀和数组上进行类似方法一的暴力枚举,枚举每一个i后面的下标j
最后,我们发现稍作变化,像这种线性的求值问题,联合二分查找可以做到 求 s[j] >=s[i]+target.


(以上来自评论区,感觉说的很准确)
而且C++中有库函数lower_bound可以直接进行二分查找。
因此对于sum中每一个sum[i]都可以将其视为sum[a],在整个sum中寻找第一个大于sum[i]+target.的sum[j],如果能找到,可以存j到i的最短长度,遍历结束后返回。

注意:end() 返回的迭代器指向容器末尾之外的位置,因此不能解引用它。在代码中,通常将 end() 与其他迭代器进行比较,以确定是否已遍历完整个容器。
也就是说判断迭代器返回的是否为sum.end()就能判断是否找到
如果能找到,将迭代器返回的与sum.begin()相减就可以得到j的值。
这里有一个确保代码安全的操作:
“迭代器之间的差返回的是一个名为 std::iterator_traits::difference_type 的类型,它可能与 int 不同。显式转换可以确保代码中的数据类型一致性。”
因此相减以后需要强制转换成int型。
static_cast((bound - sums.begin())) 是一种显式类型转换,它将迭代器之间的差转换为 int 类型。
没什么问题了,上代码!

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int size=nums.size();
        int len=size+1;
        vector<int> sum(size+1,0);
        sum[0]=0;
        for(int i=1;i<=size;++i){
            sum[i]=sum[i-1]+nums[i-1];
        }
        for(int i=1;i<=size;++i){
            int s=target+sum[i-1];
            auto it=lower_bound(sum.begin(),sum.end(),s);
            if(it!=sum.end()){
                len=min(len,static_cast<int>(it-sum.begin())-i+1);
            }
        }

        return len==size+1 ? 0 : len;

    }
};

需要注意的一些细节就是sum[0]中存0,表示前0个元素的和为0.

59.螺旋矩阵II

第一次写

想了半天没捋明白。。看题解吧

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> result(n,vector<int>(n,0));
        int x=0,y=0;
        int offset=1;
        int loop=n/2;
        int i,j;
        int count=1;
        while(loop--){
            for(i=y;i<n-offset;++i ){
                result[x][i]=count++;
            }
            for(j=x;j<n-offset;++j){
                result[j][i]=count++;
            }
            for( ;i>y;--i){
                result[j][i]=count++;
            }
            for( ;j>x;--j){
                result[j][y]=count++;
            }
            ++x;
            ++y;
            ++offset;
        }
        if(n%2!=0) result[n/2][n/2]=count;
        return result;
    }
};

这道题也没有很复杂的算法,就是在考察编码能力和逻辑
我和题解的思路差距在于,题解会有一个“找到循环不变量”的意识,而我只是一直在想每次循环情况都不一样,怎么写循环条件。
我一直想写出一个循环适用于每一条边,不管是从上到下还是从左到右从右到左,然而这样要考虑的因素就很复杂。
题解直接把每次绕圈的四条边都按顺序写出来了,很清晰。

还是菜就多练吧、、、不能再摆了

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
抱歉,由于时间限制和技术限制,我无法为您提供完整的代码。但是,我可以向您解释一下如何使用二维数组和指针数组来实现日历。 首先,我们需要一个二维数组来存储每个月的天数和每个月的第一天是星期几。例如,我们可以定义一个名为calendar的数组,它包含12行和2列。第一列存储每个月的天数,第二列存储每个月的第一天是星期几。 ```c int calendar[12][2] = { {31, 2}, // January {28, 5}, // February {31, 5}, // March {30, 1}, // April {31, 3}, // May {30, 6}, // June {31, 1}, // July {31, 4}, // August {30, 0}, // September {31, 2}, // October {30, 5}, // November {31, 0} // December }; ``` 接下来,我们需要一个指针数组来存储每个月的日历。我们可以定义一个名为month的指针数组,它包含12个指向int类型的指针。每个指针都指向一个包含每个月日历的二维数组。 ```c int* month[12]; ``` 然后,我们可以使用一个for循环来创建每个月的日历。在每个月的循环中,我们需要使用malloc函数动态分配一个二维数组,然后使用for循环填充日历。 ```c for (int i = 0; i < 12; i++) { int days = calendar[i][0]; int first_day = calendar[i][1]; int week_day = 0; month[i] = (int*)malloc(6 * 7 * sizeof(int)); for (int j = 0; j < 6 * 7; j++) { if (j < first_day || j >= first_day + days) { month[i][j] = 0; } else { month[i][j] = j - first_day + 1; } } } ``` 最后,我们可以使用另一个for循环来打印整个日历。在每个月的循环中,我们需要使用for循环打印日历的表头和每一天的日期。 ```c for (int i = 0; i < 12; i++) { printf("%d月\n", i + 1); printf("日 一 二 三 四 五 六\n"); int days = calendar[i][0]; int first_day = calendar[i][1]; int week_day = 0; for (int j = 0; j < 6 * 7; j++) { if (j < first_day || j >= first_day + days) { printf(" "); } else { printf("%2d ", month[i][j]); } week_day++; if (week_day == 7) { printf("\n"); week_day = 0; } } printf("\n"); } ``` 这就是如何使用二维数组和指针数组来实现日历的基本过程。当然,这只是一个简单的例子,您可以根据自己的需求和喜好进行修改和扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值