刷题03 数组mid

2085.统计出现过一次的公共字符串

        使用哈希表,找出两个字符串数组中只出现过一次的公共字符串。map中记录者字符串以及字符串出席的频次。然后在数组查找,字符串相等且频次均为1的字符串即可。

class Solution {
public:
    int countWords(vector<string>& words1, vector<string>& words2) {
        unordered_map<string,int> f1,f2;
        for(string str:words1){
            f1[str]++;
        }
        for(string str:words2){
            f2[str]++;
        }
        int res=0;
        //遍历word1是否满足条件
        for(auto[str,cnt1]:f1){
            if(cnt1==1&&f2[str]==1){
                res++;
            }
        }
        return res;
    }
};

 215.数组中第K大的元素

        1.暴力解法

        排序+直接返回倒数第k个元素

int cmp(int* a,int* b){
    return *a-*b;
}
int findKthLargest(int* nums, int numsSize, int k) {
    qsort(nums,numsSize,sizeof(int),cmp);
    return nums[numsSize-k];
}
        2. 快速排序

        本质上是找n-k位置上的元素。因为快排每趟排序都会将一个元素送到最终位置上。

class Solution {
public:
    int qsort(vector<int>& nums, int l,int r){
        int mid=nums[l];
        while(l<r){
            while(l<r&&nums[r]>mid) r--;
            nums[l]=nums[r];
            while(l<r&&nums[l]>mid) l++;
            nums[r]=nums[l];
        }
        nums[l]=mid;
        return l;
    }
    int findKthLargest(vector<int>& nums, int k) {
        int n=nums.size();
        int l=0,r=n-1,m=0;
        while(1){
            m=qsort(nums,l,r);
            if(m==n-k-1){
                break;
            }else if(m>n-k-1){
                r=m-1;
            }else l=m+1;
        }
        return nums[n-k-1];
    }
};

260. 只出现一次的数字III

        哈希表,记录数组中各个数字出现的频次。

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        unordered_map<int,int> mp;
        for(int num:nums){
            mp[num]++;
        }
        vector<int> ans;
        for(auto[num,cnt]:mp){
            if(cnt==1){
                ans.push_back(num);
            }
        }
        return ans;
    }
};

152.乘积最大的连续子数组

         动态规划:因为数组中存在负数,因此还得维护最小值,因为两个负值相乘有可能会产生最大值。更新时也需要用最小值和最大值分别乘nums[i]进行比较。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n=nums.size();
        if(n==0) return 0;
        if(n==1) return nums[0];
        int ans=nums[0];
        int mmax=nums[0];
        int mmin=nums[0];
        for(int i=1;i<n;++i){
            int temp=mmax;
            mmax=max(max(mmax*nums[i],nums[i]),mmin*nums[i]);
            mmin=min(min(temp*nums[i],nums[i]),mmin*nums[i]);
            ans=max(mmax,ans);
        }
        return ans;
    }
};

162.寻找峰值

         由于假设 nums[-1] = nums[n] = -∞ 。因此需要单独判断只有一个元素的情况,以及第一个元素与最后一个元素。

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

164.最大间距

        给定一个无序的数组 nums,返回 数组在排序之后,相邻元素之间最大的差值 。如果数组元素个数小于 2,则返回 0 。         

int cmp(int* a,int* b){
    return *a-*b;
}
int maximumGap(int* nums, int numsSize) {
    if(numsSize<2) return 0;
    qsort(nums,numsSize,sizeof(int),cmp);
    int mmax=0;
    for(int i=0;i<numsSize-1;++i){
        int cur=nums[i+1]-nums[i];
        if(cur>mmax){
            mmax=cur;
        }
    }
    return mmax;
}

167.两数之和II-有序数组

         1.暴力解法

        排序+直接for循环进行查找

int cmp(int* a,int* b){
    return *a-*b;
}
int maximumGap(int* nums, int numsSize) {
    if(numsSize<2) return 0;
    qsort(nums,numsSize,sizeof(int),cmp);
    int mmax=0;
    for(int i=0;i<numsSize-1;++i){
        int cur=nums[i+1]-nums[i];
        if(cur>mmax){
            mmax=cur;
        }
    }
    return mmax;
}
        2.双指针

         由于数组是非递减的,用双指针向中间收缩,找到一个比target大的两数之和,由于两个数不能相等,所以low<high

