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
- 存在下标
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 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
。对于每个下标
i
(0 <= 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;
}