一 、哈希表简介
作用: 一般哈希表都是用来快速判断一个元素是否出现集合里。
例如: 要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)
哈希函数: 把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下表快速知道这位同学是否在这所学校里了。

哈希碰撞: 一般哈希碰撞有两种解决方法, 拉链法和线性探测法。

拉链法:

**线性探测法:**使用线性探测法,一定要保证tableSize大于dataSize。我们需要依靠哈希表中的空位来解决碰撞问题
常见的三种哈希结构: 数组、set (集合)、map(映射)
二、String常用方法
| 函数 | 作用 |
|---|---|
| char charAt(int index) | 返回 char指定索引处的值。 |
| int compareTo(String anotherString) | 按字典顺序比较两个字符串。 负、零、正 |
| int hashCode() | 返回此字符串的哈希码。 |
| char[] toCharArray() | 将此字符串转换为新的字符数组。 |
| String toUpperCase() +toLowerCase() | 将所有在此字符 String使用默认语言环境的规则大写(小)。 |
| String[] split(String regex) | 将此字符串分割为给定的 regular expression的匹配。 |
| String replace(char oldChar, char newChar) | 返回从替换所有出现的导致一个字符串 oldChar在此字符串 newChar 。 |
| int indexOf(int ch) | 返回指定字符第一次出现的字符串内的索引。 |

三:题-.有效的字母异位词
https://leetcode-cn.com/problems/valid-anagram/
题目: 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
思路: 数组其实就是一个简单哈希表,而且这道题目中字符串只有小写字符,那么就可以定义一个数组(大小26),来记录字符串s里字符出现的次数。
class Solution {
public boolean isAnagram(String s, String t) {
int[] record = new int[26];
for (char c : s.toCharArray()) {
record[c - 'a'] += 1;
}
for (char c : t.toCharArray()) {
record[c - 'a'] -= 1;
}
for (int i : record) {
if (i != 0) {
return false;
}
}
return true;
}
}
四、查找常用字符-题
给你一个字符串数组 words ,请你找出所有在 words 的每个字符串中都出现的共用字符(包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。
【示例二】
输入:[“cool”,“lock”,“cook”]
输出:[“c”,“o”]
思路: 使用数组来做哈希的题目,是因为题目都限制了数值的大小。该题目就明确限制了大小,最大26
代码:
class Solution {
public List<String> commonChars(String[] words) {
ArrayList<String> result = new ArrayList();
if (words.length == 0)
return result;
int [] hash = new int [26];
for(int i = 0;i<words[0].length();i++){
hash[words[0].charAt(i) - 'a'] ++;
}
for(int i =1;i<words.length;i++){
int [] hash_other = new int[26];
for(int j =0;j<words[i].length();j++){
hash_other[words[i].charAt(j)-'a'] ++;
}
for(int k =0;k<26;k++)
{
if(hash[k] > hash_other[k]){
hash[k] = hash_other[k];
}
}
}
for(int i =0;i<26;i++)
{
while (hash[i] != 0) { // 注意这里是while,多个重复的字符
char c= (char) (i+'a');
result.add(String.valueOf(c));
hash[i]--;
}
}
return result;
}
}
五、两个数组的交集
思路:
遇到哈希问题我直接都用set不就得了,用什么数组啊?
直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
if(nums2.length == 0||nums1.length == 0){
return new int[0];
}
Set<Integer> hashset = new HashSet<>();
Set<Integer> resultset = new HashSet<>();
for(int i : nums1){
hashset.add(i);
}
for(int i = 0 ;i < nums2.length;i++){
if(hashset.contains(nums2[i]))
{
resultset.add(nums2[i]);
}
}
//输出结果
int[] resul = new int[resultset.size()];
Iterator<Integer> it = resultset.iterator();
int i = 0;
while(it.hasNext()){
resul[i] = it.next();
i++;
}
return resul;
}
}
六、两数之和-题
题目:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
思路:
1、数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
2、set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下表位置,因为要返回x 和 y的下表。所以set 也不能用。
3、此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下表
注意:
两数之和 就不能使用双指针法,因为1.两数之和要求返回的是索引下表, 而双指针法一定要排序,一旦排序之后原数组的索引就被改变了。
如果1.两数之和要求返回的是数值的话,就可以使用双指针法了
代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> hashmap=new HashMap<Integer,Integer>();
for(int i = 0;i<nums.length;i++){
if(hashmap.containsKey(target -nums[i])){
return new int[]{hashmap.get(target-nums[i]),i};
}
hashmap.put(nums[i],i);
}
return new int[]{-1,-1};
}
}
拓展-三数相加、四数相加
题目:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
思想:排序加双指针
代码:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同,因为题目要求的是数,而不是下标
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
//如果现在的和大了则让c左移(现在大了,后面的循环只会越来越大,无论是a还是b循环),小了就让b右移
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;//跳出b循环,进入a循环
}
if (nums[second] + nums[third] == target) {
List<Integer> list = new ArrayList<Integer>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
ans.add(list);
}
}
}
return ans;
}
}
七、四数相加
题目: 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
思路: 本题是使用哈希法的经典题目,而15.三数之和,18.四数之和并不合适使用哈希法,因为三数之和和四数之和这两道题目使用哈希法在不超时的情况下做到对结果去重是很困难的,很有多细节需要处理。
而这道题目是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于题目18. 四数之和,题目15.三数之和,还是简单了不少!
代码:
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//用于存储nums1和nums2和的个数
Map<Integer,Integer> hashmap = new HashMap<Integer,Integer>();
int result =0;
//用于存储a+b+C+d=0的个数
int sum;
for(int i:nums1){
for(int j:nums2){
sum = i+j;
if(hashmap.containsKey(sum)){
hashmap.put(sum,hashmap.get(sum)+1);
}else{
hashmap.put(sum,1);
}
}
}
for(int i:nums3){
for(int j:nums4){
sum =i+j;
if(hashmap.containsKey(-sum))
result += hashmap.get(-sum);
}
}
return result;
}
}
八、一个数组内四数相加-不重复
题目:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
示例:给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。满足要求的四元组集合为:[ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
思路:排序+双指针
代码:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
//双指针法必须先排序
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
//去除重复
if (i > 0 && nums[i - 1] == nums[i]) {
continue;
}
for (int j = i + 1; j < nums.length; j++) {
//去除重复
if (j > i + 1 && nums[j - 1] == nums[j]) {
continue;
}
//双指针法
int left = j + 1;
int right = nums.length - 1;
while (right > left) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum > target) {
right--;
} else if (sum < target) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
//双指针只需要在获得答案后去重复
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
//双指针同时先中间靠
left++;
right--;
}
}
}
}
return result;
}
}
1305

被折叠的 条评论
为什么被折叠?