int* twoSum(int* numbers, int numbersSize, int target, int* returnSize) {
    *returnSize=2;
    int* ret=malloc(sizeof(int)*2);
    int low=0,high=numbersSize-1;
    while(low<high){
        int sum=numbers[low]+numbers[high];
        if(sum==target){
            ret[0]=low+1,ret[1]=high+1;
            return ret;
        }else if(sum<target){
            low++;
        }else{
            high--;
        }
    }
    ret[0]=-1,ret[1]=-1;
    return ret;
}

179.最大数

        此题与真题有些类似

        第一步:排序

        第二步:转化为字符串数组

        排序:两个数a和b的先后,需要通过拼接后的数字进行比较,但是需要先确定a与b的位数,然后拼接时,乘上系数。

       int sprintf( char *buffer, const char *format, [ argument] … );
       buffer:char型指针,指向欲写入的字符串地址。
  format:char型指针,指向的内存里面存放了格式字符串。
  [argument]…:可选参数,可以是任何类型的数据。
  返回值:字符串长度(strlen)

        实际上sprintf()的作用就是把后面的字符串按照一定的格式放到从p开始的空间里。

unsigned long long cmp(int* x,int* y){
    unsigned long long sx=10,sy=10;
    while(sx<=*x){
        sx*=10;
    }
    while(sy<=*y){
        sy*=10;
    }
    return sx*(*y)+(*x)>sy*(*x)+(*y);//会溢出,排序顺序为y x
}
char* largestNumber(int* nums, int numsSize) {
    qsort(nums,numsSize,sizeof(int),cmp);
    if(nums[0]==0){
        char *ret=malloc(sizeof(char)*2);
        ret[0]='0',ret[1]='\0';
        return ret;
    }
    char *ret=malloc(sizeof(char)*1000);
    char *p=ret;
    for(int i=0;i<numsSize;++i){
        sprintf(p,"%d",nums[i]);
        p+=strlen(p);
    }
    return ret;
}

14.最长公共前缀

 编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:输入:strs = ["flower","flow","flight"] 输出:"fl"

        此题为模拟,取出第一个字符串,然后将接下来的字符串依次与第一个进行比较,一旦发现有不一样的字符就填入终止符,代表字符串结束了。要熟悉字符串数组的这种操作。 

char* longestCommonPrefix(char** strs, int strsSize) {
    if(strsSize==0) return "";
    for(int i=0;i<strlen(strs[0]);++i){
        for(int j=1;j<strsSize;++j){
            if(strs[0][i]!=strs[j][i]){
                strs[0][i]='\0';
                return strs[0];
            }
        }
    }
    return strs[0];
}

 43.字符串相乘

        涉及字符串操作。

        法一:字符串转化为整型数据

        sprintf(char * str,数据类型,num):将字符串变成数字类型,将单个字符变成数字的操作是

str[i]-'0' 注意不要忘,还有str[i]-‘a’这个操作也不要忘。

        但是这个方法会溢出,有一半的例子通不过。

long long fun(char* str){
    long long sum=0;
    int n=strlen(str);
    for(int i=0;i<n;++i){
        sum=sum*10+str[i]-'0';
    }
    return sum;
}
char* multiply(char* num1, char* num2) {
    long long n1=fun(num1);
    long long n2=fun(num2);
    char* s=malloc(sizeof(char)*10000);
    sprintf(s,"%ld",n1*n2);
    return s;
}
        方法二:直接同字符串模拟乘法

189.轮转数组

        用一个辅助数组,将原数组的i位置上的元素放在(i+k)%numsSize位置,还有一种解法是原地逆置,这里先不写。

void rotate(int* nums, int numsSize, int k) {
    int temp[numsSize];
    for(int i=0;i<numsSize;++i){
        temp[(i+k)%numsSize]=nums[i];
    }
    for(int i=0;i<numsSize;++i){
        nums[i]=temp[i];
    }
}

