刷题06 数组mid

494.目标和

给你一个非负整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5

        此题涉及回溯,函数参数需要包含数组下标,当前的数组之和。 

int res;
void backtravel(int *nums,int numsSize,int target,int sum,int idx){
    if(idx==numsSize){
        if(sum==target){
            res++;
        }
    }else{
        backtravel(nums,numsSize,target,sum+nums[idx],idx+1);
        backtravel(nums,numsSize,target,sum-nums[idx],idx+1);
    }
}
int findTargetSumWays(int* nums, int numsSize, int target) {
    res=0;
    backtravel(nums,numsSize,target,0,0);
    return res;
}

560.和为K的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

子数组是数组中元素的连续非空序列。

示例 1:

输入:nums = [1,1,1], k = 2
输出:2

         暴力解法

int subarraySum(int* nums, int numsSize, int k) {
    int ans=0;
    for(int i=0;i<numsSize;++i){
        int sum=0;
        for(int j=i;j<numsSize;++j){
            sum+=nums[j];
            if(sum==k){
                ans++;
            }
        }
    }
    return ans;
}

        前缀和

int subarraySum(int* nums, int numsSize, int k) {
    int ans=0;
    int* presum=malloc(sizeof(int)*numsSize);
    presum[0]=nums[0];
    for(int i=1;i<numsSize;++i){
        presum[i]=presum[i-1]+nums[i];
    }
    for(int i=0;i<numsSize;++i){
        for(int j=i;j<numsSize;++j){
            if(presum[j]-presum[i]+nums[i]==k){
                ans++;
            }
        }
    }
    return ans;
}


540. 有序数组中的单一元素

给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。

请你找出并返回只出现一次的那个数。

你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。

示例 1:

输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

         注意边界条件,只有一个元素,首尾都需要单独讨论。

int singleNonDuplicate(int* nums, int numsSize) {
    if(numsSize==1) return nums[0];
    if(nums[0]!=nums[1]) return nums[0];
    if(nums[numsSize-1]!=nums[numsSize-2]) return nums[numsSize-1];
    for(int i=1;i<numsSize-2;++i){
        if(nums[i-1]!=nums[i]&&nums[i]!=nums[i+1]){
            return nums[i];
        }
    }
    return -1;
}


665. 非递减数列

给你一个长度为 n 的整数数组 nums ,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中任意的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]

示例 1:

输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个 4 变成 1 来使得它成为一个非递减数列。
bool checkPossibility(int* nums, int numsSize) {
    int count=0;
    for(int i=0;i<numsSize-1;++i){
        int x=nums[i],y=nums[i+1];
        //统计数组中逆序的个数
        if(x>y){
            count++;
            if(count>1){
                return false;
            }
            if(i>0&&y<nums[i-1]){
                nums[i+1]=x;
            }
        }
    }
    return true;
}

718.最长重复子数组

 给两个整数数组  A  和  B ,返回两个数组中公共的、长度最长的子数组的长度。

输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
输出: 3         解释:长度最长的公共子数组是 [3, 2, 1]。

        dp[i][j]为以 nums1[i], nums2[j]结尾的xxx。这道题就是:以 nums1[i], nums2[j] 结尾的两个数组中公共的、长度最长的子数组的长度。

        双层循环找出所有的 i, j 组合,时间复杂度 O(m∗n)O(m * n)O(m∗n),其中 m 和 n 分别为 A 和 B 的 长度。
        如果 A[i-1] == B[j-1],dp[i][j] = dp[i - 1][j - 1] + 1
        否则,dp[i][j] = 0
        循环过程记录最大值即可。

