数据结构之:散列表

散列表又名哈希表
概念
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>
26
2 26-2 = 24
7 26-7 = 19
11 26-11 = 15
三、一次哈希法
[2, 7, 11, 15] 4
26 map
2 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 = 394
a + 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 = c
s = "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};
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值