七月集训------哈希表

本文介绍了使用哈希表解决四个不同的算法问题:强整数、卡牌分组、检查数组对是否能被k整除以及字母与数字的子数组。通过哈希表存储和分析数据,找到解决问题的关键思路并提供了相应的代码实现。
摘要由CSDN通过智能技术生成

 

目录

题目一:970. 强整数

问题思路:

题目二:914. 卡牌分组

问题思路:

题目三:1497. 检查数组对是否可以被 k 整除

问题思路:

题目四: 面试题 17.05. 字母与数字

 解题思路:



题目一:970. 强整数

问题描述:

给定三个整数 x 、 y 和 bound ,返回 值小于或等于 bound 的所有 强整数 组成的列表 。

如果某一整数可以表示为 xi + yj ,其中整数 i >= 0 且 j >= 0,那么我们认为该整数是一个 强整数 。

你可以按 任何顺序 返回答案。在你的回答中,每个值 最多 出现一次。

  • 0 <= bound <= 10^6

问题思路:

  1. 因为题目所给的bound值小于10的六次方,而2的20次方是1048756,并且x,y的值都大于等于2,因此建立一个res1[ ]与res2[ ]存储x与y不同次方的结果 。(两个数组的大小都等于21,代表从x^0,x^1....x^21与y^0,y^1,y^2...y^21的值)
  2. 建立两个for循环,判断result = res[i]与res[j]相加是否小于bound,是则哈希表hash[result]的值为1
  3. i=0到哈希表最后一个元素,值为1的将下标放到ret数组

代码:

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* powerfulIntegers(int x, int y, int bound, int* returnSize){
  long res1[21];
  long res2[21];
  int i, j;
  for(i=0;i < 21;i++)      //将x的不同次方放入res1数组
  {
    res1[i] = pow(x, i);
  }
  for(i=0; i< 21;i++)      //将y的不同次方放入res1数组
  {
    res2[i] = pow(y, i);
  }
int hash[2000000];
int result = 0;
memset(hash, 0, hash);
  for(i=0; i<21; i++)      //将符合res1[i]+res2[j] <=boud的放入哈希表,令hash[result] = 1;
    for(j=0; j<21; j++)
    {
      result = res1[i] + res2[j];
      if(result > bound)
      {
          break;
      }
      if(result <= bound)
        hash[result] = 1; 
    }
  i, j=0;
  *returnSize = 0;
  while(i<= bound && hash[i] != 0)  //统计符合条件值的个数
  {
    (*returnSize)++;
  }
int *ans = (int *)malloc(sizeof(int) * ( (*returnSize)+1) );  //动态创建数组
int temp = 0;
for(i=0; i<bound+1; i++)         //hash[i]的值不为0的 i放入ans数组
  if(i<= bound && hash[i] != 0)
    {
      ans[temp] = i;
      temp++;
    }
}
return ans;                   

}

题目二:914. 卡牌分组

问题描述:

给定一副牌,每张牌上都写着一个整数。

此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:

每组都有 X 张牌。
组内所有的牌上都写着相同的整数。
仅当你可选的 X >= 2 时返回 true。

问题思路:

一开始看题目示例,以为每个数字出现的次数是一样的,于是想着先把数组中每个元素出现的次数用哈希表记录下来,如果有一个元素出现次数不同,则返回false,提交之后直接解答错误。仔细看了一下,[1,1,1,1,1,1,2,2]可以分成[1,1],[1,1],[1,1],[2,2]四组,只要每组牌的个数大于等于K即可.

因此找他们之间的规律,发现每个数字出现的个数都与K有最大公因数(>1),这个条件转换成每个数字出现的个数之间都有最大公因数(>1),如果是则true,否则为false。

代码:

int gongyueshu(int a, int b)
{
  int c;
  while(b)
  {
    c = a % b;
    a = b;
    b = c;
  }
  return a;
}


bool hasGroupsSizeX(int* deck, int deckSize){
  int hash[100011];
  int temp;
  memset(hash,0,sizeof(int) * 100011);
  int i = 0;
  for(i=0; i<deckSize; i++)   //将每个数字出现的次数计入哈希表
  {
      ++hash[deck[i]];
  }
  int gcd = -1;
  for(i=0; i<100011; i++)     //求每个次数之间的最大公约数
  {
    if(hash[i] == 1)
      return false;
    if(hash[i] != 0 )
      {
        if(gcd == -1)
        {
          gcd = hash[i];
        }
        else
        {
          gcd = gongyueshu(gcd,hash[i]);  
          if(gcd == 1)          //如果最大公约数为1则返回false
            return false; 
        }
      }
  }
  return true;
  }

 

