[代码随想录一刷] day4 链表

● 哈希表理论基础 ● 242.有效的字母异位词 ● 349. 两个数组的交集 ● 202. 快乐数 ● 1. 两数之和

当题目有判断元素是否在集合中出现过,就要想到用哈希。

有效的字母异位词

C++

由于是字母长度有限可以用数组哈希,先记录字符串1每个单词对应的出现次数,再遍历字符串2减去,如果最后数组所有元素都为0,则代表匹配可以互相替换。

class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for(int i = 0; i < s.size(); i++) {
            record[s[i] - 'a']++;;
        }
        for(int i = 0; i< t.size(); i++) {
            record[t[i] - 'a']--;
        }
        for(int i = 0; i < 26; i++) {
            if(record[i] != 0) return false;
        }
        return true;
    }
};

C#

public class Solution {
    public bool IsAnagram(string s, string t) {
        int[] record = new int[26];
        for(int i = 0; i < s.Length; i++ ) {
            record[s[i] - 'a']++;
        }
        for(int i = 0 ; i < t.Length; i++) {
            record[t[i] - 'a']--;
        }
        for(int i = 0 ;i < 26; i++) {
            if(record[i] != 0) return false;
        }
        return true;

    }
}

两个数组的交集

C++

先把一个数组使用unordered_set存储,遍历另一个数组的元素判断是否在set中出现过,如果出现过,则加入到结果set中(去重),最后转为vector。

  • 使用数组来做哈希的题目,需要明确使用空间的大小,太大数组存不了。
  • 而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
  • unordered_set不可重复,只存key值,查询,增删效率为O(1),底层是哈希表实现,可以理解成一个可以无限扩容的数组,可以用来去重。
  • unordered_set查找,找不到返回末尾迭代器,即set.end();
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        unordered_set<int> num_set(nums1.begin(), nums1.end());
        for(int num : nums2) {
            if(num_set.find(num) != num_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

C#

C#的HashSet等同于C++的unordered_set,容器对应关系参考

public class Solution {
    public int[] Intersection(int[] nums1, int[] nums2) {
        HashSet<int> n1 = new HashSet<int>();
        HashSet<int> res = new HashSet<int>();
        foreach(int item in nums1) {
            n1.Add(item);
        }
        foreach(int item in nums2) {
            if(n1.Contains(item)) {
                res.Add(item);
            }
        }
        return res.ToArray();
    }
}

快乐数

C++

  • 在平方和更新为1之前判断,如果这个数出现过,则陷入了无线循环,不符合快乐数的定义。
  • 理解无限循环,如果不是快乐数(循环后平方和为1),则会重复出现
class Solution {
public:
    int getSum(int n){
        int sum = 0;
        while(n != 0) {
            sum += (n % 10) * (n % 10);
            n = n / 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set<int> result;
        int sum = n;
        while(1) {
            sum = getSum(sum);
            if(sum == 1)  return true;

            if(result.find(sum) != result.end()) {
                return false;
            } else {
                result.insert(sum);
            }  
        }
    }
};

C#

public class Solution {
    public bool IsHappy(int n) {
        HashSet<int> res = new HashSet<int>();
        int sum = n;
        while(1 > 0) {
            sum = getSum(sum);
            if(sum == 1) return true;
            if(res.Contains(sum)) return false;
            res.Add(sum);
        }
    }
    public int getSum(int n) {
        int sum = 0;
        while(n > 0) {
            sum += (n % 10) * (n % 10);
            n = n / 10;
        }
        return sum;
    }
}

两数之和

A+B=target->判断target-B是否在已遍历的集合中->哈希存遍历过的元素集合。

  • 为什么使用哈希,A+B =target转化为找是否已遍历target-B。
  • 为什么使用std::unordered_map,首先查找需要记录值,而返回结果需要记录下标,<key,value>结构即使用map,unorderedmap无序,不可重复,查询效率更高为O(1).
  • map用来存遍历过的元素。
  • map的key存遍历的数组值,value存数组下标,应为数组值是查询的依据。

C++

C+的语法有点麻烦,插入还要构建pair,访问还要find,不能直接索引(目前看到的),补坑:插入可以直接访问key索引赋值,访问也可以直接访问key。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++) {
            auto iter = map.find(target- nums[i]);
            if(iter != map.end()) {
                return {iter->second, i};
            }
            map.insert(pair<int, int>(nums[i], i));
        }
        return {};
    }
};