204.计数质数

        用枚举。质数就是只能除1和自己本身的数,枚举可以从2到根号n依次判断是否能够整除来判断该数是否是质数。

bool isPrime(int x){
    for(int i=2;i*i<=x;++i){
        if(x%i==0){
            return false;
        }
    }
    return true;
}

int countPrimes(int n){
    int ans=0;
    for(int i=2;i<n;++i){
        ans+=isPrime(i);
    }
    return ans;
}

209.长度最小的连续子数组

        滑动窗口:找总和大于等于 target 的长度最小的连续子数组

int minSubArrayLen(int target, int* nums, int numsSize) {
    //滑动窗口
    int ans=INT_MAX;
    int sum=0,i=0;
    for(int j=0;j<numsSize;++j){
        sum+=nums[j];
        while(sum>=target){
            if(ans>j-i+1){
                ans=j-i+1;
            }
            sum-=nums[i];//将i后移,缩小滑动窗口的范围
            i++;
        }
    }
    return ans==INT_MAX?0:ans;
}

238.除自身之外的数字的乘积 

        记录前缀乘积与后缀乘积,前缀值从前到后遍历,后缀值从后到前遍历。

int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
    int* res=malloc(sizeof(int)*numsSize);//结果数组
    for(int i=0;i<numsSize;++i){
        res[i]=1;//初始化为1
    }
    int pre=1,suf=1;
    for(int i=1;i<numsSize;++i){
        pre*=nums[i-1];//计算前缀乘积
        suf*=nums[numsSize-i];//计算后缀乘积
        res[i]*=pre;//更新结果数组
        res[numsSize-i-1]*=suf;
    }
    *returnSize=numsSize;
    return res;
}

287.寻找重复数

        1.排序+遍历
int cmp(int*a,int*b){
    return *a-*b;
}
int findDuplicate(int* nums, int numsSize) {
    qsort(nums,numsSize,sizeof(int),cmp);
    for(int i=0;i<numsSize-1;++i){
        if(nums[i]==nums[i+1]){
            return nums[i];
        }
    }
    return 0;
}
        2.哈希表 
int findDuplicate(int* nums, int numsSize) {
    int flag[100003]={0};
    for(int i=0;i<numsSize;++i){
        flag[nums[i]]++;
        if(flag[nums[i]]==2) return nums[i];
    }
    return 0;
}

300.最长递增子序列

        用动态规划,dp[i]是以从0到i的元素的最长递增子序列的长度,遍历0到i位置

#define Max(x,y) (x)>(y)?(x):(y)
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]=Max(dp[i],dp[j]+1);
            }
        }
        ans=Max(ans,dp[i]);
    }
    return ans;
}

334.递增的三元子序列

         判断这个数组中是否存在长度为 3 的递增子序列。

        1.动态规划,超时

        将此题转化为求最长递增子序列的长度,然后求的时候判断能否达到3。

#define Max(x,y) (x)>(y)?(x):(y)
bool increasingTriplet(int* nums, int numsSize) {
    int dp[numsSize];//dp[i] 为考虑前i个元素,以第i个数字结尾的最长上升子序列的长度
    dp[0]=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]=Max(dp[i],dp[j]+1);
            }
        }
        if(dp[i]==3){
            return true;
        }
    }
    return false;
}
        2.贪心算法

        从左到右遍历数组 nums,遍历过程中维护两个变量 first 和 second,分别表示递增的三元子序列中的第一个数和第二个数,任何时候都有 first<second。为了找到递增的三元子序列,first和 second应该尽可能地小,此时找到递增的三元子序列的可能性更大。

bool increasingTriplet(int* nums, int numsSize) {
   if(numsSize<3){
       return false;
   }
   int first=nums[0],second=INT_MAX;//first与second维护时总是最小的
   for(int i=1;i<numsSize;++i){
       int num=nums[i];
       if(num>second){//一旦有num比second大就存在递增的三院子序列
           return true;
       }else if(num>first){
           second=num;
       }else if(num<first){
           first=num;
       }
   }
    return false;
}

