989. 数组形式的整数加法
整数的 数组形式
num
是按照从左到右的顺序表示其数字的数组。例如,对于num = 1321
,数组形式是[1,3,2,1]
。给定
num
,整数的 数组形式 ,和整数k
,返回 整数num + k
的 数组形式 。输入:num = [1,2,0,0], k = 34 输出:[1,2,3,4] 解释:1200 + 34 = 1234
int* addToArrayForm(int* num, int numSize, int k, int* returnSize) {
int* res=malloc(sizeof(int)*100000);
*returnSize=0;
//从后向前进行加
for(int i=numSize-1;i>=0||k>0;--i){
if(i>=0){
k+=num[i];
}
res[(*returnSize)++]=k%10;
k/=10;
}
//翻转
for(int i=0;i<(*returnSize)/2;++i){
int tmp=res[i];
res[i]=res[(*returnSize)-i-1];
res[(*returnSize)-i-1]=tmp;
}
return res;
}
941. 有效的山脉数组
给定一个整数数组
arr
,如果它是有效的山脉数组就返回true
,否则返回false
。让我们回顾一下,如果arr
满足下述条件,那么它是一个山脉数组:arr.length >=3
- 在
0 < i < arr.length - 1
条件下,存在i
使得:
arr[0] < arr[1] < ... arr[i-1] < arr[i]
arr[i] > arr[i+1] > ... > arr[arr.length - 1]
线性扫描,进行模拟。
bool validMountainArray(int* arr, int arrSize){
int i=0;
while(i+1<arrSize&&arr[i]<arr[i+1]){
i++;
}
if(i==0||i==arrSize-1){
return false;
}
while(i+1<arrSize&&arr[i]>arr[i+1]){
i++;
}
return i==arrSize-1;
}
1207. 独一无二的出现次数
给你一个整数数组
arr
,请你帮忙统计数组中每个数的出现次数。如果每个数的出现次数都是独一无二的,就返回true
;否则返回false
。输入:arr = [1,2,2,1,1,3] 输出:true 解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出现次数相同。
int cmp(int* a,int* b){
return *a-*b;
}
bool uniqueOccurrences(int* arr, int arrSize) {
int flag[2002]={0};
for(int i=0;i<arrSize;++i){
if(arr[i]<0){
//-1就放到下标为1001的位置
flag[arr[i]+1002]++;
}else{
flag[arr[i]]++;
}
}
qsort(flag,2002,sizeof(int),cmp);
for(int i=0;i<2001;++i){
if(flag[i]!=0){
if(flag[i]==flag[i+1]){
return false;
}
}
}
return true;
}
1437. 是否所有 1 都至少相隔 k 个元素
给你一个由若干
0
和1
组成的数组nums
以及整数k
。如果所有1
都至少相隔k
个元素,则返回True
;否则,返回False
。输入:nums = [1,0,0,0,1,0,0,1], k = 2 输出:true 解释:每个 1 都至少相隔 2 个元素。
记录 上一个1出现的下标,除了第一个1之外,每次遇到一个1就判断与前一个1的距离。
bool kLengthApart(int* nums, int numsSize, int k) {
int pre=-1;
for(int i=0;i<numsSize;++i){
if(nums[i]==1){
if(pre!=-1&&i-pre-1<k){
return false;
}
pre=i;
}
}
return true;
}
2553. 分割数组中数字的数位
给你一个正整数数组
nums
,请你返回一个数组answer
,你需要将nums
中每个整数进行数位分割后,按照nums
中出现的 相同顺序 放入答案数组中。对一个整数进行数位分割,指的是将整数各个数位按原本出现的顺序排列成数组。比方说,整数
10921
,分割它的各个数位得到[1,0,9,2,1]
。输入:nums = [13,25,83,77] 输出:[1,3,2,5,8,3,7,7]
void reverse(int* res,int l,int r){
while(l<r){
int tmp=res[l];
res[l]=res[r];
res[r]=tmp;
l++;r--;
}
return;
}
int* separateDigits(int* nums, int numsSize, int* returnSize) {
int* res=malloc(sizeof(int)*100000);
int l=0;
for(int i=0;i<numsSize;++i){
if(nums[i]<10){
res[l++]=nums[i];
}else{
int start=l;
int num=nums[i];
while(num){
int cur=num%10;
res[l++]=cur;
num/=10;
}
reverse(res,start,l-1);
}
}
*returnSize=l;
return res;
}
2855. 使数组成为递增数组的最少右移次数
给你一个长度为
n
下标从 0 开始的数组nums
,数组中的元素为 互不相同 的正整数。请你返回让nums
成为递增数组的 最少右移 次数,如果无法得到递增数组,返回-1
。一次 右移 指的是同时对所有下标进行操作,将下标为
i
的元素移动到下标(i + 1) % n
处。输入:nums = [3,4,5,1,2] 输出:2 解释:第一次右移后,nums = [2,3,4,5,1] 。第二次右移后,nums = [1,2,3,4,5] 。现在 nums 是递增数组了,所以答案为 2 。
将数组分成两段,每一段都单调递增,并且前一段严格大于后一段。
int minimumRightShifts(int* nums, int numsSize){
int i=1;
while(i<numsSize&&nums[i-1]<nums[i]){
//找到第一个开始下降的点
i++;
}
if(i==numsSize) return 0; //数列递增
if(nums[0]<nums[numsSize-1]) return -1;//最后一个元素值大于第一个元素值
int mid=i;
i++;
//判断mid之后的元素是否递增
while(i<numsSize&&nums[i-1]<nums[i]) i++;
if(i<numsSize) return -1;
return numsSize-mid;
}
2869. 收集元素的最少操作次数
给你一个正整数数组
nums
和一个整数k
。一次操作中,你可以将数组的最后一个元素删除,将该元素添加到一个集合中。请你返回收集元素1, 2, ..., k
需要的 最少操作次数 。输入:nums = [3,1,5,4,2], k = 2 输出:4 解释:4 次操作后,集合中的元素依次添加了 2 ,4 ,5 和 1 。此时集合中包含元素 1 和 2 ,所以答案为 4 。
int minOperations(int* nums, int numsSize, int k){
int* ans=malloc(sizeof(int)*(k+1));
for(int i=0;i<=k;++i) ans[i]=0;
for(int i=0;i<numsSize;++i){
//记录每个数字的下标位置
if(nums[i]<=k){
ans[nums[i]]=i;
}
}
int mmin=INT_MAX;
//找到符合要求的最小下标位置
for(int i=1;i<=k;++i){
if(ans[i]<mmin){
mmin=ans[i];
}
}
return numsSize-mmin;
}
2970. 统计移除递增子数组的数目 I
给你一个下标从 0 开始的 正 整数数组
nums
。如果
nums
的一个子数组满足:移除这个子数组后剩余元素 严格递增 ,那么我们称这个子数组为 移除递增 子数组。比方说,[5, 3, 4, 6, 7]
中的[3, 4]
是一个移除递增子数组,因为移除该子数组后,[5, 3, 4, 6, 7]
变为[5, 6, 7]
,是严格递增的。请你返回
nums
中 移除递增 子数组的总数目。注意 ,剩余元素为空的数组也视为是递增的。子数组 指的是一个数组中一段连续的元素序列。输入:nums = [1,2,3,4] 输出:10 解释:10 个移除递增子数组分别为:[1], [2], [3], [4], [1,2], [2,3], [3,4], [1,2,3], [2,3,4] 和 [1,2,3,4]。移除任意一个子数组后,剩余元素都是递增的。注意,空数组不是移除递增子数组。
int incremovableSubarrayCount(int* nums, int numsSize) {
int i=0;
int n=numsSize;
while(i<n-1&&nums[i]<nums[i+1]){
i++;
}
if(i==n-1){
//每个非空子数组都可以移除
return n*(n+1)/2;
}
int ans=i+2;
for(int j=n-1;j==n-1||nums[j]<nums[j+1];j--){
while(i>=0&&nums[i]>=nums[j]){
i--;
}
ans+=i+2;
}
return ans;
}
453. 最小操作次数使数组元素相等
给你一个长度为
n
的整数数组,每次操作将会使n - 1
个元素增加1
。返回让数组所有元素相等的最小操作次数。输入:nums = [1,2,3] 输出:3 解释:只需要3次操作(注意每次操作会增加两个元素的值): [1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
int minMoves(int* nums, int numsSize){
int mmin=INT_MAX;
for(int i=0;i<numsSize;++i){
if(nums[i]<mmin){
mmin=nums[i];
}
}
int ans=0;
for(int i=0;i<numsSize;++i){
ans+=nums[i]-mmin;
}
return ans;
}
462. 最小操作次数使数组元素相等 II
给你一个长度为
n
的整数数组nums
,返回使所有数组元素相等需要的最小操作数。在一次操作中,你可以使数组中的一个元素加
1
或者减1
。输入:nums = [1,2,3] 输出:2 解释:只需要两次操作(每次操作指南使一个元素加 1 或减 1): [1,2,3] => [2,2,3] => [2,2,2]
int cmp(int* a,int* b){
return *a-*b;
}
int minMoves2(int* nums, int numsSize) {
qsort(nums,numsSize,sizeof(int),cmp);
int ans=0;
int x=nums[numsSize/2];
for(int i=0;i<numsSize;++i){
ans+=abs(nums[i]-x);
}
return ans;
}
int* nextGreaterElements(int* nums, int numsSize, int* returnSize) {
int *res=malloc(sizeof(int)*numsSize);
*returnSize=numsSize;
memset(res,-1,sizeof(int)*numsSize);
for(int i=0;i<numsSize;++i){
for(int j=i+1;j<i+1+numsSize;++j){
if(nums[j%numsSize]>nums[i]){
res[i]=nums[j%numsSize];
break;
}
}
}
return res;
}
532. 数组中的 k-diff 数对
给你一个整数数组
nums
和一个整数k
,请你在数组中找出 不同的 k-diff 数对,并返回不同的 k-diff 数对 的数目。k-diff 数对定义为一个整数对
(nums[i], nums[j])
,并满足下述全部条件:
0 <= i, j < nums.length
i != j
|nums[i] - nums[j]| == k
输入:nums = [3, 1, 4, 1, 5], k = 2 输出:2 解释:数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。尽管数组中有两个 1 ,但我们只应返回不同的数对的数量。
int cmp(int* a,int* b){
return *a-*b;
}
int findPairs(int* nums, int numsSize, int k) {
int ans=0;
qsort(nums,numsSize,sizeof(int),cmp);
for(int i=0;i<numsSize-1;++i){
//去重
if(i!=0&&nums[i]==nums[i-1]) continue;
int l=i+1,r=numsSize-1;
//二分查找
while(l<r){
int mid=(r-l)/2+l;
if(nums[mid]>=nums[i]+k){
r=mid;
}else{
l=mid+1;
}
}
if(nums[l]==nums[i]+k) ans++;
}
return ans;
}
560. 和为 K 的子数组
给你一个整数数组
nums
和一个整数k
,请你统计并返回 该数组中和为k
的子数组的个数 。子数组是数组中元素的连续非空序列。
输入:nums = [1,1,1], k = 2 输出:2
暴力法: 简单枚举
int subarraySum(int* nums, int numsSize, int k) {
int count=0;
int ans=0;
for(int i=0;i<numsSize;++i){
count=nums[i];
if(count==k){
ans++;
}
for(int j=i+1;j<numsSize;++j){
count+=nums[j];
if(count==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;
}
611. 有效三角形的个数
给定一个包含非负整数的数组
nums
,返回其中可以组成三角形三条边的三元组个数。输入: nums = [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 2) 2,2,3
三重循环:超时
int cmp(int* a,int* b){
return *a-*b;
}
int triangleNumber(int* nums, int numsSize) {
qsort(nums,numsSize,sizeof(int),cmp);
int ans=0;
for(int i=0;i<numsSize;++i){
for(int j=i+1;j<numsSize;++j){
for(int k=j+1;k<numsSize;++k){
if(nums[k]<nums[i]+nums[j]){
ans++;
}else{
break;
}
}
}
}
return ans;
}
673. 最长递增子序列的个数
给定一个未排序的整数数组
nums
, 返回最长递增子序列的个数 。注意 这个数列必须是 严格 递增的。输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
int findNumberOfLIS(int* nums, int numsSize) {
int dp[numsSize];//表示以i结尾的最长递增子序列的长度
int cnt[numsSize];//表示以i结尾的最长递增子序列的个数
int mmax=0,ans=0;
for(int i=0;i<numsSize;++i){
dp[i]=1;
cnt[i]=1;
for(int j=0;j<i;++j){
if(nums[j]<nums[i]){
if(dp[j]+1>dp[i]){
dp[i]=dp[j]+1;//最长的长度发生变化
cnt[i]=cnt[j];//重置计数
}else if(dp[j]+1==dp[i]){
cnt[i]+=cnt[j];//最长子序列个数增加
}
}
}
if(dp[i]>mmax){
//最大长度改变,更新mmax与ans
mmax=dp[i];
ans=cnt[i];
}else if(dp[i]==mmax){
//如果最大长度没变
ans+=cnt[i];
}
}
return ans;
}
179. 最大数
给定一组非负整数
nums
,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。
输入:nums = [10,2] 输出:"210"
int cmp(int* x,int* y){
char s1[101];
char s2[101];
sprintf(s1,"%d%d",*x,*y);
sprintf(s2,"%d%d",*y,*x);
return strcmp(s2,s1);
}
char* largestNumber(int* nums, int numsSize) {
qsort(nums,numsSize,sizeof(int),cmp);
if(nums[0]==0){
return "0";
}
char *res=malloc(sizeof(char)*10000);
memset(res, '\0', 10000);//初始化为结束符
for(int i=0;i<numsSize;++i){
char tmp[101];
sprintf(tmp,"%d",nums[i]);
strcat(res,tmp);
}
return res;
}
229. 多数元素 II
给定一个大小为 n 的整数数组,找出其中所有出现超过
⌊ n/3 ⌋
次的元素。输入:nums = [3,2,3] 输出:[3]
排序后用双指针查找
int cmp(int* a,int* b){
return *a-*b;
}
int* majorityElement(int* nums, int numsSize, int* returnSize) {
qsort(nums,numsSize,sizeof(int),cmp);
int* res=(int*)malloc(sizeof(int)*numsSize);
int sum=0;
int idx=0;
int l=0,r=0;
while(l<numsSize){
sum = 1;
for(r=l+1; r<numsSize; r++) {
if(nums[l] == nums[r]) {
sum++;
} else {
break;
}
}
if(sum>(numsSize/3)) {
res[idx++] = nums[l];
}
l=r;
}
*returnSize=idx;
return res;
}
357. 统计各位数字都不同的数字个数
给你一个整数
n
,统计并返回各位数字都不同的数字x
的个数,其中0 <= x < 10n
。输入:n = 2 输出:91 解释:答案应为除去 11、22、33、44、55、66、77、88、99 外,在 0 ≤ x < 100 范围内的所有数字。 输入:n = 0 输出:1
int countNumbersWithUniqueDigits(int n) {
if(n==0) return 1;
if(n==1) return 10;
int ans=10,cur=9;
//第一位有9种可能,第二位有9种可能,第三位有8种可能,第四位有7种可能
for(int i=0;i<n-1;++i){
cur*=9-i;
ans+=cur;
}
return ans;
}
368. 最大整除子集
给你一个由 无重复 正整数组成的集合
nums
,请你找出并返回其中最大的整除子集answer
,子集中每一元素对(answer[i], answer[j])
都应当满足:
answer[i] % answer[j] == 0
,或answer[j] % answer[i] == 0
如果存在多个有效解子集,返回其中任何一个均可。
输入:nums = [1,2,3] 输出:[1,2] 解释:[1,3] 也会被视为正确答案。
int cmp(int* a,int* b){
return *a-*b;
}
int* largestDivisibleSubset(int* nums, int numsSize, int* returnSize) {
qsort(nums,numsSize,sizeof(int),cmp);
int n=numsSize;
int dp[n];
memset(dp,1,sizeof(int)*n);
int mmax=dp[0],mmaxl=1;
//找出最大子集的个数、以及子集中的最大整数
for(int i=1;i<n;++i){
for(int j=0;j<i;++j){
if(nums[i]%nums[j]==0){
dp[i]=fmax(dp[i],dp[j]+1);
}
}
if(dp[i]>mmaxl){
mmaxl=dp[i];
mmax=nums[i];
}
}
//倒推得最大子集
int *res=malloc(sizeof(int)*n);
int pos=0;
if(mmaxl==1){
res[pos++]=nums[0];
*returnSize=1;
return res;
}
for(int i=n-1;i>=0&&mmaxl>0;--i){
if(dp[i]==mmaxl&&mmax%nums[i]==0){
res[pos++]=nums[i];
mmax=nums[i];
mmaxl--;
}
}
*returnSize=pos;
return res;
}
372. 超级次方
你的任务是计算
ab
对1337
取模,a
是一个正整数,b
是一个非常大的正整数且会以数组形式给出。输入:a = 2, b = [3] 输出:8 输入:a = 2, b = [1,0] 输出:1024
int mypow(int a,int b){
a=a%1337;
int ans=1;
for(int i=0;i<b;++i){
ans*=a;
ans%=1337;
}
return ans;
}
int superPow(int a, int* b, int bSize) {
if(a==1) return 1;
int sum=1;
for(int i=0;i<bSize;++i){
sum=mypow(sum,10);
sum*=mypow(a,b[i]);
sum=sum%1337;
}
return sum;
}
395. 至少有 K 个重复字符的最长子串
给你一个字符串
s
和一个整数k
,请你找出s
中的最长子串, 要求该子串中的每一字符出现次数都不少于k
。返回这一子串的长度。如果不存在这样的子字符串,则返回 0。
输入:s = "aaabb", k = 3 输出:3 解释:最长子串为 "aaa" ,其中 'a' 重复了 3 次。
滑动窗口
int longestSubstring(char* s, int k) {
int ans=0;
int n=strlen(s);
for(int t=1;t<=26;++t){
int l=0,r=0;
int cnt[26]={0};
int type=0;//当前区间内字符种类数量
int less=0;//当前出现次数小于k的字符的数量
while(r<n){
cnt[s[r]-'a']++;
if(cnt[s[r]-'a']==1){
type++;
less++;
}
if(cnt[s[r]-'a']==k){
less--;
}
while(type>t){
cnt[s[l]-'a']--;
if (cnt[s[l]-'a']==k-1) {
less++;
}
if (cnt[s[l]-'a']==0) {
type--;
less--;
}
l++;
}
if(less==0){
ans=fmax(ans,r-l+1);
}
r++;
}
}
return ans;
}
754. 到达终点数字
在一根无限长的数轴上,你站在
0
的位置。终点在target
的位置。你可以做一些数量的移动
numMoves
:
- 每次你可以选择向左或向右移动。
- 第
i
次移动(从i == 1
开始,到i == numMoves
),在选择的方向上走i
步。给定整数
target
,返回 到达目标所需的 最小 移动次数(即最小numMoves
) 。输入: target = 2 输入: target = 3 输出: 3 输出: 2 解释:0+1-2+3=2 解释:0+1+2=3
int reachNumber(int target) {
int sum=0;
int idx=0;
target=abs(target);
while(sum<target){
idx++;
sum+=idx;
}
if(sum==target){
return idx;
}
//当sum-target为偶数时,可以将一个数值的符号改为符号使得等式成立
//当sum-target为奇数,那么就需要继续相加来凑出偶数
while((sum-target)%2!=0){
idx++;
sum+=idx;
}
return idx;
}
762. 二进制表示中质数个计算置位
给你两个整数
left
和right
,在闭区间[left, right]
范围内,统计并返回 计算置位位数为质数 的整数个数。计算置位位数 就是二进制表示中1
的个数。例如,
21
的二进制表示10101
有3
个计算置位。输入:left = 6, right = 10 输出:4 解释: 6 -> 110 (2 个计算置位,2 是质数) 7 -> 111 (3 个计算置位,3 是质数) 9 -> 1001 (2 个计算置位,2 是质数) 10-> 1010 (2 个计算置位,2 是质数) 共计 4 个计算置位为质数的数字。
//判断质数
bool fun(int x){
if(x<2){
return false;
}
for(int i=2;i*i<=x;++i){
if(x%i==0){
return false;
}
}
return true;
}
//统计二进制1的个数
int count(int x){
int ans=0;
while(x){
if(x%2==1){
ans++;
}
x/=2;
}
return ans;
}
int countPrimeSetBits(int left, int right) {
int ans=0;
for(int i=left;i<=right;++i){
if(fun(count(i))){
ans++;
}
}
return ans;
}
845. 数组中的最长山脉
把符合下列属性的数组
arr
称为 山脉数组 :
arr.length >= 3
- 存在下标
i
(0 < 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 ans=0;
while(k<arrSize){
int i1=i,j1=j,k1=k;
if(arr[i1]<arr[j1]&&arr[j1]<arr[k1]){
while(i1>0&&arr[i1-1]<arr[i1]){
i1--;
}
while(k1<arrSize-1&&arr[k1]>arr[k1+1]){
k1++;
}
ans=fmax(ans,k1-i1+1);
}
i++;j++;k++;
}
return ans;
}
424. 替换后的最长重复字符
给你一个字符串
s
和一个整数k
。你可以选择字符串中的任一字符,并将其更改为任何其他大写英文字符。该操作最多可执行k
次。在执行上述操作后,返回 包含相同字母的最长子字符串的长度。
输入:s = "ABAB", k = 2 输出:4 解释:用两个'A'替换为两个'B',反之亦然。
int characterReplacement(char * s, int k){
int flag[26]={0};
int n=strlen(s);
int mmax=0;
int l=0,r=0;
while(r<n){
flag[s[r]-'A']++;
mmax=fmax(mmax,flag[s[r]-'A']);//记录最大字符的数量
//当前长度大于最大字符长度+k
if(r-l+1>mmax+k){
flag[s[l]-'A']--;
l++;
}
r++;
}
return r-l;
}
520. 检测大写字母
我们定义,在以下情况时,单词的大写用法是正确的:
- 全部字母都是大写,比如
"USA"
。- 单词中所有字母都不是大写,比如
"leetcode"
。- 如果单词不只含有一个字母,只有首字母大写, 比如
"Google"
。给你一个字符串
word
。如果大写用法正确,返回true
;否则,返回false
。输入:word = "USA" 输入:word = "FlaG" 输出:true 输出:false
只有三种情况:
1、全是大写字母
2、 没有大写字母
3、只有一个大写字母,并且只存在在第一个位置
bool detectCapitalUse(char* word) {
int len=strlen(word);
int res=0;
int idx=-1;
if(len==1) return true;
for(int i=0;i<len;++i){
if(word[i]>='A'&&word[i]<='Z'){
res++;//记录大写字母的个数
idx=i;//记录最后一个大写字母的下标
}
}
return res==len||res==0||(res==1&&idx==0);
}
438. 找到字符串中所有字母异位词
给定两个字符串
s
和p
,找到s
中所有p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。异位词 指由相同字母重排列形成的字符串(包括相同的字符串)
输入: s = "cbaebabacd", p = "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。 起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
bool check(int *cnt1,int *cnt2){
for(int i=0;i<26;++i){
if(cnt1[i]!=cnt2[i]){
return false;
}
}
return true;
}
int* findAnagrams(char * s, char * p, int* returnSize){
int cnt1[26]={0},cnt2[26]={0};
int n=strlen(s),m=strlen(p);
for(int i=0;i<m;++i){
cnt2[p[i]-'a']++;
}
int pos=0;
int* ans=malloc(sizeof(int)*n);
for(int l=0,r=0;r<n;++r){
cnt1[s[r]-'a']++;
//窗口长度超过m
if(r-l+1>m){
cnt1[s[l]-'a']--;
l++;
}
//如果窗口内的字母与p一样,就将下标记录
if(check(cnt1,cnt2)){
ans[pos++]=l;
}
*returnSize=pos;
}
return ans;
}
1838. 最高频元素的频数
元素的 频数 是该元素在一个数组中出现的次数。
给你一个整数数组
nums
和一个整数k
。在一步操作中,你可以选择nums
的一个下标,并将该下标对应元素的值增加1
。执行最多
k
次操作后,返回数组中最高频元素的 最大可能频数 。输入:nums = [1,2,4], k = 5 输出:3 解释:对第一个元素执行 3 次递增操作,对第二个元素执 2 次递增操作,此时 nums = [4,4,4] 。 4 是数组中最高频元素,频数是 3 。
滑动窗口
int cmp(int* a,int* b){
return *a-*b;
}
int maxFrequency(int* nums, int numsSize, int k) {
qsort(nums,numsSize,sizeof(int),cmp);
long long total=0;
int l=0,ans=1;
for(int r=1;r<numsSize;++r){
//r之前的数都是一样的
total+=(long long)(nums[r]-nums[r-1])*(r-l);
while(total>k){
total-=nums[r]-nums[l];//减去添加l所需要增加的步数
l++;
}
ans=fmax(ans,r-l+1);
}
return ans;
}
下一个更大系列:
31. 下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
arr = [1,2,3]
,以下这些都可以视作arr
的排列:[1,2,3]
、[1,3,2]
、[3,1,2]
、[2,3,1]
。整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,
arr = [1,2,3]
的下一个排列是[1,3,2]
。- 类似地,
arr = [2,3,1]
的下一个排列是[3,1,2]
。- 而
arr = [3,2,1]
的下一个排列是[1,2,3]
,因为[3,2,1]
不存在一个字典序更大的排列。输入:nums = [1,2,3] 输出:[1,3,2]
void swap(int *a,int *b){
int tmp=*a;
*a=*b;
*b=tmp;
}
void reverse(int* nums,int i,int j){
int l=i,r=j;
while(l<r){
swap(&nums[l],&nums[r]);
l++;r--;
}
}
void nextPermutation(int* nums, int numsSize) {
int i=numsSize-2;
//找第一个(小,大)的排序
while(i>=0&&nums[i]>=nums[i+1]){
i--;
}
//已经是从大到小排列了
if(i==-1){
reverse(nums,0,numsSize-1);
return;
}
int j=numsSize-1;
//从后往前,找第一个比nums[i]大的数字
while(j>=0&&nums[j]<=nums[i]){
j--;
}
//交换
swap(&nums[i],&nums[j]);
//将i以后的数字进行逆置
reverse(nums,i+1,numsSize-1);
}
496. 下一个更大元素 I
nums1
中数字x
的 下一个更大元素 是指x
在nums2
中对应位置 右侧 的 第一个 比x
大的元素。给你两个 没有重复元素 的数组
nums1
和nums2
,下标从 0 开始计数,其中nums1
是nums2
的子集。对于每个
0 <= i < nums1.length
,找出满足nums1[i] == nums2[j]
的下标j
,并且在nums2
确定nums2[j]
的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是-1
。返回一个长度为
nums1.length
的数组ans
作为答案,满足ans[i]
是如上所述的 下一个更大元素 。输入:nums1 = [4,1,2], nums2 = [1,3,4,2]. 输出:[-1,3,-1] 解释:nums1 中每个值的下一个更大元素如下所述: - 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。 - 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。 - 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
int* nextGreaterElement(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
int* ans=malloc(sizeof(int)*nums1Size);
for(int i=0;i<nums1Size;++i){
int j=0;
while(j<nums2Size&&nums1[i]!=nums2[j]){
j++;
}
int k=j+1;
while(k<nums2Size&&nums1[i]>nums2[k]){
k++;
}
ans[i]=k<nums2Size?nums2[k]:-1;
}
*returnSize=nums1Size;
return ans;
}
503. 下一个更大元素 II
给定一个循环数组
nums
(nums[nums.length - 1]
的下一个元素是nums[0]
),返回nums
中每个元素的 下一个更大元素 。数字
x
的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出-1
。输入: nums = [1,2,1] 输出: [2,-1,2] 解释: 第一个 1 的下一个更大的数是 2; 数字 2 找不到下一个更大的数; 第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
int* nextGreaterElements(int* nums, int numsSize, int* returnSize) {
int* ans=malloc(sizeof(int)*numsSize);
memset(ans,-1,sizeof(int)*numsSize);
*returnSize=numsSize;
for(int i=0;i<numsSize;++i){
for(int j=i+1;j<i+1+numsSize;++j){
if(nums[i]<nums[j%numsSize]){
ans[i]=nums[j%numsSize];
break;
}
}
}
return ans;
}
556. 下一个更大元素 III
给你一个正整数
n
,请你找出符合条件的最小整数,其由重新排列n
中存在的每位数字组成,并且其值大于n
。如果不存在这样的正整数,则返回-1
。注意 ,返回的整数应当是一个 32 位整数 ,如果存在满足题意的答案,但不是 32 位整数 ,同样返回
-1
。输入:n = 12 输出:21
第一步:先找到第一个非递减的排列,如51432中,要找到1,4
第二步:再找到第一个大于1的数字2,并与其交换,52431
第三步:逆转后面的位数,得到52134
void swap(char* a,char* b){
char tmp=*a;
*a=*b;
*b=tmp;
}
int nextGreaterElement(int n) {
char nums[32];
sprintf(nums,"%d",n);
int i=strlen(nums)-2;
//找第一个非递减的i
while(i>=0&&nums[i]>=nums[i+1]){
i--;
}
//从大到小的排列
if(i<0) return -1;
int j=strlen(nums)-1;
//找到第一个大于nums[i]的j
while(j>=0&&nums[i]>=nums[j]){
j--;
}
//交换
swap(&nums[i],&nums[j]);
//将i后面的数字反转
int l=i+1,r=strlen(nums)-1;
while(l<r){
swap(&nums[l],&nums[r]);
l++;r--;
}
long long ans=atol(nums);
if(ans>INT_MAX||ans<INT_MIN) return -1;
return ans;
}
面试题 05.04. 下一个数
下一个数。给定一个正整数,找出与其二进制表达式中1的个数相同且大小最接近的那两个数(一个略大,一个略小)。
输入:num = 2(或者0b10) 输出:[4, 1] 或者([0b100, 0b1])
暴力解:距离最近的两个数,加减之后判断即可,但是会溢出
int count(long long n){
int ans=0;
while(n){
if(n%2==1){
ans++;
}
n/=2;
}
return ans;
}
int* findClosedNumbers(int num, int* returnSize){
int *ans=malloc(sizeof(int)*2);
*returnSize=2;
long long n=num;
ans[0]=-1;ans[1]=-1;
while(n){
n++;
if(count(n)==count(num)){
ans[0]=n;
break;
}
}
n=num;
while(n){
n--;
if(count(n)==count(num)){
ans[1]=n;
break;
}
}
return ans;
}
2048. 下一个更大的数值平衡数
如果整数
x
满足:对于每个数位d
,这个数位 恰好 在x
中出现d
次。那么整数x
就是一个 数值平衡数 。给你一个整数
n
,请你返回 严格大于n
的 最小数值平衡数输入:n = 1000 输出:1333 解释: 1333 是一个数值平衡数,因为: - 数字 1 出现 1 次。 - 数字 3 出现 3 次。
bool check(int n){
int count[10]={0};
while(n){
count[n%10]++;
n/=10;
}
for(int i=0;i<10;++i){
if(count[i]>0&&count[i]!=i){
return false;
}
}
return true;
}
int nextBeautifulNumber(int n){
for(int i=n+1;i<INT_MAX;++i){
if(check(i)){
return i;
}
}
return -1;
}
1432. 改变一个整数能得到的最大差值
给你一个整数
num
。你可以对它进行如下步骤恰好 两次 :
- 选择一个数字
x (0 <= x <= 9)
.- 选择另一个数字
y (0 <= y <= 9)
。数字y
可以等于x
。- 将
num
中所有出现x
的数位都用y
替换。- 得到的新的整数 不能 有前导 0 ,得到的新整数也 不能 是 0 。
请你返回
a
和b
的 最大差值 。输入:num = 555 输出:888 解释:第一次选择 x = 5 且 y = 9 ,并把得到的新数字保存在 a 中。 第二次选择 x = 5 且 y = 1 ,并把得到的新数字保存在 b 中。 现在,我们有 a = 999 和 b = 111 ,最大差值为 888
枚举x与y,模拟,记录最大值与最小值,然后返回差值
int maxDiff(int num) {
int mmin=num;
int mmax=num;
//枚举xy
for(int x=0;x<10;++x){
for(int y=0;y<10;++y){
//用y替换x
char s[100];
sprintf(s,"%d",num);
for(int i=0;i<strlen(s);++i){
if(s[i]-'0'==x){
s[i]='0'+y;
}
}
//判断是否有前导0
if(s[0]!='0'){
int res=atoi(s);
mmin=fmin(mmin,res);
mmax=fmax(mmax,res);
}
}
}
return mmax-mmin;
}
1493. 删掉一个元素以后全为 1 的最长子数组
给你一个二进制数组
nums
,你需要从中删掉一个元素。请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。
如果不存在这样的子数组,请返回 0 。
输入:nums = [1,1,0,1] 输出:3 解释:删掉位置 2 的数后,[1,1,1] 包含 3 个 1 。
int longestSubarray(int* nums, int numsSize) {
int ans=0;
int zero=0;
int l=0;
for(int r=0;r<numsSize;++r){
if(nums[r]==0){
zero++;
}
while(zero>1){
if(nums[l]==0) zero--;
l++;
}
ans=fmax(ans,r-l);//要减去一个位置,因为必须删除一个元素
}
return ans;
}
2962. 统计最大元素出现至少 K 次的子数组
给你一个整数数组
nums
和一个 正整数k
。请你统计有多少满足 「
nums
中的 最大 元素」至少出现k
次的子数组,并返回满足这一条件的子数组的数目。子数组是数组中的一个连续元素序列。
输入:nums = [1,3,2,3,3], k = 2 输出:6
long long countSubarrays(int* nums, int numsSize, int k) {
long long ans=0;
int mmax=0;
//找出最大值
for(int i=0;i<numsSize;++i){
if(nums[i]>mmax){
mmax=nums[i];
}
}
int cnt=0,l=0;
for(int r=0;r<numsSize;++r){
if(nums[r]==mmax){
cnt++;
}
//当满足条件,让l后移计数
while(cnt==k){
if(nums[l]==mmax){
cnt--;
}
l++;
}
ans+=l;
}
return ans;
}
3011. 判断一个数组是否可以变为有序
给你一个下标从 0 开始且全是 正 整数的数组
nums
。一次 操作 中,如果两个 相邻 元素在二进制下数位为 1 的数目 相同 ,那么你可以将这两个元素交换。你可以执行这个操作 任意次 (也可以 0 次)。
如果你可以使数组变有序,请你返回
true
,否则返回false
。输入:nums = [8,4,2,30,15] 输出:true 解释:我们先观察每个元素的二进制表示。 2 ,4 和 8 分别都只有一个数位为 1 ,分别为 "10" ,"100" 和 "1000" 。15 和 30 分别有 4 个数位为 1 :"1111" 和 "11110" 。
正难则反:找到一对不满足条件(i,j)找一个数比另一个数小,但位数不同,返回false,若找不到就返回true。
int count(int n){
int ans=0;
while(n){
if(n%2==1){
ans++;
}
n/=2;
}
return ans;
}
bool canSortArray(int* nums, int numsSize) {
//正难则反:找一个数比另一个数小,但位数不同
for(int i=0;i<numsSize;++i){
for(int j=i+1;j<numsSize;++j){
if(nums[i]>nums[j]&&count(nums[i])!=count(nums[j])){
return false;
}
}
}
return true;
}
直接法:分组循环
int count(int n){
int ans=0;
while(n){
ans+=n%2;
n/=2;
}
return ans;
}
bool canSortArray(int* nums, int numsSize) {
//对于每一段,如果这一段的每个数,都大于等于上一段的最大值
//那么就能把数组排成递增的,否则不行
int i=0,pre_max=0;
while(i<numsSize){
int mmax=nums[i];
int one=count(mmax);
//二进制下数位为1的数目相同且临近的为一组,每次记录最大值
while(i<numsSize&&count(nums[i])==one){
if(nums[i]<pre_max){
return false;
}
mmax=fmax(mmax,nums[i]);
i++;
}
pre_max=mmax;
}
return true;
}
1144. 递减元素使数组呈锯齿状
给你一个整数数组
nums
,每次 操作 会从中选择一个元素并 将该元素的值减少 1。如果符合下列情况之一,则数组
A
就是 锯齿数组:
- 每个偶数索引对应的元素都大于相邻的元素,即
A[0] > A[1] < A[2] > A[3] < A[4] > ...
- 或者,每个奇数索引对应的元素都大于相邻的元素,即
A[0] < A[1] > A[2] < A[3] > A[4] < ...
返回将数组
nums
转换为锯齿数组所需的最小操作次数。输入:nums = [1,2,3] 输出:2 解释:我们可以把 2 递减到 0,或把 3 递减到 1。
分奇偶下标进行计算,返回两者中的较小值
int movesToMakeZigzag(int* nums, int numsSize) {
int res1=0,res2=0;
//偶数下标
for(int i=0;i<numsSize;i+=2){
int a=0;
if(i-1>=0){
a=fmax(a,nums[i]-nums[i-1]+1);
}
if(i+1<numsSize){
a=fmax(a,nums[i]-nums[i+1]+1);
}
res1+=a;
}
//奇数下标
for(int i=1;i<numsSize;i+=2){
int a=0;
if(i-1>=0){
a=fmax(a,nums[i]-nums[i-1]+1);
}
if(i+1<numsSize){
a=fmax(a,nums[i]-nums[i+1]+1);
}
res2+=a;
}
return fmin(res1,res2);
}
2294. 划分数组使最大差为 K
给你一个整数数组
nums
和一个整数k
。你可以将nums
划分成一个或多个 子序列 ,使nums
中的每个元素都 恰好 出现在一个子序列中。在满足每个子序列中最大值和最小值之间的差值最多为
k
的前提下,返回需要划分的 最少 子序列数目。子序列 本质是一个序列,可以通过删除另一个序列中的某些元素(或者不删除)但不改变剩下元素的顺序得到。
输入:nums = [3,6,1,2,5], k = 2 输出:2 解释: 可以将 nums 划分为两个子序列 [3,1,2] 和 [6,5] 。 第一个子序列中最大值和最小值的差值是 3 - 1 = 2 。 第二个子序列中最大值和最小值的差值是 6 - 5 = 1 。 由于创建了两个子序列,返回 2 。可以证明需要划分的最少子序列数目就是 2 。
由于此题求的是分组的个数,虽然要求的是子序列,但是依旧可以先排序然后进行统计
int cmp(int* a,int* b){
return *a-*b;
}
int partitionArray(int* nums, int numsSize, int k) {
qsort(nums,numsSize,sizeof(int),cmp);
int ans=0;
int l=0,r=0;
while(r<numsSize){
while(r<numsSize&&(nums[r]-nums[l]<=k)) r++;//注意每次r会多移一位
ans++;
l=r;
}
return ans;
}
2348. 全 0 子数组的数目
给你一个整数数组
nums
,返回全部为0
的 子数组 数目。子数组 是一个数组中一段连续非空元素组成的序列。
输入:nums = [1,3,0,0,2,0,0,4] 输出:6 解释: 子数组 [0] 出现了 4 次。 子数组 [0,0] 出现了 2 次。 不存在长度大于 2 的全 0 子数组,所以我们返回 6 。
要统计全为0的子数组,就是要统计连续0的个数,累加到答案中。
long long zeroFilledSubarray(int* nums, int numsSize) {
int l=0,r=0;
long long ans=0;
long long zero=0;
while(r<numsSize){
if(nums[r]==0){
zero++;
ans+=zero;
}else{
zero=0;
}
r++;
}
return ans;
}
2384. 最大回文数字
给你一个仅由数字(
0 - 9
)组成的字符串num
。请你找出能够使用num
中数字形成的 最大回文 整数,并以字符串形式返回。该整数不含 前导零 。输入:num = "444947137" 输出:"7449447" 解释: 从 "444947137" 中选用数字 "4449477",可以形成回文整数 "7449447" 。 可以证明 "7449447" 是能够形成的最大回文整数。
char* largestPalindromic(char* num) {
int n=strlen(num);
int flag[10]={0};
//统计数字个数
for(int i=0;i<n;++i){
flag[num[i]-'0']++;
}
int j=0;
//大于2的数字进行统计
for(int i=9;i>0;--i){
while(flag[i]>1){
num[j]=i+'0';
j++;
flag[i]-=2;
}
}
//对0单独处理
if(flag[0]>1&&j>0){
while(flag[0]>=2){
num[j]='0';
j++;
flag[0]-=2;
}
}
//对奇数个进行处理
int x=0;
for(int i=9;i>=0;--i){
if(flag[i]!=0){
num[j++]=i+'0';
x=1;
break;
}
}
int i=j;
//对后半段进行处理
for(i=j;i<n;++i){
if(j-1>=0&&x==0){
//偶数个
num[i]=num[j-1];
}else if(j-2>=0&&x==1){
//奇数个
num[i]=num[j-2];
}else{
break;
}
j--;
}
num[i]='\0';
return num;
}
2396. 严格回文的数字
如果一个整数
n
在b
进制下(b
为2
到n - 2
之间的所有整数)对应的字符串 全部 都是 回文的 ,那么我们称这个数n
是 严格回文 的。给你一个整数
n
,如果n
是 严格回文 的,请返回true
,否则返回false
。如果一个字符串从前往后读和从后往前读完全相同,那么这个字符串是 回文的 。
输入:n = 9 输出:false 解释:在 2 进制下:9 = 1001 ,是回文的。 在 3 进制下:9 = 100 ,不是回文的。 所以,9 不是严格回文数字,我们返回 false 。 注意在 4, 5, 6 和 7 进制下,n = 9 都不是回文的。
bool check(int n,int x){
int ans[100]={0};
int cnt=0;
while(n){
ans[cnt++]=n%x;
n/=x;
}
for(int i=0;i<cnt;++i){
if(ans[i]!=ans[cnt-i-1]){
return false;
}
}
return true;
}
bool isStrictlyPalindromic(int n) {
for(int i=2;i<=n-2;++i){
if(check(n,i)==false){
return false;
}
}
return true;
}
2443. 反转之后的数字和
给你一个 非负 整数
num
。如果存在某个 非负 整数k
满足k + reverse(k) = num
,则返回true
;否则,返回false
。
reverse(k)
表示k
反转每个数位后得到的数字。输入:num = 443 输出:true 解释:172 + 271 = 443 ,所以返回 true 。
int reverse(int n){
int ans=0;
while(n){
ans=ans*10+n%10;
n/=10;
}
return ans;
}
bool sumOfNumberAndReverse(int num) {
if(num==0) return true;
for(int i=0;i<num;++i){
if(i+reverse(i)==num){
return true;
}
}
return false;
}
2442. 反转之后不同整数的数目
给你一个由 正 整数组成的数组
nums
。你必须取出数组中的每个整数,反转其中每个数位,并将反转后得到的数字添加到数组的末尾。这一操作只针对nums
中原有的整数执行。返回结果数组中 不同 整数的数目。输入:nums = [1,13,10,12,31] 输出:6 解释:反转每个数字后,结果数组是 [1,13,10,12,31,1,31,1,21,13] 。 反转后得到的数字添加到数组的末尾并按斜体加粗表示。注意对于整数 10 ,反转之后会变成 01 ,即 1 数组中不同整数的数目为 6(数字 1、10、12、13、21 和 31)。
int cmp(int* a,int* b){
return *a-*b;
}
int reverse(int n){
int ans=0;
while(n){
ans=ans*10+n%10;
n/=10;
}
return ans;
}
int countDistinctIntegers(int* nums, int numsSize) {
int* ans=malloc(sizeof(int)*(2*numsSize));
int pos=0;
for(int i=0;i<numsSize;++i){
ans[pos++]=nums[i];
ans[pos++]=reverse(nums[i]);
}
qsort(ans,2*numsSize,sizeof(int),cmp);
int cnt=1;
for(int i=1;i<2*numsSize;++i){
if(ans[i-1]!=ans[i]){
cnt++;
}
}
return cnt;
}
1509. 三次操作后最大值与最小值的最小差
给你一个数组
nums
。每次操作你可以选择
nums
中的任意一个元素并将它改成 任意值 。在 执行最多三次移动后 ,返回
nums
中最大值与最小值的最小差值。输入:nums = [1,5,0,10,14] 输出:1 解释:我们最多可以走 3 步。 第一步,将 5 改为 0 。 nums变成 [1,0,0,10,14] 。 第二步,将 10 改为 0 。 nums变成 [1,0,0,0,14] 。 第三步,将 14 改为 1 。 nums变成 [1,0,0,0,1] 。 执行 3 步后,最小值和最大值之间的差值为 1 - 0 = 1 。 可以看出,没有办法可以在 3 步内使差值变为0。
int cmp(int* a,int* b){
return *a-*b;
}
int minDifference(int* nums, int numsSize) {
if(numsSize<=3) return 0;
qsort(nums,numsSize,sizeof(int),cmp);
int ans=INT_MAX;
//删去最小的k个数,还需要删去3−k个最大值
for(int i=0;i<4;++i){
ans=fmin(ans,nums[numsSize-4+i]-nums[i]);
}
return ans;
}