简便写法。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> res;
        for(int i = 0; i < nums.size(); i++) {
            if(res.find(target - nums[i]) != res.end()) {
                return {res[target - nums[i]], i};
            }
            res[nums[i]] = i;
        }
        return {};
    }
};

C#

C#的dic注意下插入的时候要判断下元素不重复,和C+ unordered_map不一样(插入重复不会报错,会自动去重),插入是不允许重复的。

public class Solution {
    public int[] TwoSum(int[] nums, int target) {
        Dictionary<int, int> dic = new Dictionary<int, int>();
        for(int i = 0; i< nums.Length; i++) {
            if(dic.ContainsKey(target - nums[i])) {
                return new int[]{dic[target - nums[i]], i};
            }
            if(!dic.ContainsKey(nums[i]))
            dic.Add(nums[i], i);
        }
        return new int[]{};
    }
}

二刷

242 有效的字母异位词

用unordered_map代替数组,熟悉了API。

#include<iostream>
#include<unordered_map>
using namespace std;
class Solution {
public:
    unordered_map<char, int> smap;
    unordered_map<char, int> tmap;
    bool isAnagram(string s, string t) {
        for (char it : s) {
            smap[it]++;
        }
        for (char it : t) {
            if (smap.find(it) != smap.end()) {
                smap[it]--;
                if (smap[it] == 0) {
                    smap.erase(smap.find(it));
                }
            }
            else {
                return false;
            }
        }
        if (smap.size() == 0) return true;
        return false;
    }
};

int main() {
    Solution s1;
    string s;
    string t;
    cin >> s >> t;
    cout << s1.isAnagram(s, t);

    return 0;
}

349 两个数组的交集

利用迭代及将set和vector进行转换可以更简单一点,下边是第一反应,转换见一刷。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> data;
        unordered_set<int> data2;
        vector<int> res;
        for (const int it : nums1) {
            data.insert(it);
        }
        for (const int it : nums2) {
            if (data.find(it) != data.end() && data2.find(it) == data.end()) {
                res.push_back(it);
            }
            data2.insert(it);
        }
        return res;
    }
};

202 快乐数

#include<iostream>
#include<unordered_set>
using namespace std;
class Solution {
public:
	bool isHappy(int n) {
		//n转换为每个位置上数的vector
		vector<int> data;
		unordered_set<int> pset;
		while (n != 1) {
			while (n > 0) {
				int m = n % 10;
				n = n / 10;
				data.push_back(m);
			}
			reverse(data.begin(), data.end());
			for (int i : data) cout << i << " ";
			cout << endl;

			//遍历vector计算平方和,判断是否重复,然后重复过程
			int sum = 0;
			for (int i : data) {
				sum += i * i;
			}
			if (pset.find(sum) != pset.end()) {
				return false;
			}
			pset.insert(sum);
			n = sum;
			data.clear();
		}
		return true;

	}
};

1 两数之和

  • 注意更新记录要后置,避免自己和自己相加符合条件,实际查询的时候用的都是当前点之前插入的数据,同时前面的数据也不可能有重复的,因为题目限定不能有重复解,相同元素只可能相邻且就是target/2。

  • map存的是<值,索引>的键值对,用于找到目标key后返回下标作为结果。

alt

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> data;
        for (int i = 0; i < nums.size(); i++) {
            auto it = data.find(target - nums[i]);
            if (it != data.end()) {
                return vector<int>{i, it->second};
            }
            data[nums[i]] = i;
        }
        return vector<int>();
    }
};