318.最大单词长度乘积

         此题的困难之处在与判断两个字符串是否含有相同的字符,此处复习字符串的位运算。

由于单词只包含小写字母,共有 26个小写字母,因此可以使用位掩码的最低 26位分别表示每个字母是否在这个单词中出现。将 a 到z 分别记为第 0个字母到第 25 个字母,则位掩码的从低到高的第 i位是 1当且仅当第 i个字母在这个单词中,其中 0≤i≤25

用数组masks 记录每个单词的位掩码表示。计算数组 masks 之后,判断第 i个单词和第 j个单词是否有公共字母可以通过判断 masks[i] & masks[j]是否等于 0实现,当且仅当 masks[i] & masks[j]=0 时第 i个单词和第 j个单词没有公共字母,此时使用这两个单词的长度乘积更新最大单词长度乘积

int maxProduct(char ** words, int wordsSize){
    int makes[wordsSize];
    for(int i=0;i<wordsSize;++i){
        makes[i]=0;
        for(int j=0;j<strlen(words[i]);++j){
            makes[i]|=1<<(words[i][j]-'a');//记录第i-1个字符串的字符
        }
    }
    int ans=0;
    for(int i=0;i<wordsSize;++i){
        for(int j=i+1;j<wordsSize;++j){
            if((makes[i]&makes[j])==0){//如果没有重复的字符
                int cur=strlen(words[i])*strlen(words[j]);
                if(cur>ans){
                    ans=cur;
                }
            }
        }
    }
    return ans;
}

376.摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的

        动态规划:用up,down数组进行动态, 每次更新时要判断最新的值是否能够增加摆动子序列的长度,如果不能就还是用数组的前一个值。

int wiggleMaxLength(int* nums, int numsSize){
    if(numsSize<2) return numsSize;
    int up[numsSize],down[numsSize];
    up[0]=1;down[0]=1;
    for(int i=1;i<numsSize;++i){
        if(nums[i-1]>nums[i]){
            down[i]=fmax(down[i-1],up[i-1]+1);
            up[i]=up[i-1];
        }else if(nums[i-1]<nums[i]){
            up[i]=fmax(up[i-1],down[i-1]+1);
            down[i]=down[i-1];
        }else{
            up[i]=up[i-1];
            down[i]=down[i-1];
        }
    }
    return fmax(up[numsSize-1],down[numsSize-1]);
}

        优化后的动态规划,不用数组而是用up和down。因为很多时候不需要进行值更新,而且只需要关注最后的那两个值即可。

int wiggleMaxLength(int* nums, int numsSize){
    if(numsSize<2) return numsSize;
    int up=1,down=1;
    for(int i=1;i<numsSize;++i){
        if(nums[i-1]>nums[i]){
           up=fmax(up,down+1);
        }else if(nums[i-1]<nums[i]){
           down=fmax(up+1,down);
        }
    }
    return fmax(up,down);
}

324.摆动排序II

        给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。你可以假设所有输入数组都可以得到满足题目要求的结果。

        此题可以先进行排序,然后将数组逆序插入到奇数下标和偶数下标。

        例如:nums = [1,5,3,2,6,4],排序之后为[1,2,3,4,5,6],逆序插入奇数下标后为[x,6,x,5,x,4,x],逆序插入偶数下标为[3,6,2,5,1,4],满足题意。

        由于要子啊原数组中进行修改,故需要一个辅助数组进行记录。

int cmp(int* a,int* b){
    return *a-*b;
}
void wiggleSort(int* nums, int numsSize) {
    //排序后切分成两部分然后逆序穿插
    int temp[numsSize];
    for(int i=0;i<numsSize;++i){
        temp[i]=nums[i];
    }
    int n=numsSize-1;
    qsort(temp,numsSize,sizeof(int),cmp);
    for(int i=1;i<numsSize;i+=2){
        //奇数下标
        nums[i]=temp[n--];
    }
    for(int i=0;i<numsSize;i+=2){
        //偶数下标
        nums[i]=temp[n--];
    }
}

