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;
}