本文是(第18讲) 线性枚举(二) - 统计法入门所涉及课后习题的解答,发帖来记录自己关于这些题的理解和做题过程中思路,如果有错误,欢迎批评指正。
在开始之前,首先 恭喜EDG!鏖战五局,为LPL捧回总冠军奖杯。
好了,开始今天的刷题。
1.统计位数为偶数的数字
题目:给你一个整数数组 nums
,请你返回其中位数为 偶数 的数字的个数。
主要思路:可以利用字符串取数字位数或者数学方法取数字位数。
代码:
class Solution {
public:
//利用字符串取数字位数
// bool bitIsEven(int n){
// string s=to_string(n);
// return s.size()%2==0? true : false;
// }
//利用数学方法取数字位数
bool bitIsEven(int n){
int bit=0;
while(n){
n/=10;
bit++;
}
return bit%2==0? true:false;
}
int findNumbers(vector<int>& nums) {
int ans=0;
for(auto num:nums){
if(bitIsEven(num)){
ans++;
}
}
return ans;
}
};
2.有序数组中的单一元素
题目:给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
主要思路:利用异或运算,两个相同的数异或为0,将数组中所有数异或,即可得到唯一的那个数。
代码:
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int ans=0;
for(auto num:nums){
ans^=num;
}
return ans;
}
};
3.调整数组顺序使奇数位于偶数前面
题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
主要思路:使用双指针,从头尾向中间遍历,当前面的数为奇数时,左指针右移;当后面的数为偶数时,右指针左移;当前偶后奇时,交换两数。最后将结果数组返回。
指针:
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
int i=0,j=nums.size()-1;
while(i<j){
if(nums[i]%2==0 && nums[j]%2==1){
nums[i]=nums[i]+nums[j];
nums[j]=nums[i]-nums[j];
nums[i]=nums[i]-nums[j];
i++;
j--;
}
else if(nums[i]%2==1){
i++;
}else if(nums[j]%2==0){
j--;
}
}
return nums;
}
};
4.找到数组的中间位置 & 寻找数组的中心下标
题目:给你一个下标从 0 开始的整数数组 nums ,请你找到 最左边 的中间位置 middleIndex,中间位置即为数组中该位置左边和与右边和相等,将左边界的左边和和右边界的右边和视为0。
主要思路:先求出数组总和sum,从头开始遍历每个数,并计算当前位置i左边部分的和leftSum,当leftSum=sum-nums[i]-leftSum时(即2*leftSum+nums[i]=sum时),该位置即为要求的中间位置。
代码:
class Solution {
public:
int findMiddleIndex(vector<int>& nums) {
int sum=accumulate(nums.begin(),nums.end(),0);
int leftSum=0;
for(int i=0;i<nums.size();i++){
if(2*leftSum+nums[i]==sum){
return i;
}
leftSum+=nums[i];
}
return -1;
}
};
5.删除有序数组中的重复项
题目:给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
主要思路:使用快慢指针,快指针用于遍历每个数,慢指针用于将不重复的数前移。
代码:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int i=1,j=1;
if(nums.size()==0){
return 0;
}
while(j<nums.size()){
if(nums[j]!=nums[j-1]){
nums[i]=nums[j];
i++;
}
j++;
}
return i;
}
};
6.可被5整除的二进制前缀
题目:给定由若干 0 和 1 组成的数组 A。我们定义 N_i:从 A[0] 到 A[i] 的第 i 个子数组被解释为一个二进制数(从最高有效位到最低有效位)。返回布尔值列表 answer,只有当 N_i 可以被 5 整除时,答案 answer[i] 为 true,否则为 false。
主要思路:遍历数组,使用temp变量,通过累加公式temp=temp*2+nums[i]来表示当前遍历到的子数组所代表的二进制值,例如,对nums=[1,1,1] temp=0,有
i=0时, temp=0*2+1=1
i=1时,temp=1*2+1=3
i=2时,temp=3*2+1=7
然后不断循环,不断判断是否能被5整除,并将结果存入ans数组中。
需要注意的是,由于代码中采用了累加的方法,所以可能溢出,溢出产生于公式temp=temp2+nums[i]中temp2部分,所以让其对5取余,可以防止溢出。前面有学习过,(ab)%c=(a%c)(b%c)%c,所以temp=temp*2+nums[i]=((temp%5)*2)%5+nums[i]。
为什么这样可以呢?拿14举例,14+1=(72)+1=15可以被5整除,(7%52)%5+1=5可以被5整除,即若temp2+nums[i]可以被5整除,那么(temp2)%5+nums[i]也一定能被5整除。
代码:
class Solution {
public:
vector<bool> prefixesDivBy5(vector<int>& nums) {
int temp=0;
vector<bool> ans;
for(int i=0;i<nums.size();i++){
temp=((temp%5)*2)%5+nums[i];
if(temp%5==0){
ans.push_back(true);
}
else{
ans.push_back(false);
}
}
return ans;
}
};
7.可被k整除的最小整数
题目:给定正整数k,你需要找出可以被 k整除的、仅包含数字1 的最小正整数 N。返回 N的长度。如果不存在这样的 N,就返回 -1。
代码:
class Solution {
public:
int smallestRepunitDivByK(int k) {
if(k%2==0 || k%5==0){
return -1;
}
int ans=1,temp=1;
while(temp%k){
temp%=k; //防止溢出
temp=temp*10+1;
ans++;
}
return ans;
}
}; //枚举法
8.哪种连续子串更长
题目:给你一个二进制字符串s。如果字符串中由1组成的 最长 连续子字符串 严格长于 由 0组成的 最长 连续子字符串,返回 true ;否则,返回 false 。
主要思路:遍历数组,分别统计连续的1和0的长度,最后比较即可。
代码:
class Solution {
public:
bool checkZeroOnes(string s) {
int m=0,n=0,one=0,zero=0;
for(int i=0;i<s.size();i++){
if(s[i]=='1'){
m++;
if(i!=s.size()-1 && s[i]!=s[i+1]){
one=max(one,m);
m=0;
}
}else{
n++;
if(i!=s.size()-1 && s[i]!=s[i+1]){
zero=max(zero,n);
n=0;
}
}
}
one=max(one,m);
zero=max(zero,n);
return one>zero;
}
};