Leecode刷题日记Day4(数组、滑动窗口)

 1. 除自身以外数组的乘积 (数组)

我的思路

计算左右乘积列

时间复杂度:O(N) 扫描常数遍数组(乘积列一遍,赋值ans一遍)

空间复杂度:O(N) 使用了常数个Nsize数组

我的算法

int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
    int* left = malloc(sizeof(int)*numsSize); int leftmul = 1;
    int* right = malloc(sizeof(int)*numsSize); int rightmul = 1;
    int* ans = malloc(sizeof(int)*numsSize);
    *returnSize = numsSize;
    for(int i = 0; i<numsSize; i++){
        leftmul *= nums[i]; rightmul *= nums[numsSize-1-i];
        left[i] = leftmul;  right[numsSize-1-i] = rightmul;
    }
    ans[0] = right[1]; ans[numsSize-1] = left[numsSize-2];
    for(int i = 1; i<numsSize-1; i++){
        ans[i] = left[i-1] * right[i+1]; 
    }  
    return ans;
}

算法学习

因为输出数组不算空间复杂度,所以我们用Left或Right数组来作为输出数组,并且在构建完LorR数组后动态计算RorL(通过维护一个mul变量)来构建ans数组。

能这么做的原因是因为,在最后计算ans数组的工作在计算left和right数组的时候已经干过了,相当于是做了很多重复冗余的工作。

int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
    int* left = malloc(sizeof(int)*numsSize); 
    int leftmul = 1, rightmul = 1;
    *returnSize = numsSize;
    //先完成对left数组的计算
    for(int i = 0; i<numsSize; i++){
        leftmul *= nums[i]; 
        left[i] = leftmul; 
    }
    //然后逆向计算rightmul,一边算一边和left数组结合
    for(int i = numsSize-1; i>=0; i--){
        left[i] = (i!=0?left[i-1]:1) * rightmul; 
        rightmul *= nums[i];
    }  
    return left;
}

滑动窗口专题

2.长度最小的子数组 

我的思路

双指针滑动窗口,最短子数组一定是多一个元素就多,少一个元素就少,所以在这个遍历过程中一定可以被找到。

算法核心:是计算以nums[i]为右端点的最小子数组,可以这样双指针滑动(end向右一直滑动到sum>=target)的原因,是因为题目中有个性质:都是正数,所以要多思考题目给的条件。

        这样滑动的理论基础:我们滑动end的时候,可以一直向右滑动,跳过了某个点没有做停留是因为我们认为这个点(以nums[i]为右端点)不可能得到更好的结果,为什么呢?

        因为,当start固定在某个地方的时候,说明前面已经有一个end_old让(start-1)~ end_old的sum>target了,从end_old继续向右滑动,得到的end_new >= end_old+1, 如果这时候去考虑把start向左滑动以寻求更全面的搜寻的时候,只要滑动一步得到的窗口长度就一定是大于等于之前的最好结果的了(当end_new = end_old+1的时候取等), 所以end向左滑动的过程中,可以一直向左滑动直到sum>target而不用考虑start向右滑动。

                                                                            ——在这个过程中用到了都是正数的性质。

我的算法

#define min(a, b) a<b?a:b
int minSubArrayLen(int target, int* nums, int numsSize) {
    int start = 0, end = -1;
    int min = INT_MAX, sum = 0;
    while(1){
        if(min == 1){
            break;
        }
        if(sum < target){
            end++;
            //出去的情况一定只有end走到数组外面了
            if(end >= numsSize){
                break;
            }
            sum += nums[end];
        }
        else{
            min = min(min, end - start + 1);
            sum -= nums[start];
            start++;
        }
    }
    return min==INT_MAX?0:min;
}

3.无重复字符的最长子串 

我的思路

注意题目的条件,这里的字符是字母、数字、空格、符号,我一开始以为只有字母,费了很多时间。

这种双指针法,一般只有在end的位置判断出口条件,所以while1,只判断s[end] == 0.

再无重复这一点上,我把每个字符当作只有1的互斥量来使用。

一边是先释放再加,一边是先加再用值,这种非对称性似乎没办法解决,虽然无伤大雅但是代码看起来不好看。出口判断要在用值前面,不然会错误.

我的算法

#define max(a, b) a>b?a:b
int lengthOfLongestSubstring(char* s) {
    if(strlen(s) == 1){
        return 1;
    }
    int alpha[255];
    for(int i = 0; i<255; i++){
        alpha[i] = 1;
    }
    int start = 0, end = -1;
    int max = 0;
    int mode = 1;
    while(1){
        switch(mode){
            case 1:
            //先加再用值
            end++;
            //出口判断
            if(s[end] == 0){
                goto exit;
            }
            alpha[s[end]]--;
            if(alpha[(s[end])] == -1){
                mode = 0;
            }
            else{
                max = max(max, end - start +1);
            }
            break;
            case 0:
            //先释放再加
            alpha[s[start]]++;
            start++;
            if(alpha[s[end]] == 0){
                mode = 1;
            }
            break;
        }
    }
    exit:
    return max;
}

算法学习

4. 最小覆盖子串 

我的思路

完全手搓,模仿上面的思路写的,但是似乎效果不理想。

我的算法

#define min(a, b) a<b?a:b
#define SIZE 256
int isOverlapped(int* array, int length){
    int ret = 1;
    for(int i = 0; i<length; i++){
        if(array[i] > 0){
            ret = 0;
            break;
        }
    }
    return ret;
}

int isInt(char* s, char c){
    int ret = 0;
    while(*s!=0){
        if(c == *s){
            ret = 1;
            break;
        }
        s++;
    }
    return ret;
}

char* minWindow(char* s, char* t) {
    char* N = malloc(sizeof(int)*1);
    *N = 0;
    if(strlen(s) < strlen(t)){
        return N;
    }
    //每记录下一个min,都要跟着记录min_start
    int min_start = 0, min = INT_MAX;
    //双指针
    int start = 0, end = -1;
    //模式选择
    int mode = 1;
    int* t_array = malloc(sizeof(int)*SIZE);
    for(int i =0; i< SIZE; i++){
        t_array[i] = 0;
    }
    for(int i =0; i< strlen(t); i++){
        t_array[t[i]]++;
    }

    //业务功能
    while(1){
        switch(mode){
            case 1:
                end++;
                if(s[end] == 0){
                    goto exit;
                }
                if(isInt(t, s[end])){
                    t_array[s[end]]--;
                }
                if(isOverlapped(t_array, SIZE)){
                    if( (end - start +1) < min){
                        min =  end - start + 1;
                        min_start  = start;
                    }
                    mode = 0;
                }
                break;
            case 0:
                if(isInt(t, s[start++]) ){
                    t_array[s[start-1]]++;
                    if(!isOverlapped(t_array, SIZE)){
                        mode = 1;
                    }
                    else{
                        if( (end - start +1) < min){
                        min =  end - start + 1;
                        min_start  = start;
                    }
                    }
                }
                else{
                    if( (end - start +1) < min){
                        min =  end - start + 1;
                        min_start  = start;
                    }
                }
                break;
        }
    }
    exit:
    if(min == INT_MAX){
        return N;
    }
    s[min_start+min] = 0;
    return &s[min_start];
}

算法学习

看了一下题解,发现思路完全正确,看来就是数据结构使用得不够合理了。

还是得学一下C++才行

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值