题目三:1497. 检查数组对是否可以被 k 整除

给你一个整数数组 arr 和一个整数 k ,其中数组长度是偶数,值为 n 。

现在需要把数组恰好分成 n / 2 对,以使每对数字的和都能够被 k 整除。

如果存在这样的分法,请返回 True ;否则,返回 False 。

问题思路:

 这道题一开始简单的想当然记录每个数出现的次数,循环遍历hash[i],看是否出现hash[j]使两者之和相加为0,但是提交之后解答错误。原因是hash[i]中的i是可以随机与其他数匹配的,使之相加为k,这样就出现了非常多的匹配结果,用n重循环显然是不现实的(时间复杂度过高)。

所以正确的思路是对每个数出现的次数取模,并且hash[mode]的值即为出现的次数,判断hash[i]与hash[k-i]的次数是否一致,不一致则返回false,全部模扫描完毕之后,如果没有出现return false,则返回true。

代码:

bool canArrange(int* arr, int arrSize, int k){
  int hash[1000010];
  int mod;
  memset(hash,0,sizeof(int) * 1000010);
  for(int i=0; i<arrSize; i++)
  { 
    mod = (arr[i] % k +k) % k;     //PS:看了题解才知道这里的取模是防止array[i]取负数,并且取模之 
                                     后数的范围就变成1-k了
                                    
    ++hash[mod];
  }
  if(hash[0] % 2)              // 如果余数为0的出现次数为奇数,则必有一个落单
    return false;

  for(int i=1; i<k; i++)      //判断hash[i] 与hash[k-i]的个数是否相等
    if(hash[i] != hash[k-i])
      return false;

  return true;
}

 

题目四: 面试题 17.05. 字母与数字

问题描述:

给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。

返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。

 解题思路:

这道题感觉自己理解的不透彻,似懂非懂就是不懂,先把题目收藏,把大概思路列出来,之后再过一遍。


遍历数组,如果是字母则记为-1,是数字记为1,例【‘1’,‘A’,‘B’,‘2’】则转换为[“1”,“-1“”, ‘-1’,‘1”];用sum[i]计算它们的前缀和,这里注意,前缀和相同的两下标之间的字母和数字是相同的,即中间的前缀和为0.

建立哈希表,记录hash[sum[i]],此时值为第一次出现这个元素的sum数组下标。遍历i,

if ((i - hash[sum[i]]) > max) {       如果出现同前缀,记录开始和结束标志。
                max = i - hash[sum[i]];
                start = hash[sum[i]];+ 1;
                end = i;
            } //

如果sum[i] == arraySize(sum[i]如果没加arrSize,此处的条件则为==0),说明0-i这个范围内数字和字母的个数相等,此时start为0,end为i,长度为i+1

 代码:

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
char** findLongestSubarray(char** array, int arraySize, int* returnSize){
  int i, j = 0;
  int sum[100010];

  if(array[0][0] <= 'Z' && array[0][0] >='A' || array[0][0] <= 'z' && array[0][0] >='a')
    sum[0] = -1;
  else
    sum[0] = 1;

  int q = 1;
  for(i=1; i<arraySize; i++)
  {
    if(array[i][0] <= 'Z' && array[i][0] >='A' || array[i][0] <= 'z' && array[i][0] >='a')
    {
      sum[i] = sum[i] - 1;
      q--;
    }
    else
    {
      sum[i] = sum[i] + 1;
      q++;    
    }
  }

  for(i=0; i<q; i++)
    sum[i] = sum[i] + arraySize;   //这里是防止hash[sum[i]]的sum[i]<0
  int start, max, end = -1;   

int hash[100010];
memset(hash,-1,sizeof(int)*100010);
  for(i=0; i<q; i++)
  {
    if(sum[i] == arraySize)
    {
      if(i+1 > max)
      {
        max = i + 1;
        start = 0;
        end = i;
      }
    }
    continue;
    if (hash[sum[i]] == -1) {
            hash[sum[i]] = i;
        } else {
            if ( i - hash[sum[i]]  > max) {
                max = i - hash[sum[i]];
                start = hash[sum[i]] + 1;
                end = i;
            }
        }
  }
  if(start == -1 || end == -1)
  {
    *returnSize = 0;
    return NULL;
  }
  *returnSize = end - start +1;
  char** ret = (char**)malloc((*returnSize) * sizeof(char*));
    for (int i = 0; i < *returnSize; i++) {
        ret[i] = (char*)malloc(20);
        strcpy(ret[i], array[start + i]);
    }
    return ret;
  
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值