C语言代码随想录刷题计划 Day1

先附上地址方便对照着看:代码随想录

一、数组

1、数组元素不能删,只能覆盖,数据结构有提到存储空间连续问题。

2、二分查找:这一块其实可以看题目要求,如果题目仅仅要求找到该元素而不返回下标之类的话,可以使用bsearch,如果不是有序数组需要用qsort。关于这两个函数的具体参数可以在dev c++中ctrl+鼠标左键点击头文件进入,然后ctrl+F键搜索查看具体参数。

但是如果需要返回数组下标之类的就比较麻烦一些,首先需要看数组是否是有序数组,而且要注意是否存在重复元素,不然返回的下标不唯一,但通常这种题最多让你计数。

手撕代码如下:个人喜欢闭区间,写起来对称

int left = 0;
int right = numsSize-1; //这个numsSize是数组大小,是sizeof(nums)/sizeof(nums[0])
int middle = 0;
while(left <= right){
    middle = (left + right)/2;
    if(nums[middle] > target)
        right = middle-1;
    else if(nums[middle] < target)
        left = middle+1;
    else if(nums[middle] == target)
        return middle;
}

3、移除元素:暴力解法基本都能想到,快慢指针法确实想不到,还是思路没打开

快慢指针重点在于如何出现快慢并满足题目要求,分步骤分析:

step 1:设置双指针,一起向数组尾部移动

step 2:找到需要删除的元素,快指针后移,慢指针不动

step 3:快指针指向的元素覆盖慢指针元素,直至到达尾部

作者在这里用了很巧妙的思路:

1、快指针一直覆盖慢指针的元素:没有找到待删除元素时,两指针指向元素相同,覆盖也不会有影响。

2、当找到该元素时,慢指针停住如何实现?观察可得:快指针不受影响一直移动,而慢指针只需要在待删除元素那里停住,用if语句即可

于是核心就出来了:注意if判断就好

for(int fastIndex=0;fastIndex<numsSize;fastIndex++){
    if(nums[fastIndex]!= val) //写slowIndex不行,因为slowIndex会一直停住,只有fastIndex才行
        nums[slowIndex++] = nums[fastIndex];
}

4、有序数组的平方:确实想到的是暴力解法,没好好看题

题目中有个很重要的条件:非递减顺序,这个就隐含了重要性质:数组两端的元素平方之后的值是最大值或次大值,也就是说只有数组两端的元素才有资格争夺最大值。

那么可以想到另一种双指针法:两端指针,一个指针在头,从前往后;另一个在尾,从后往前。

核心思路:把两个指针所指元素的平方拿出来比较,大的存进新数组然后移动指针,直至两个指针相同,存入两个指针所指的同一元素即可。

while(i!=j){ //i在头,j在尾
        int a = nums[i]*nums[i];
        int b = nums[j]*nums[j];
        if(a>=b){
            res[cnt--] = a;
            i++;
        }
        else{
            res[cnt--] = b;
            j--;
        }
    }
res[cnt] = nums[i]*nums[i];

5、长度最小的子数组:经过前两题之后能想到双指针了,但不知道怎么判定指针移动

还是双指针法:滑动窗口,通常用于寻找连续子数组,分步骤分析

step 1:定义双指针都在数组头部,其中一个指针 j 一直向后走

step 2: j 每走一次都要加上当前 j 的下标所指元素的值,并将总和与题目给定值比较

step 3:如果小了,那么没事,j 继续走;如果大了,必须调整另一个指针 i 了。

注意,这里的 i 不能简单用 i++ 调整,否则找出来的可能不是最小子数组!(虽然我当时是这么想的)

具体如何调整呢,上代码,感觉一下思路就清晰了。

for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while (sum >= s) {
                subLength = (j - i + 1); // 取子序列的长度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
            }
        }

6、螺旋矩阵:没啥好说的,单纯我自己菜,CPU干烧了也不知道怎么下手

但其实对于这类题目吧,如果考试遇到了,我更愿意去背模板,现场模拟效率低还容易错

 //设置每次循环的起始位置
    int startX = 0;
    int startY = 0;
    //设置二维数组的中间值,若n为奇数。需要最后在中间填入数字
    int mid = n / 2;
    //循环圈数
    int loop = n / 2;
    //偏移数
    int offset = 1;
    //当前要添加的元素
    int count = 1;

循环圈数我能想到,但偏移数还真想不到,n为奇数的话矩阵还要填数字也想不到,orz

while(loop) {
        int i = startX;
        int j = startY;
        //模拟上侧从左到右
        for(; j < startY + n - offset; j++) {
            ans[i][j] = count++;
        }
        //模拟右侧从上到下
        for(; i < startX + n - offset; i++) {
            ans[i][j] = count++;
        }
        //模拟下侧从右到左
        for(; j > startY; j--) {
            ans[i][j] = count++;
        }
        //模拟左侧从下到上
        for(; i > startX; i--) {
            ans[i][j] = count++;
        }
        //偏移值每次加2
        offset+=2;
        //遍历起始位置每次+1
        startX++;
        startY++;
        loop--;
    }
    //若n为奇数需要单独给矩阵中间赋值
    if(n%2)
        ans[mid][mid] = count;

看起来很复杂但其实很好背,四次for循环里面的都是一样的赋值流程,要记住的只有三个点:

1、前两次的for循环内需要加n并减去偏移值

为啥只有前两次循环要这么操作,因为只有前两次循环需要找到右边界和下边界

2、偏移值每次加2,起始位置在对角线上,所以x,y都要加1,而loop控制圈数,正常减1就行。

至于偏移值为什么加2,因为你循环一圈最左边,最右边一圈元素遍历过了,最上面,最下面一圈元素遍历过了,而且startX加进来也意味着左边已经遍历过的也进来了,在找边界的时候不得减掉两边。(n-offset)

3、n为奇数还要给中间元素赋值

然后就可以背下来了

7、总结一下,双指针yyds,但是如果暴力能过,其实就不用把自己绕晕了,但并不是说双指针没用,而是面对机试的时候,暴力是很好的节约时间的办法,这也是在规定时间内没办法的事,谁管你代码好不好看,能AC你就牛逼。

然后在写代码的时候,把一些步骤或者思路先用注释写在开头是非常好的整理思路的方法,避免你写到一半忘了题目条件或者算法思路之类的。

目前一刷就刷刷对应推荐题了,二刷的时候再刷类似题

  • 56
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值