413.等差数列划分

 如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。

例如,[1,3,5,7,9][7,7,7,7] 和 [3,-1,-5,-9] 都是等差数列。

int numberOfArithmeticSlices(int* nums, int numsSize){
    if(numsSize==1) return 0;
    int d=nums[0]-nums[1],t=0;
    int ans=0;
    for(int i=2;i<numsSize;++i){
        if(nums[i-1]-nums[i]==d){
            //能形成等差数列
            ++t;
        }else{
            //不能形成等差数列
            d=nums[i-1]-nums[i];
            t=0;
        }
        ans+=t;
    }
    return ans;
}

41.缺失的第一个正数

         用哈希表进行统计数组中出现的数字。

int firstMissingPositive(int* nums, int numsSize) {
    int hash[100002]={0};
    for(int i=0;i<numsSize;++i){
        if(nums[i]>0&&nums[i]<=numsSize){
            hash[nums[i]]=1;
        }
    }
    for(int i=1;i<=numsSize;++i){
        if(hash[i]==0){
            return i;
        }
    }
    return numsSize+1;
}

53.最大连续子数组和

解题思路:动态规划:
 1. dp数组:dp[i]表示从0到i的子序列中最大序列和的值
 2. 递推公式:dp[i] = max(dp[i-1] + nums[i], nums[i]),若dp[i-1]<0,对最后结果无益。dp[i]则为nums[i]。
 3. dp数组初始化:dp[0]的最大子数组和为nums[0]
 4. 推导顺序:从前往后遍历

int maxSubArray(int* nums, int numsSize) {
    int dp[numsSize];
    memset(dp,0,numsSize);
    int mmax=nums[0];
    dp[0]=nums[0];
    for(int i=1;i<numsSize;++i){
        dp[i]=fmax(dp[i-1]+nums[i],nums[i]);
        mmax=fmax(mmax,dp[i]);
    }
    return mmax;
}

使用贪心的思想,每次计算当前最优的子数组之和

int maxSubArray(int* nums, int numsSize) {
    int mmax=INT_MIN;
    int cur=0;//存放当前结果
    for(int i=0;i<numsSize;++i){
        cur+=nums[i];
        //当局部结果大于mmax时更新结果
        if(cur>mmax){
            mmax=cur;
        }
        //当局部结果为负时,对最终结果无益,重新开始计算
        if(cur<0){
            cur=0;
        }
    }
    return mmax;
}

581.最短无序连续子数组

 给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

请你找出符合题意的 最短 子数组,并输出它的长度。

示例 1:

输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
        解法一:排序比较两个数组,记录不同值的下标 
int cmp(int* a,int* b){
    return *a-*b;
}
int findUnsortedSubarray(int* nums, int numsSize) {
    if(numsSize<=1) return 0;
    int temp[numsSize];
    for(int i=0;i<numsSize;++i){
        temp[i]=nums[i];
    }
    qsort(temp,numsSize,sizeof(int),cmp);
    int left=0,right=0;
    for(int i=0;i<numsSize;++i){
        if(temp[i]!=nums[i]){
            left=i;
            break;
        }
    }
    for(int i=numsSize-1;i>=0;--i){
        if(temp[i]!=nums[i]){
            right=i;
            break;
        }
    }
    if(right==left) return 0;
    return right-left+1;
}
        解法二:一次遍历

        维护一个最大值与最小值。

int findUnsortedSubarray(vector<int>& nums) {
        int n=nums.size();
        int mmax=INT_MIN,mmin=INT_MAX;
        int left=-1,right=-1;
        //维护一个最大值与最小值,如果最大值大于当前值,就更新right的值,小于当前值就更新最大值,最小值同理
        for(int i=0;i<n;++i){
            if(mmax>nums[i]){
                right=i;
            }else{
                mmax=nums[i];
            }
            if(mmin<nums[n-i-1]){
                left=n-i-1;
            }else{
                mmin=nums[n-i-1];
            }
        }
        //如果right没有更新,说明数组有序,返回0
        return right==-1?0:right-left+1;
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值