散列表又名哈希表
概念
Hash 散列 哈希 杂凑把任意长度的输入 通过算法 变换成固定长度的输出相较于 顺序存储结构而言 当存储量达到一定程度时 查找效率得到提高“ 空间换时间 ”映射关系,根据关键字 key 访问到具体值 value不同 key 映射到同一个地址 哈希碰撞 或 哈希冲突
哈希函数
1 )直接寻址法取关键字或关键字的线性函数 作为散列地址2 )除留取余法对关键字或关键字的部分取模 作为散列地址 取模的除数 一般为素数 / 质数取模的除数 一般为素数 / 质数3 )取随机数法使用随机函数,取关键字的随机值 作为散列地址4 )数字分析法根据数字的特性,经过分析,取部分进行计算(如手机号后四位 身份证后四位等等)5 )平方取中法先求平方,取中间几位 作为散列地址6 )折叠法取关键字的几部分 取叠加和 作为散列地址发生哈希冲突的原因 —— 抽屉原理
解决冲突的办法: 再找一个空闲位置
具体如下1 )线性探测key 01 —— 1 号柜子如果满了 顺延到下一个位置 —— 2 号柜子2 )二次探测如果满了 按照一定规律顺延如以二次方顺延 value value+1^2 value+2^2 3 )双重哈希使用两种哈希函数 第一个位置被占用时 计算第二个4 )链表法让一个位置存储多个 value ( 用链表串联起来 )
哈希应用之两数之和
https://leetcode-cn.com/problems/two-sum/
1. 两数之和给定一个整数数组 nums 和一个目标值 target ,请你在该数组中找出和为目标值的那 两个整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。示例 :给定 nums = [2, 7, 11, 15], target = 4因为 nums[0] + nums[1] = 2 + 7 = 9所以返回 [0, 1]
一、暴力破解法
2 [7, 11, 15]7 [11, 15]11 [15]遍历每个元素 查找后续元素与其相加的和 是否等于 target依次遍历出 每两个元素之和
二、倒推法
使用额外容器存储 快速找到是否存在某个值< 元素值,索引位置 > hashmap[2, 7, 11, 15]<2,0> <7,1> <11,2> <15,3>262 26-2 = 247 26-7 = 1911 26-11 = 15
三、一次哈希法
[2, 7, 11, 15] 426 map2 26-2 = 24 <2,0>7 26-7 = 19 <2,0> <7,1>11 26-11 = 15 <2,0> <7,1> <11,2>15 26-15 = 11边遍历边修改 map 的值 能达到最好效率
public static int[] twoSum(int[] nums, int target) {
//遍历每个元素 查找后续元素与其相加的和 是否等于target
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[]{-1, -1};
}
public static int[] twoSum1(int[] nums, int target) {
// 使用map 存储 <元素值,索引位置>
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int needNum = target - nums[i];
// 数组中同一个元素不能使用两遍
if (map.containsKey(needNum) && map.get(needNum) != i) {
return new int[]{i, map.get(needNum)};
}
}
return new int[]{-1, -1};
}
public static int[] twoSum2(int[] nums, int target) {
// 边遍历边修改map的值 能达到最好效率
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int needNum = target - nums[i];
if (map.containsKey(needNum)) {
return new int[]{map.get(needNum), i};
}
map.put(nums[i], i);
}
return new int[]{-1, -1};
}
哈希应用之找不同
找不同
https://leetcode-cn.com/problems/fifind-the-difffference/
389. 找不同给定两个字符串 s 和 t ,它们只包含小写字母。字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。请找出在 t 中被添加的字母。示例 1 :输入: s = "abcd", t = "abcde"输出: "e"解释: 'e' 是那个被添加的字母。示例 2 :输入: s = "", t = "y"输出: "y"示例 3 :输入: s = "a", t = "aa"输出: "a"示例 4 :输入: s = "ae", t = "aea"输出: "a"
方案一:
使用 map 分别记录 s 和 t 中 < 字母,出现的次数 >查找 t 中出现次数多了一次 或者在 s 中从未出现的字母s = "abcd", t = "abcde"<a,1> <b,1> <c,1> <d,1> map<a,0> <b,0> <c,0> <d,0> 找到 e 时 返回s = "abcd", t = "abcda"<a,1> <b,1> <c,1> <d,1> map<a,0> <b,0> <c,0> <d,0> 第二次遍历到 a 此时次数为 0 说明出现额外一次字母 a
public static char findTheDifference(String s, String t) {
Map<Character, Integer> map = new HashMap<>();
// 存储s中的字母 及其出现次数
for (Character c : s.toCharArray()) {
if (map.containsKey(c)) {
int newNum = map.get(c) + 1;
map.put(c, newNum);
continue;
}
map.put(c, 1);
}
for (char tc : t.toCharArray()) {
// 在s中从未出现的字母
if (!map.containsKey(tc)) {
return tc;
}
// 查找t中出现次数多了一次
if (map.get(tc) == 0) {
return tc;
}
int newNum = map.get(tc) - 1;
map.put(tc, newNum);
}
return '-';
}
方案二:
字符串的替换方法 replace()遍历 s 中每个字母 将其在 t 中替换为空 t 最后只剩一个字母
public static char findTheDifference1(String s, String t) {
for (Character c : s.toCharArray()) {
// 替换第一个出现的位置
t = t.replaceFirst(c.toString(), "");
}
return t.toCharArray()[0];
}
方案三:
根据 ascii 码表的特性分别遍历 s 和 t 将每个字母的值相加 所得结果相减 差值就是要找的值a + b + c + d = 97 + 98 + 99 + 100 = 394a + b + c + d + e = 97 + 98 + 99 + 100 + 101 = 495
// 根据ascii码表的特性
public static char findTheDifference2(String s, String t) {
int sSum = 0, tSum = 0;
for (Character c : s.toCharArray()) {
sSum += c;
}
for (Character c : t.toCharArray()) {
tSum += c;
}
return (char) (tSum - sSum);
}
方案四:
异或运算 二进制运算0 ^ 1 = 1 ^ 0 = 1 两者不同0 ^ 0 = 1 ^ 1 = 0 两者相同a ^ b ^ c ^ a ^ b = (a ^ a) ^ (b ^ b) ^ c = cs = "abcd", t = "abcde"(a ^ b ^ c ^ d) ^ (a ^ b ^ c ^ d ^ e) = e
//异或运算
public static char findTheDifference3(String s, String t) {
int result = 0;
for (Character c : s.toCharArray()) {
result ^= c;
}
for (Character c : t.toCharArray()) {
result ^= c;
}
return (char) result;
}
public class FindDiff {
public static void main(String[] args) {
System.out.println(findTheDifference3("abcd", "abcde"));
System.out.println(findTheDifference3("abcd", "abcda"));
}
public static char findTheDifference(String s, String t) {
Map<Character, Integer> map = new HashMap<>();
// 存储s中的字母 及其出现次数
for (Character c : s.toCharArray()) {
if (map.containsKey(c)) {
int newNum = map.get(c) + 1;
map.put(c, newNum);
continue;
}
map.put(c, 1);
}
for (char tc : t.toCharArray()) {
// 在s中从未出现的字母
if (!map.containsKey(tc)) {
return tc;
}
// 查找t中出现次数多了一次
if (map.get(tc) == 0) {
return tc;
}
int newNum = map.get(tc) - 1;
map.put(tc, newNum);
}
return '-';
}
public static char findTheDifference1(String s, String t) {
for (Character c : s.toCharArray()) {
// 替换第一个出现的位置
t = t.replaceFirst(c.toString(), "");
}
return t.toCharArray()[0];
}
// 根据ascii码表的特性
public static char findTheDifference2(String s, String t) {
int sSum = 0, tSum = 0;
for (Character c : s.toCharArray()) {
sSum += c;
}
for (Character c : t.toCharArray()) {
tSum += c;
}
return (char) (tSum - sSum);
}
//异或运算
public static char findTheDifference3(String s, String t) {
int result = 0;
for (Character c : s.toCharArray()) {
result ^= c;
}
for (Character c : t.toCharArray()) {
result ^= c;
}
return (char) result;
}
}
public class TwoSum {
public static void main(String[] args) {
int[] nums = {2, 7, 11, 15};
System.out.println(Arrays.toString(twoSum2(nums, 26)));
}
public static int[] twoSum(int[] nums, int target) {
//遍历每个元素 查找后续元素与其相加的和 是否等于target
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[]{-1, -1};
}
public static int[] twoSum1(int[] nums, int target) {
// 使用map 存储 <元素值,索引位置>
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int needNum = target - nums[i];
// 数组中同一个元素不能使用两遍
if (map.containsKey(needNum) && map.get(needNum) != i) {
return new int[]{i, map.get(needNum)};
}
}
return new int[]{-1, -1};
}
public static int[] twoSum2(int[] nums, int target) {
// 边遍历边修改map的值 能达到最好效率
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int needNum = target - nums[i];
if (map.containsKey(needNum)) {
return new int[]{map.get(needNum), i};
}
map.put(nums[i], i);
}
return new int[]{-1, -1};
}
}