刷题 ------ 枚举

文章目录

  • 1.统计好三元组
  • 2.重复至少k次且长度为M的模式
  • 3.统计平方和三元数组数目
  • 4.统计特殊四元组
  • 5. 找出3为偶数
  • 6.移除指定数字得到的最大结果
  • 7. 兼具大小写的最好英文字母
  • 8. 算术三元组的数目
  • 9.公因子的数目
  • 10.有效的时间数目
  • 11.从两个数字数组里生成最小的数字
  • 12.最长的交替子序列
  • 13.特殊元素平方和
  • 14.统计对称整数的数目
  • 15.给小朋友们分糖果
  • 16.找出峰值
  • 17.统计移除递增子数组的数目|
  • 18.文件组合

这篇博客和上篇一样,既然叫成枚举,那么在做题上也大多采用了暴力枚举的方式去做的,有些更有效的方式也没有去写

1.统计好三元组

刷题 ------ 枚举_第1张图片

  • 嗯,,,,直接三重for循环搞定了吧。
  • 注意如果前两个 i 和 j 已经不满足了,k就没有继续下去的必要了
int countGoodTriplets(int* arr, int arrSize, int a, int b, int c)
{
    int i,j,k,ans = 0;
    for (i = 0; i < arrSize - 2; i++)
    {
        for (j = i + 1; j < arrSize - 1; j++)
        {
            //前俩下标和已经不满足了,后面的就没有必要进行了
            if(abs(arr[i] - arr[j]) > a)
            {
                continue;
            }
            for (k = j + 1; k < arrSize; k++)
            {
                if(abs(arr[j] - arr[k]) <= b && abs(arr[i] - arr[k]) <= c)
                {
                    ans++;
                }
            }
        }
    }
    return ans;
}

2.重复至少k次且长度为M的模式

刷题 ------ 枚举_第2张图片
上篇博客中所解题方式是模拟,这题同样也可以模拟出来。

  • 遍历所有的情况!!
//比较俩数组是否相同
bool Compare(int* nums1, int* nums2, int numsSize)
{
    int i;
    for (i = 0; i < numsSize; i++)
    {
        if(nums1[i] != nums2[i])
        {
            return false;
        }
    }

    return true;
}


bool containsPattern(int* arr, int arrSize, int m, int k)
{
    int left,right,count = 0;;
    int* tmp = (int*)calloc(m, sizeof(int));
    for (int i = 0; i < arrSize - 1; i++)
    {
        for (left = i,right = left + m - 1; right < arrSize; left += m,right += m)
        {
            if(Compare(tmp,arr + left,m))
            {
                count++;
                if(count == k)
                {
                    return true;
                }
            }
            else
            {
                //重置次数,更新数组
                memcpy(tmp,arr + left,sizeof(int) * m);
                count = 1;
            }
        }
        //将tmp数组清空
        memset(tmp,0,sizeof(int) * m);
    }

    return false;
}
  • 下面这种方法,牛的,
  • 我感觉它的精髓就在 arr[i + j] != arr[i + j % m] 这一句话
bool containsPattern(int* arr, int arrSize, int m, int k)
{
    int i,j;
    for (i = 0; i <= arrSize - m * k;i++)
    {
        //
        for (j = 0; j < m*k; j++)
        {
            if(arr[i + j] != arr[i + j % m])
            {
                break;
            }
        }
        //提前出来的话就证明前后不相同,
        if(j == m*k)
        {
            //j全部走完了,就证明全部相等
            return true;
        }
    }

    return false;
}

3.统计平方和三元数组数目

刷题 ------ 枚举_第3张图片

  • 这题肯定也可以使用三层for循环来解决这个。
  • 但是时间上肯定是不快的,我们可以利用C语言的powsqrt函数来帮助实现。
    刷题 ------ 枚举_第4张图片
    上图中两个a和b可以看出来,就是一个精确,一个不精确的意思