int findLength(int* nums1, int nums1Size, int* nums2, int nums2Size) {
    int dp[nums1Size+1][nums2Size+1];
    memset(dp,0,sizeof(dp));
    int ans=0;
    for(int i=1;i<=nums1Size;++i){
        for(int j=1;j<=nums2Size;++j){
            if(nums1[i-1]==nums2[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;
            }
            ans=fmax(ans,dp[i][j]);
        }
    }
    return ans;
}

1143.最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

输入:text1 = "abcde", text2 = "ace" 
输出:3  

数组(连续)变成了子序列 (非连续)。

算法只需要一点小的微调: 如果 A[i] != B[j],那么 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

int longestCommonSubsequence(char* text1, char* text2) {
    int m=strlen(text1),n=strlen(text2);
    int dp[m+1][n+1];
    memset(dp, 0, sizeof(dp));
    for(int i=1;i<=m;++i){
        for(int j=1;j<=n;++j){
            if(text1[i-1]==text2[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;
            }else{
                dp[i][j]=fmax(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    return dp[m][n];
}

300.最长递增子序列 

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

       dp[i]为考虑前i个元素,以第i个数字结尾的最长上升子序列的长度。

int lengthOfLIS(int* nums, int numsSize) {
    int dp[numsSize];//dp[i]为考虑前i个元素,以第i个数字结尾的最长上升子序列的长度
    dp[0]=1;
    int ans=1;
    for(int i=1;i<numsSize;++i){
        dp[i]=1;
        for(int j=0;j<i;++j){
            if(nums[j]<nums[i]){
                dp[i]=fmax(dp[i],dp[j]+1);
            }
        }
        ans=fmax(ans,dp[i]);
    }
    return ans;
}

 795.区间子数组个数

给你一个整数数组 nums 和两个整数:left 及 right 。找出 nums 中连续、非空且其中最大元素在范围 [left, right] 内的子数组,并返回满足条件的子数组的个数。

生成的测试用例保证结果符合 32-bit 整数范围。

输入:nums = [2,1,4,3], left = 2, right = 3
输出:3
解释:满足条件的三个子数组:[2], [2, 1], [3]
int numSubarrayBoundedMax(int* nums, int numsSize, int left, int right) {
    int ans=0;
    int l=-1,r=-1;
    for(int i=0;i<numsSize;++i){
        if(nums[i]>=left&&nums[i]<=right){
            //不断记录符合条件的右边界
            r=i;
        }else if(nums[i]>right){
            //记录左边界
            l=i;
        }
        //如果相减是负数,则不加
        ans+=fmax(r-l,0);
    }
    return ans;
}

 845.数组中最长山脉

把符合下列属性的数组 arr 称为 山脉数组 :

  • arr.length >= 3
  • 存在下标 i0 < i < arr.length - 1),满足
    • arr[0] < arr[1] < ... < arr[i - 1] < arr[i]
    • arr[i] > arr[i + 1] > ... > arr[arr.length - 1]

给出一个整数数组 arr,返回最长山脉子数组的长度。如果不存在山脉子数组,返回 0 。

输入:arr = [2,1,4,7,3,2,5]
输出:5
int longestMountain(int* arr, int arrSize) {
    if(arrSize<3) return 0;
    int i=0,j=1,k=2;
    int mmax=0;
    //枚举首尾和山峰三个下标
    while(k<=arrSize-1){
        int i1=i,j1=j,k1=k;
        if(arr[i1]<arr[j1]&&arr[j1]>arr[k1]){
            while(i1>0&&arr[i1]>arr[i1-1]){
                i1--;
            }
            while(k1<arrSize-1&&arr[k1]>arr[k1+1]){
                k1++;
            }
            if(mmax<k1-i1+1) mmax=k1-i1+1;
        }
        i++;j++;k++;
    }
    return mmax;
}

910.最小差值II

 给你一个整数数组 nums,和一个整数 k 。

对于每个下标 i0 <= i < nums.length),将 nums[i] 变成 nums[i] + k 或 nums[i] - k 。nums 的 分数 是 nums 中最大元素和最小元素的差值。

在更改每个下标对应的值之后,返回 nums 的最小 分数 。

输入:nums = [1], k = 0
输出:0
解释:分数 = max(nums) - min(nums) = 1 - 1 = 0 。
int cmp(int* a,int* b){
    return *a-*b;
}
int smallestRangeII(int* nums, int numsSize, int k) {
    qsort(nums,numsSize,sizeof(int),cmp);
    int n=numsSize;
    int minscore=nums[n-1]-nums[0];
    for(int i=1;i<n;++i){
        int high=fmax(nums[i-1]+k,nums[n-1]-k);
        int low=fmin(nums[0]+k,nums[i]-k);
        minscore=fmin(minscore,high-low);
    }
    return minscore;
}

947.和可被K整除的子数组 

给定一个整数数组 nums 和一个整数 k ,返回其中元素之和可被 k 整除的(连续、非空) 子数组 的数目。

子数组 是数组的 连续 部分。

输入:nums = [4,5,0,-2,-3,1], k = 5
输出:7
int subarraysDivByK(int* nums, int numsSize, int k) {
    int sum[numsSize+1];
    sum[0]=0;
    for(int i=0;i<numsSize;++i){
        sum[i+1]=sum[i]+nums[i];
    }
    int ans=0;
    for(int i=0;i<numsSize;++i){
        for(int j=i+1;j<=numsSize;++j){
            int cur=sum[j]-sum[i];
            if(cur%k==0) ans++;
        }
    }
    return ans;
}

1004.最大连续1的个数

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。

输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

        滑动窗口:遍历数组时记录窗口中0的个数,如果0的个数超过k个,就让left移动,遍历过程中记录最大值。 

int longestOnes(int* nums, int numsSize, int k) {
    int res=0,zero=0,left=0;
    for(int right=0;right<numsSize;++right){
        if(nums[right]==0) zero++;
        while(zero>k){
            if(nums[left++]==0){
                zero--;
            }
        }
        res=fmax(res,right-left+1);
    }
    return res;
}

1013.将数组分成和相等的3个部分 

给你一个整数数组 arr,只有可以将其划分为三个和相等的 非空 部分时才返回 true,否则返回 false

形式上,如果可以找出索引 i + 1 < j 且满足 (arr[0] + arr[1] + ... + arr[i] == arr[i + 1] + arr[i + 2] + ... + arr[j - 1] == arr[j] + arr[j + 1] + ... + arr[arr.length - 1]) 就可以将数组三等分。

输入:arr = [0,2,1,-6,6,-7,9,1,2,0,1]
输出:true
解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1
bool canThreePartsEqualSum(int* arr, int arrSize){
  int sum=0;
  for(int i=0;i<arrSize;i++){
    sum+=arr[i];
  }
  if(sum%3!=0) return false;
  int cur=0,cxt=0,target=sum/3;
  for(int i=0;i<arrSize;i++){
    cur+=arr[i];
    if(cur==target){
      cur=0;
      cxt++;
    }
    if(cxt==3) return true;
  }
  return false;
}

1017.负二进制转换

给你一个整数 n ,以二进制字符串的形式返回该整数的 负二进制(base -2表示。

注意,除非字符串就是 "0",否则返回的字符串中不能含有前导零。

输入:n = 2
输出:"110"
解释:(-2)2 + (-2)1 = 2
char* baseNeg2(int n) {
    if(n==0) return "0";
    if(n==1) return "1";
    char* ans=malloc(sizeof(char)*32);
    int pos=0;
    while(n!=0){
        //奇数填1,偶数填0
        if(abs(n)%2==0){
            ans[pos++]='0';
            n=-(n/2);
        }else{
            ans[pos++]='1';
            n=-((n-1)/2);
        }
    }
    ans[pos]='\0';
    //翻转字符串
    int l=0,r=pos-1;
    while(l<r){
        char tmp=ans[l];
        ans[l]=ans[r];
        ans[r]=tmp;
        l++;r--;
    }
    return ans;
}

1053.交换一次的先前排列

给你一个正整数数组 arr(可能存在重复的元素),请你返回可在 一次交换(交换两数字 arr[i] 和 arr[j] 的位置)后得到的、按字典序排列小于 arr 的最大排列。

如果无法这么操作,就请返回原数组。

输入:arr = [3,2,1]
输出:[3,1,2]
解释:交换 2 和 1

         贪心思想:从后往前找小于a[i]的最大的数a[j],如果存在多个数满足条件,选第一个进行交换。

int* prevPermOpt1(int* arr, int arrSize, int* returnSize) {
    //贪心从后往前找小于a[i]的最大的数a[j],如果存在多个数满足条件,选第一个进行交换。
    for (int i=arrSize-2; i>=0; i--) {//从后进行枚举
        if (arr[i]>arr[i+1]) {//第一个非递减位置
            int j=arrSize-1;
            while (arr[j]>=arr[i]||arr[j]==arr[j-1]) {
                j--;//寻找并自己小并且距离最近的元素,元素已经是递增排列。直接枚举即可
            }
            int val=arr[i];//交换
            arr[i]=arr[j];
            arr[j]=val;
            break;
        }
    }
    *returnSize=arrSize;
    return arr;
}

1218.最长定差子序列

给你一个整数数组 arr 和一个整数 difference,请你找出并返回 arr 中最长等差子序列的长度,该子序列中相邻元素之间的差等于 difference 。

子序列 是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从 arr 派生出来的序列。

输入:arr = [1,2,3,4], difference = 1
输出:4
解释:最长的等差子序列是 [1,2,3,4]。

        暴力法:遍历枚举等差数列第一个元素。

int longestSubsequence(int* arr, int arrSize, int difference) {
    int mmax=0;
    for(int i=0;i<arrSize;++i){
        int idx=i+1;
        int first=arr[i];
        int count=1;
        while(idx<arrSize){
            if(first+difference==arr[idx]){
                first=arr[idx];
                count++;
                idx++;
            }else{
                idx++;
            }
        }
        if(count>mmax){
            mmax=count;
        }
    }
    return mmax;
}

        用时间换空间

int longestSubsequence(int* arr, int arrSize, int difference) {
    int len=arrSize,ans=0;
    int dp[200000]={1};
    for(int i=0;i<len;++i){
        arr[i]+=100000;
        dp[arr[i]]=dp[arr[i]-difference]+1;
        ans=fmax(ans,dp[arr[i]]);
    }
    return ans;
}

128.最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

输入:nums = [100,4,200,1,3,2] 输出:4

int cmp(int* a,int* b){
    return *a-*b;
}
int longestConsecutive(int* nums, int numsSize) {
    if(numsSize==0) return 0;
    int ans=1;
    int tmp=1;
    qsort(nums,numsSize,sizeof(int),cmp);
    for(int i=1;i<numsSize;++i){
        if(nums[i]==nums[i-1]) continue;
        else if(nums[i]==nums[i-1]+1) tmp++;
        else{
            tmp=1;
        }
        ans=fmax(ans,tmp);
    }
    return ans;
}

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值