模板描述
概念:对数组中具有某特性元素进行统计(如统计位数为偶数的数字,统计不含7或不能被7整除的数字…)
通法:线性枚举数组各元素,依次进行判断统计
典题
实现一个函数,给定一个数组,返回其中十进制为偶数的数的个数
方法:
//判断方法1️⃣
int isEvenBit(int num) {
// (1)将num逐次除以十
int bit = 0; //直到num为零,统计除以10的次数(bit)
while(num) {
//若num为偶数,则其经过偶数次除以10后变为零
num /= 10; //由此我们可以通过bit是否为偶来间接判断num的奇偶
++bit;
}
return (bit % 2 == 0);
}
//------------------------------------------------------
//判断方法2️⃣
int isEvenBit(int num) {
// (1)运用C++的to_string函数
string jd=to_string(num); //及string类的成员函数
int k=jd.size(); //通过num转化字符串的长度判断
return k%2==0;
}
//----------------------------------------------------------
//判断方法3️⃣
int isEvenBit(int num){
return (int)(log10(num)+1)%2==0;//简单的数学方法
}
//-----------------------------
int findNumbers(int* nums, int numsSize){
int i, cnt = 0;
for(i = 0; i < numsSize; ++i) {
// (2)有了判断方法
if(isEvenBit(nums[i])) //我们只需遍历数组
++cnt; //逐次判断统计
}
return cnt;
}
上例题!开始实践!
例题1️⃣:
统计位数为偶数的数字
分析:典型的模板
class Solution {
//一般不过的枚举统计
public:
int findNumbers(vector<int>& nums) {
int cnt=0;
for(int i:nums){
if(to_string(i).size()%2==0)cnt++;
}
return cnt;
}
};
例题2️⃣:
有序数列中的单一元素
分析:思路一:数组元素较小,取一计数数组进行计数,线性枚举两遍
一次计数,一次找出计数为一的元素
思路二:根据数组的特性,取下标为偶数的元素,判断其是否与后一个元素相同
for(int i=0;i<nums.size();i+=2){
if(nums[i]!=nums[i+1])return nums[i];
}
return nums.back();
思路三:虽然该数组未经过排序,不满足二分查找的标准模板,但,要搜索的元素只出现一次,而其他元素都出现两次,所以具备这么明显的标志,仔细思考,该题也能使用二分搜索。
class Solution {
//二分查找模板是从有序数组搜索指定元素
public: //这里同样适用
int singleNonDuplicate(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
while (left <right) {
int mid = left + (right- left) / 2;
if (nums[mid + 1] == nums[mid]) {
if ((right- mid) % 2 == 0) left = mid + 2;
else right= mid - 1;
}
else if (nums[mid - 1] == nums[mid]) {
if ((right- mid) % 2 == 0) right= mid - 2;
else left = mid + 1;
}
else return nums[mid];
}
return nums[left];
}
};
这是我所能想出的最快解法,而官方题解给出了“仅对偶数索引进行二分搜索”,我还是差了一点啊,当然这里不做扩展。
例题3️⃣:
剑指offer 调整数组顺序,是奇数位于偶数前面
class Solution {
public: //创建临时数组,遍历nums,先放奇数后放偶数
vector<int> exchange(vector<int>& nums) {
vector<int>jie;
for(int i=0;i<nums.size();i++){
if(nums[i]&1)jie.push_back(nums[i]);
}
for(int i=0;i<nums.size();i++){
if(!(nums[i]&1))jie.push_back(nums[i]);
}
return jie;
}
};
当然双指针,咳咳,这里还是先不拓展了
例题4️⃣:
找到数组的中间位置
例题5️⃣:
寻找数组的中心下标
(两题相同)
分析:若存在中间元素k,则k左边的元素和sum1等于k右边的元素和sum2,
即sum1+sum2+k=2*sum1+k=sum(数组元素总和)
由此:
class Solution {
public:
int pivotIndex(vector<int> &nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
int sum1 = 0;
for (int i = 0; i < nums.size(); ++i) {
if (2 * sum1 + nums[i] == sum) return i;
sum1 += nums[i];
}
return -1;
}
};
补充:
C++STL,accumulate用法:
函数原型:int accumulate(首地址,尾地址,int val).可以返回从首地址到尾地址的元素总和+val.
例题6️⃣:
删除有序序列中的重复项
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n=nums.size();
if(n==0) return 0; //如果nums元素为零,直接返回0
int tmp=nums[0];
for( vector<int>::iterator it=nums.begin()+1;it!=nums.end();){
if(*it==tmp) {
tmp=*it;nums.erase(i);} //用迭代器遍历数组
else {
tmp = *it;it++;} //如果数组元素*it等于tmp,说明存在重复元素,erase删除
} //否则,更新tmp为*it
return nums.size(); //返回数组长度
}
};
分析:观察发现,这里的01数组长度很大,最坏有2^30000,我们不能直接求出每个N_i,否则计算量过大,会溢出,就算用快速幂,也一样会超时,怎嘛办——
(a+b)%c=(a%c+b%c)%c;
(ab)%c=(a%c)(b%c)%c;
从A[0]开始,N_0=A[0]2^0=A[0]1,设每次结果N_i%5的结果为k_i
N_1=(N_0)*2+A[1]*1;
简化得:
N_1=(N_0%5)*2+A[1]*1=k_0*2+A[1]
由此数学归纳可得:
N_i=(N_(i-1)%5)*2+A[1]*1=k_(i-1)*2+A[i]
这意味着,我们只需计算每一步的k_(i-1)即可推之k_i
而k_i<5,这样我们便可以轻易地将题目解开
class Solution {
public:
vector<bool> prefixesDivBy5(vector<int>& nums) {
vector<bool>res;
int k = 0;
for (int i = 0; i <nums.size(); i++) {
k = (nums[i]+(k << 1) ) % 5;//用上一步的k_(i-1)
res.push_back(k== 0); //推出这一步的k_i,储存结果
}
return res;
}
};
例题8️⃣:
可被k整除的最小整数
分析:和上一题相似的思路
注意:找不到111…是2或5的倍数
class Solution {
public:
int smallestRepunitDivByK(int k) {
if(k%2==0||k%5==0)return -1;//注意,没有这一步会出现死循环
int tmp=0;
for(int i=1;;i++){
tmp=(tmp*10+1)%k; //和上一题相似k%5==
if(tmp==0)return i;
}
}
};
例题9️⃣:
那种连续子字符串更长
分析:这一题较简单,遍历一遍即可
class Solution {
public:
bool checkZeroOnes(string s) {
int t1=0,t0=0;
int max1=0,max0=0;
for(int i=0;i<s.size();i++){
if(s[i]-'0'==1){
t1++;
max0=max(max0,t0); //开始记录
max1=max(max1,t1);//持续更新最大连续长度
t0=0;
}
else{
t0++;
max0=max(max0,t0);
max1=max(max1,t1);
t1=0;
}
}
return max1>max0;
}
};