int countTriples(int n)
{
    int a,b,c,ans = 0;
    for (a = 3; a < n; a ++)
    {
        for (b = 1; b < n; b++)
        {
            if(a*a + b*b <= n*n)
            {
                if(a*a + b*b == (int)pow((int)sqrt(a*a+b*b),2))
                {
                    ans++;
                }
            }
        }
    }    

    return ans;
}

4.统计特殊四元组

刷题 ------ 枚举_第5张图片

  • 直接枚举,四层for循环就能做出来。
int countQuadruplets(int* nums, int numsSize)
{
    int a,b,c,d,ans = 0;
    for (a = 0; a < numsSize-3; a++)
    {
        for (b = a + 1; b < numsSize-2; b++)
        {
            for (c = b + 1; c < numsSize-1; c++)
            {
                for (d = c + 1; d < numsSize; d++)
                {
                    if(nums[a] + nums[b] + nums[c] == nums[d])
                    {
                        ans++;
                    }
                }
            }
        }
    }

    return ans;
}
  • 也可以利用哈希表,这样可以减少一层for循环
  • 因为要满足 a < b < c < d 这样一个条件,
  • 所以可以倒着遍历。
int countQuadruplets(int* nums, int numsSize)
{
    int map[10000] = {0};
    int a,b,c,ans = 0;
    for (c = numsSize - 2; c >= 2; c--)
    {
        map[nums[c+1]]++;
        for (a = 0; a < c; a++)
        {
            for (b = a + 1; b < c; b++)
            {
                ans += map[nums[a] + nums[b] + nums[c]];
            }
        }
    }

    return ans;
}

5. 找出3为偶数

刷题 ------ 枚举_第6张图片

  • 枚举所有的数
  • 首位不能是0
  • 中间随便,但是不能用首位用过的数
  • 末尾只能是偶数,这样才能保证整体是偶数,但是不能用前两位用过的数。
#define MAX_SIZE 100000

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

int cmp_int(const void* x, const void* y)
{
    return *(int*)x - *(int*)y;
}


int* findEvenNumbers(int* digits, int digitsSize, int* returnSize)
{
    int i,j,k;
    int map[1000] = {0};
    int* ans = (int*)malloc(sizeof(int) * MAX_SIZE);
    int size = 0;
    for (i = 0; i < digitsSize; i++)
    {
        //首位不能是 0 
        if(digits[i] != 0)
        {
            //中间
            for (j = 0; j < digitsSize; j++)
            {
                //不能重复使用一个数字
                if(j == i)
                {
                    continue;
                }
                //末尾
                for(k = 0; k < digitsSize; k++)
                {
                    //不能重复,并且不能是奇数
                    if(k == i || k == j || digits[k] % 2 != 0)
                    {
                        continue;
                    }
                    int num = digits[i] * 100 + digits[j] * 10 + digits[k];
                    //如果出现过就不用再去录入了
                    if(map[num] == 0)
                    {
                        map[num]++;
                        ans[size++] = num;
                    }
                }

            }
        }
    }
    qsort(ans,size,sizeof(ans[0]),cmp_int);
    *returnSize = size;
    return ans;
}

6.移除指定数字得到的最大结果

刷题 ------ 枚举_第7张图片

  • 可以直接枚举出所有的结果,然后去比较哪一个构造出来的更大,从而选择返回哪一个即可
char* removeDigit(char* number, char digit)
{
    int i,len = strlen(number);
    char* ans = (char*)malloc(sizeof(char) * len);
    char* tmp = (char*)malloc(sizeof(char*) * len);
    ans[0] = '\0';
    tmp[0] = '\0';
    for (i = 0; i < len; i++)
    {
        //求出所有可能行
        if(number[i] == digit)
        {
            //前面
            memcpy(tmp,number,sizeof(char) * i);
            //后面
            memcpy(tmp+i,number + i + 1,sizeof(char) * len - i- 1);
            tmp[len-1] = '\0';
            //(判断选择哪一个)新构造出来的比原来的大
            if(strcmp(tmp,ans) > 0)
            {
                strcpy(ans,tmp);
            }
        }
    }
    
    return ans;
}
  • 还有一种办法,不需要这么麻烦,
  • 找到所需要删除的字符,然后这个字符比后一个位置小,那么这就是那个需要删除的下标.
