先附上地址方便对照着看:代码随想录
三、哈希表
1、哈希表结构:就是散列表,根据关键码的值直接访问的数据结构。
哈希函数:将关键值映射成索引的函数,比如 f(x) = x+1,那么关键值为1就访问下标为2的数组元素
哈希碰撞:存在多个关键值映射到同一位置上,就会发生冲突,如何解决:拉链法和线性探测法
2、有效的字母异位词:数组也是一种哈希表嘛,但是要注意数组的大小,要是太大的话就不适合用数组了
基本思路很简单:用数组存储字符串中每个字符出现的次数,s 字符串中的字符出现一次就加一,t 字符串的字符出现一次就减一,最后看看数组元素最后是不是全为0即可
注意题目条件:s,t 都只会包含小写字母,也就是说最多只可能出现26个字母,那就可以放心用数组了。
直接上步骤:
step 1:定义一个数组并初始化所有元素为0
step 2:遍历字符串 s 的字符,每次访问字符时,将数组中 s[i] - 'a' 的下标元素加一,不减也是可以的,那数组就必须大一些,而且要把字符形式的ASCII码转换为 int 类型。
step 3:遍历字符串 t 的字符,每次访问字符时,将数组中 s[i] - 'a‘ 的下标元素减一
step 4:最后遍历数组,如果出现不为0的元素,返回false,否则返回true
代码如下:
bool isAnagram(char* s, char* t) {
int alphabeta[26]={0}; //step 1
for(int i=0;i<strlen(s);i++) //step 2
alphabeta[s[i] - 'a']++;
for(int i=0;i<strlen(t);i++) //step 3
alphabeta[t[i] - 'a']--;
for(int i=0;i<26;i++){ // step 4
if(alphabeta[i] != 0)
return false;
}
return true;
}
3、两个数组的交集:还是用数组当哈希表的题目,因为有说限制范围
基本思路和上一题差不多:也是先定义一个数组用于记录,但这里要注意,题目要求输出元素唯一,也就是说要去重,那么就在之前那道题的思路上改改就好。
步骤如下:
step 1:定义一个记录数组并初始化元素为0,还有一个结果数组与一个计数变量
step 2:遍历第一个数组,记录其中的每个元素,方式与上一道题相同
step 3:访问第二个数组,但这里要注意,每次访问记录数组找到与上一道数组相同的元素后先将该元素记录到结果数组,然后记得将记录数组的该元素清零,否则会使得输出结果元素不唯一
代码如下:
int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
int tmp[1000]={0},cnt=0;
int *result = (int *)malloc(sizeof(int) * 1000); //step 1
for(int i=0;i<nums1Size;i++) // step 2
tmp[nums1[i]]++;
for(int i=0;i<nums2Size;i++){ //step 3
if(tmp[nums2[i]] != 0){
result[cnt++]=nums2[i];
tmp[nums2[i]] = 0;
}
}
*returnSize = cnt;
return result;
}
4、快乐数:其他语言都挺快乐的,C语言除外,这玩意为啥没有自带的哈希表啊,不过题目给了n的取值范围,还是能用数组:因为 n 不超过2^31-1,也就是说最大不超过10位数,于是最大的结果不超过 9^2*10=810,数组设大一点就行
这里需要注意一下,求解快乐数的过程可能无限循环但不一定会等于原来的 n ,也就是说我们用sum == n这种判断方式是不对的,所以还是得老老实实存储求解过程中产生的数,如果碰到重复的就表明不是快乐数。
直接上代码,按题意来就好
int getSum(int n) {
int sum = 0;
while (n) {
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
bool isHappy(int n){
int sum = getSum(n);
int hash[820] = {0};
while (sum != 1) {
if (hash[sum] == 1) {
return false;
} else {
hash[sum]++;
}
sum = getSum(sum);
}
return true;
}
5、两数之和:不是,你力扣支持ut_hash函数库,dev c++不支持啊,上了考场一样用不了哈希表……
真无语了啊C语言,机试遇到这种题我直接暴力,你就算让我用哈希表,C语言的哈希操作还得自己写,真写不来,等机试过了之后二刷代码随想录的时候用C++吧,服了
暴力解法:两重for循环直接暴力判断
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
int *ret = malloc(sizeof(int) * 2);
for(int i=0;i<numsSize;i++){
for(int j=i+1;j<numsSize;j++){
if(nums[i] + nums[j] == target){
ret[0]=i;
ret[1]=j;
*returnSize=2;
return ret;
}
}
}
*returnSize = 0;
return NULL;
}
6、四数之和:好好好,摆烂,暴力超时,考试哈希表函数不让用,没必要练了
7、赎金信:这道题限定了字符都是小写字母,于是还是拿数组当哈希表,虽然好像也没有其他考场能用的哈希表了……
这道题和字母异位词那题大同小异,区别就在于每个字符只让用一次,这个也好办,出现的字符在哈希表里面减一就好了。
步骤如下:
step 1:创建记录数组并初始化元素全为0
step 2:统计magazine内的字符,每访问一个字符就让它与 a 字母的差值作为数组下标的元素加一
step 3:验证ransomNote的字符,访问其中的每个字符,若哈希表内有,那就减一,没有就返回false,遍历完成返回true。这里只要注意if-else关系就好了
bool canConstruct(char* ransomNote, char* magazine) {
int alphabeta[26]={0}; //step 1
for(int i=0;i<strlen(magazine);i++) //step 2
alphabeta[magazine[i] - 'a']++;
for(int i=0;i<strlen(ransomNote);i++){ //step 3
if(alphabeta[ransomNote[i] - 'a'] != 0){
alphabeta[ransomNote[i] - 'a']--;
}
else return false;
}
return true;
}
8、9两题有点超出我机试难度了,双指针法等研究一下更新步骤解析。
10、总结一下,纯C语言不太适合写算法啊,哈希表得手搓,机试大概率不会考非得用哈希表的题目,那么短的时间五道代码题还要手搓数据结构能给我送走