char* removeDigit(char* number, char digit)
{
    int i, len = strlen(number),index = 0;
    for (i = 0; i < len; i++)
    {
        if(number[i] == digit)
        {
            //找到下标
            index = i;
            if(number[i] < number[i + 1])
            {
                //找到合适的下标,当前的这个比后一个要小。
                break;
            }
        }
    }

    for (i = index; i < len; i++)
    {
        number[i] = number[i  + 1];
    }
    
    return number;
}

7. 兼具大小写的最好英文字母

刷题 ------ 枚举_第8张图片

  • 拿两个哈希表来分别记录各个大写字母和小写字母出现的次数。
  • 然后从后往前遍历,如果有一个字母大小写同时出现,那么就是它了。
char* greatestLetter(char* s)
{
    char* ans = (char*)malloc(sizeof(char) * 2);
    ans[0] = '\0';
    int mapSmall[26] = {0};
    int mapBig[26] = {0};
    int i, len = strlen(s);
    for (i = 0; i < len; i++)
    {
        if(s[i] >= 'a' && s[i] <= 'z')
        {
            mapSmall[s[i] - 'a']++;
        }
        else
        {
            //大写
            mapBig[s[i] - 'A']++;
        }
    }

    for (i = 25 ; i >= 0; i--)
    {
        //大写出现过
        if(mapBig[i] != 0 && mapSmall[i] !=0 )
        {
            ans[0] = i + 65;
            ans[1] = '\0';
            return ans;
        }
    }

    return ans;
}

8. 算术三元组的数目

刷题 ------ 枚举_第9张图片

  • 因为nums[k] - nums[j] == diff , nums[j] - nums[i]
  • 所以,可以先确保 j 是满足于 k 的情况下,再去判断 i 是多少
int arithmeticTriplets(int* nums, int numsSize, int diff)
{
    int i,j,k,ans = 0;
    for (k = numsSize-1; k >=  2; k--)
    {
        for (j = k - 1; j >= 1; j--)
        {
            //这样的话就能确定 j 是正确的了
            if(nums[k] - nums[j] == diff)
            {
                for (i = j - 1; i >= 0; i--)
                {
                    if(nums[j] - nums[i] == diff)
                    {
                        ans++;
                    } 
                }
            }
            
        }
    } 

    return ans;
}

9.公因子的数目

刷题 ------ 枚举_第10张图片

  • 嗯。。。。。怎么说
  • 选择出两个数中最小的那个数,然后从遍历到那个数,判断是否能将a 和 b同时模到 0 的就是
int commonFactors(int a, int b)
{
    int ans = 1;
    int min = a > b ? b : a;
    for (int i = 2; i <= min; i++)
    {
        if(a % i == 0 && b % i == 0)
        {
            ans++;
        }
    }


    return ans;    
}

10.有效的时间数目

刷题 ------ 枚举_第11张图片

  • 下面注释中有写特殊的例子
int countTime(char* time)
{
    char a = time[0],b = time[1],c = time[3],d = time[4];
    int ans = 1;
    if(d == '?')
    {
        ans *= 10;
    }

    if(c == '?')
    {
        ans *= 6;
    }

    if(a == '?' && b == '?')
    {
        ans *= 24;
    }
    else if(a == '?')
    {
        if(b < '4')
        {
            //0~3
            //01 02 03 
            //11 12 13
            //21 22 23
            //31 ERROR
            ans *= 3;
        }
        else
        {
            //0~1
            ans *= 2;
        }
    }
    else if(b == '?')
    {
        if(a == '2')
        {
            //0~3
            //20 21 22 23
            ans *= 4;
        }
        else
        {
            //0~9
            ans *= 10;
        }
    }
    else
    {
        ans *= 1;
    }
    
    return ans;
}

11.从两个数字数组里生成最小的数字

刷题 ------ 枚举_第12张图片

  • 求出两个数组中出现相同的数字(如果有多个,选择最小的)可以用哈希表来做
  • 如果没有相同的,那么就算了呗,哈哈哈。
  • 然后在找出两个数组中那两个最小的值所构造出来的数字
  • 使min值与same值去比较,哪一个小,选择哪一个。
int Same(int* nums1, int nums1Size, int* nums2, int nums2Size)
{
    int map[10] = {0};
    int i;
    for (i = 0; i < nums1Size; i++)
    {
        map[nums1[i]]++;
    }
    for (i = 0; i < nums2Size; i++)
    {
        map[nums2[i]]++;
    }
    for (i = 0; i < 10; i++)
    {
        if(map[i] == 2)
        {
            return i;
        }
    }

    return INT_MAX;
}


int minNumber(int* nums1, int nums1Size, int* nums2, int nums2Size)
{
    int ans = 0,i;
    int min1 = nums1[0];
    int min2 = nums2[0];
    //求出相同的数  如果有返回,没有返回一个大值
    int same = Same(nums1,nums1Size,nums2,nums2Size);
    printf("%d\n",same);
    //找出两个数组中最小的值
    for (i = 1; i < nums1Size; i++)
    {
        if(min1 > nums1[i])
        {
            min1 = nums1[i];
        }
    }   
    for (i = 1; i < nums2Size; i++)
    {
        if(min2 > nums2[i])
        {
            min2 = nums2[i];
        }
    }
    //使min1永远最小
    if(min1 > min2)
    {
        int tmp = min2;
        min2 = min1;
        min1 = tmp;
    }
    ans = min1*10 + min2;

    if(same < ans)
    {
        return same;
    }

    return ans;
}

12.最长的交替子序列

刷题 ------ 枚举_第13张图片

  • 我这边的默认长度是1,方便计算子序列的长度,可是如果最后长度还是1的话,记得返回-1
  • 还有就是判断一曾一减的公式 -1^(j + 1)
  • i 每次变化后,j永远是从1开始,就相当于,将(nums+i)看成一个新的数组的其实位置。
    刷题 ------ 枚举_第14张图片

13.特殊元素平方和

刷题 ------ 枚举_第15张图片

  • 额。。。。这题没得说,第一个元素的下标还是0.。
int sumOfSquares(int* nums, int numsSize)
{
    int i,ans = 0;
    for (i = 0; i < numsSize; i++)
    {
        if(numsSize % (i+1) == 0)
        {
            ans += nums[i] * nums[i];
        }
    }

    return ans;
}

14.统计对称整数的数目

刷题 ------ 枚举_第16张图片

  • 这道题,首先得回拆分一个整数,得到它的每一位。
  • 然后将左边位数和和右边的位数和进行比较。
bool Helper(int num)
{
    //1.先求位数
    int n = 0;// n ~ 位数
    int tmp = num;
    while(tmp != 0)
    {
        tmp /= 10;
        n++;
    }
    //如果位数是奇数,一定不会平分对称的
    if(n % 2 != 0)
    {
        return false;
    }

    //2.利用sum1 和 sum2 分别求出右边和左边的总和
    int rightSum = 0,leftSum = 0;
    int count = n / 2;
    while(count != 0)
    {
        //右边
        rightSum += num % 10;
        num /= 10;
        count--;
    }
    count = n / 2;
    while(count != 0)
    {
        //左边
        leftSum += num % 10;
        num /= 10;
        count--;
    }

    //判断
    if(rightSum == leftSum)
    {
        return true;
    }

    return false;
}


int countSymmetricIntegers(int low, int high)
{
    int ans = 0;
    while(low <= high)
    {
        if(Helper(low))
        {
            ans++;
        }
        low++;
    }


    return ans;
}

15.给小朋友们分糖果

刷题 ------ 枚举_第17张图片

  • 就这道题直接三层for肯定是ok的。
  • 但也可以利用一个数组来表示三个孩子,然后一一给他们分糖果,记录其条件满足的次数即可
int distributeCandies(int n, int limit)
{
    int childern[3] = {0};
    int i,j,ans = 0;
    for (i = 0; i <= limit; i++)
    {
        for (j = 0; j <= limit; j++)
        {   
            //给三个孩子分配
            childern[0] = i;
            childern[1] = j;
            childern[2] = n - i - j;
            //前两个孩子肯定是满足的,因为有循环限制
            if(childern[2] <= limit && childern[2] >= 0)
            {
                // printf("%d %d %d \n",childern[0],childern[1],childern[2]);
                ans++;
            }

        }
    }
    return ans;
}

16.找出峰值

刷题 ------ 枚举_第18张图片

  • 第一位和最后一位不可以,所以遍历直接不用遍历这俩就好了
  • 然后去判断当前的数据是否严格大于前一位和后一位即可。
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* findPeaks(int* mountain, int mountainSize, int* returnSize)
{
    int* ans = (int*)malloc(sizeof(int) * (mountainSize / 2));
    int size = 0,i;
    for (i = 1; i < mountainSize - 1; i++)
    {
        if(mountain[i] > mountain[i-1] && mountain[i] > mountain[i+1])
        {
            ans[size++] = i;
        }
    }

    *returnSize = size;
    return ans;
}

17.统计移除递增子数组的数目|

刷题 ------ 枚举_第19张图片

  • 枚举所有的可能,暴力无限循环!!
bool IsIncreas(int* nums,int numsSize)
{
    for (int i = 0; i < numsSize - 1; i++)
    {
        if(nums[i] >= nums[i + 1])
        {
            return false;
        }
    }

    return true;
}


int incremovableSubarrayCount(int* nums, int numsSize)
{
    int i,j,ans = 0;
    for (i = 0; i < numsSize; i++)
    {
        for (j = i; j < numsSize; j++)
        {
            int* tmp = (int*)malloc(sizeof(int) * numsSize);
            int index = 0;
            int k;
            //左边
            for (k = 0; k < i; k++)
            {
                tmp[index++] = nums[k];
            }
            //右边
            for (k = j + 1; k < numsSize; k++)
            {
                tmp[index++] = nums[k];
            }
            //判断是否递增
            if(IsIncreas(tmp,index))
            {
                ans++;
            }
        }
    }

    return ans;
}   

18.文件组合

刷题 ------ 枚举_第20张图片

  • 用暴力+枚举肯定是能做出来的,但是我想到暴力的时候,就又想到了双指针可以优化时间。

  • 就是嗯。。。。。以前肯定做过类似的求和的题,当时就是用双指针来做的。

  • 先模拟出一个数组来
    刷题 ------ 枚举_第21张图片

  • 然后将第一个值赋给sum,然后去判断当前的sum 如果小于target的话,j++,

  • 如果当前的sum 大于 target的话 i++;

  • 这两者有区别的是,i++的时候是从sum中减去当前的值,j++是从sum中加上当前的值

  • 如果两者相等的话,也找到了这一组数据,拷贝过去即可。
    刷题 ------ 枚举_第22张图片

int** fileCombination(int target, int* returnSize, int** returnColumnSizes)
{
    //构造一个数组出来
    int* nums = (int*)malloc(sizeof(int) * target);
    int i,j;
    for (i = 0; i < target; i++)
    {
        nums[i] = i + 1;
    }
    int** ans = (int**)malloc(sizeof(int*) * (target / 2));
    *returnColumnSizes = (int*)malloc(sizeof(int) * (target / 2));
    int size = 0,sum = 0;
    i = 0,j = i + 1;
    sum += nums[i];
    while(i < target / 2)
    {
        
        if(sum < target)
        {
            sum += nums[j];
            j++;
        }
        else if(sum > target)
        {
            sum -= nums[i];
            i++;
        }
        else
        {
            ans[size] = (int*)malloc(sizeof(int) * j - i );
            memcpy(ans[size],nums+i,sizeof(int) * j - i );
            (*returnColumnSizes)[size++] = j - i;
            sum -= nums[i];
            i++;
        }
    }
    *returnSize = size;
    return ans;
}


你可能感兴趣的:(算法,数据结构,leetcode,c语言)