LeetBook-哈希表

96 阅读15分钟

哈希表

哈希表 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台

设计哈希集合

解法1

class MyHashSet {
    private final int MAX_LEN = 100000; // the amount of buckets
    private List<Integer>[] set;      // hash set implemented by array
    
    /** Returns the corresponding bucket index. */
    private int getIndex(int key) {
        return key % MAX_LEN;
    }
    
    /** Search the key in a specific bucket. Returns -1 if the key does not existed. */
    private int getPos(int key, int index) {
        // Each bucket contains a list.
        List<Integer> temp = set[index];
        if (temp == null) {
            return -1;
        }
        // Iterate all the elements in the bucket to find the target key.
        for (int i = 0; i < temp.size(); ++i) {
            if (temp.get(i) == key) {
                return i;
            }
        }
        return -1;
    }
    
    /** Initialize your data structure here. */
    public MyHashSet() {
        set = (List<Integer>[])new ArrayList[MAX_LEN];
    }
    
    public void add(int key) {
        int index = getIndex(key);
        int pos = getPos(key, index);
        if (pos < 0) {
            // Add new key if key does not exist.
            if (set[index] == null) {
                set[index] = new ArrayList<Integer>();
            }
            set[index].add(key);
        }
    }
    
    public void remove(int key) {
        int index = getIndex(key);
        int pos = getPos(key, index);
        if (pos >= 0) {
            // Remove the key if key exists.
            set[index].remove(pos);
        }
    }
    
    /** Returns true if this set did not already contain the specified element */
    public boolean contains(int key) {
        int index = getIndex(key);
        int pos = getPos(key, index);
        return pos >= 0;
    }
}

/**
 * Your MyHashSet object will be instantiated and called as such:
 * MyHashSet obj = new MyHashSet();
 * obj.add(key);
 * obj.remove(key);
 * boolean param_3 = obj.contains(key);
 */

设计哈希映射

解法1

class MyHashMap {
    private final int MAX_LEN = 100000;             // the amount of buckets
    private List<Pair<Integer, Integer>>[] map;     // hash map implemented by array
    
    /** Returns the corresponding bucket index. */
    private int getIndex(int key) {
        return key % MAX_LEN;
    }
    
    /** Search the key in a specific bucket. Returns -1 if the key does not existed. */
    private int getPos(int key, int index) {
        // Each bucket contains a list.
        List<Pair<Integer, Integer>> temp = map[index];
        if (temp == null) {
            return -1;
        }
        // Iterate all the elements in the bucket to find the target key.
        for (int i = 0; i < temp.size(); ++i) {
            if (temp.get(i).getKey() == key) {
                return i;
            }
        }
        return -1;
    }

    /** Initialize your data structure here. */
    public MyHashMap() {
        map = (List<Pair<Integer, Integer>>[])new ArrayList[MAX_LEN];
    }
    
    /** value will always be positive. */
    public void put(int key, int value) {
        int index = getIndex(key);
        int pos = getPos(key, index);
        if (pos < 0) {
            // Add new (key, value) pair if key is not existed.
            if (map[index] == null) {
                map[index] = new ArrayList<Pair<Integer, Integer>>();
            }
            map[index].add(new Pair(key, value));
        } else {
            // Update the value if key is existed.
            map[index].set(pos, new Pair(key, value));
        }
    }
    
    /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
    public int get(int key) {
        int index = getIndex(key);
        int pos = getPos(key, index);
        if (pos < 0) {
            return -1;
        } else {
            return map[index].get(pos).getValue();
        }
    }
    
    /** Removes the mapping of the specified value key if this map contains a mapping for the key */
    public void remove(int key) {
        int index = getIndex(key);
        int pos = getPos(key, index);
        if (pos >= 0) {
            map[index].remove(pos);
        }
    }
}

/**
 * Your MyHashMap object will be instantiated and called as such:
 * MyHashMap obj = new MyHashMap();
 * obj.put(key,value);
 * int param_2 = obj.get(key);
 * obj.remove(key);
 */

存在重复元素

解法1

  1. 使用HashSet存放遍历过的元素
  2. 如果碰到元素已经放入HashSet中,返回true,否则返回false
class Solution {
    public boolean containsDuplicate(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (set.contains(nums[i])) {
                return true;
            }
            set.add(nums[i]);
        }
        return false;
    }
}

解法2

解法1的基础上优化

  • 不使用contains方法,而用add方法返回值是否成功来进行判断
class Solution {
    public boolean containsDuplicate(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (!set.add(nums[i])) {
                return true;
            }
        }
        return false;
    }
}

只出现一次的数字

解法1

  1. new一个HashMap用来存放出现过的数字和出现次数的映射
  2. 遍历一遍后map中存放了所有数字及出现次数
  3. 取出出现次数为1的key返回
class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (map.get(nums[i]) == null) {
                map.put(nums[i], 1);
                continue;
            }
            map.put(nums[i], map.get(nums[i]) + 1);
        }
        for (int i = 0; i < nums.length; i++) {
            if (map.get(nums[i]) == 1) {
                return nums[i];
            }
        }
        return 0;
    }
}

解法2

  1. 由于每个元素都出现两次,使用HashSet
  2. 第一次遇到该元素add,第二次remove
  3. 最后剩余的元素就是只出现一次的数字
class Solution {
    public int singleNumber(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (!set.add(nums[i])) {
                set.remove(nums[i]);
            }
        }
        return set.iterator().next();
    }
}

解法3

  • 位运算
  1. 所有数字都出现两次,则除了只出现一次的数,其他的相加%2应该等于0
  2. 对数字进行位运算,所有数字每一位都加在一起
  3. 对每一位%2,得出的结果即为只出现一次的数字
class Solution {
    public int singleNumber(int[] nums) {
        int[] bit = new int[32];
        for (int num: nums) {
            for (int i = 0; i < 32; i ++) {
                bit[i] += (num >> (31-i)) & 1;
            }
        }
        int result = 0;
        for (int i = 0; i < 32; i++) {
            result = (result << 1) + bit[i] % 2;
        }
        return result;
    }
}

解法4

按位异或

  1. 由于其他元素都出现了两次,按位异或后抵消,则可以直接按位异或所有元素
  2. 最后的异或结果即为只出现一次的数字
class Solution {
    public int singleNumber(int[] nums) {
        int result = 0;
        for (int num : nums) {
            result ^= num;
        }
        return result;
    }
}

两个数组的交集

解法1

  1. new两个hashset,第一个hashset存放第一个数组
  2. 第二个数组存放进第二个hashset,放入之前判断是否在第一个hashset中存在(hashset不可重复)
  3. 将第二个hashset放入数组返回
class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> set2 = new HashSet<>();
        for (Integer number : nums1) {
            set1.add(number);
        }
        for (Integer number : nums2) {
            if (set1.contains(number)) {
                set2.add(number);
            }
        }
        int[] result = new int[set2.size()];
        int count = 0;
        for (Integer number : set2) {
            result[count] = number;
            count++;
        }
        return result;
    }
}

快乐数

解法1

暴力循环

class Solution {
    int count = 0;
    public boolean isHappy(int n) {
        int result = 0;
        for (int i = 0; i < 30; i++) {
            while (n != 0) {
                int num = n % 10;
                result += num * num;
                n = n / 10;
            }
            if (result == 1) {
                return true;
            }
            n = result;
            result = 0;
            count++;
        }
        return false;
    }
}

解法2(思路)

解法1的优化,把每次循环出的结果放到一个hashset中,如果有重复就可以提前return,出现重复就形成循环了,不会快乐了

例子的问题,居然没有暴力循环快,这是没想到的

class Solution {
    public boolean isHappy(int n) {
        HashSet<Integer> set = new HashSet<Integer>();
        while(n != 1){
            set.add(n);// 把每次得到 新数字插存入set中
            int tmp = n, sum = 0;// sum为新数字
            // 计算新数字
            while(tmp != 0){
                sum += (tmp % 10) * (tmp % 10);
                tmp /= 10;
            }
            if (set.contains(sum)) return false; // 新数字与原来的数字相等 会陷入死循环,即 不为快乐数字
            n = sum;
        }
        return true;
    }
}

两数之和

解法1

暴力循环

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] result = new int[2];
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if(nums[i] + nums[j] == target) {
                    result[0] = i;
                    result[1] = j;
                    return result;
                }
            }
        }
        return result;
    }
}

解法2

  1. 使用HashMap遍历一遍数组(map的key为元素值,value为元素下标)
  2. 每一次遍历都求出当前元素和target的差,如果该值在map中存在,则直接返回这两个值
  3. 否则将当前元素加入到map中
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] result = new int[2];
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int number = target - nums[i];
            if (map.containsKey(number)) {
                result[0] = i;
                result[1] = map.get(number);
                return result;
            }
            map.put(nums[i], i);
        }
        return result;
    }
}

解法3

在解法2的基础上优化一丢丢

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(target - nums[i])) {
                return new int[]{i, map.get(target - nums[i])};
            }
            map.put(nums[i], i);
        }
        return new int[0];
    }
}

同构字符串

解法1

  1. 用两个map存放两个字符串的互相映射
  2. 遍历字符串
  3. 如果遍历到某个元素存在映射但是和当前另一个元素不匹配则返回false
  4. 遍历结束则返回true
class Solution {
    public boolean isIsomorphic(String s, String t) {
        Map<Character, Character> map = new HashMap<>();
        Map<Character, Character> map2 = new HashMap<>();
        char[] ss = s.toCharArray();
        char[] tt = t.toCharArray();
        for (int i = 0; i < ss.length; i++) {
            if (map.containsKey(ss[i])) {
                if (map.get(ss[i]) != tt[i]) {
                    return false;
                }
            }
            if (map2.containsKey(tt[i])) {
                if (map2.get(tt[i]) != ss[i]) {
                    return false;
                }
            }
            map.put(ss[i], tt[i]);
            map2.put(tt[i], ss[i]);
        }
        return true;
    }
}

解法2

  • 用两个数组来做map进行优化
class Solution {
    public boolean isIsomorphic(String s, String t) {
        char[] char_s = s.toCharArray();
        char[] char_t = t.toCharArray();
        HashMap<Character, Character> map = new HashMap<>();
        for (int i = 0; i < char_s.length; i++) {
            if (map.containsKey(char_s[i])) {
                if (map.get(char_s[i]) != char_t[i]) {
                    return false;
                }
            } else if (map.containsValue(char_t[i])) {
                return false;
            } else {
                map.put(char_s[i], char_t[i]);
            }
        } 
        return true;
    }
}

两个列表的最小索引总和

解法1

  1. 将list1的元素和下标放入一个map中
  2. 遍历list2,找出最小的元素放到list1的开头,覆盖掉list1的元素,并定义一个index记录下标
  3. 如果碰到更小的元素,就将index重置
  4. 定义一个新的String数组,长度为index,将list1的前index个元素放进来返回
class Solution {
    public String[] findRestaurant(String[] list1, String[] list2) {
        Map<String, Integer> map = new HashMap();
        for (int i = 0; i < list1.length; i++) {
            map.put(list1[i], i);
        }
        int index = 0;
        int min = 99999999;
        for (int i = 0; i < list2.length; i++) {
            if (map.get(list2[i]) != null) {
                int road = map.get(list2[i]) + i;
                if (road < min) {
                    min = road;
                    index = 0;
                }
                if (road <= min) {
                    list1[index++] = list2[i];
                }
            }
        }
        String[] result = new String[index];
        for (int i = 0; i < index; i++) {
            result[i] = list1[i];
        }
        return result;
    }
}

解法2

解法1的优化,思路一致

class Solution {
    public String[] findRestaurant(String[] list1, String[] list2) {
        Map<String, Integer> map = new HashMap();
        for (int i = 0; i < list1.length; i++) {
            map.put(list1[i], i);
        }
        int index = 0;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < list2.length; i++) {
            Integer number = map.get(list2[i]);
            if (number != null) {
                int road = number + i;
                if (road < min) {
                    min = road;
                    index = 0;
                }
                if (road <= min) {
                    list1[index++] = list2[i];
                }
            }
        }
        String[] result = new String[index];
        for (int i = 0; i < index; i++) {
            result[i] = list1[i];
        }
        return result;
    }
}

字符串中的第一个唯一字符

解法1

  1. 将字符串转为char数组,新建一个hashmap用于存放char和对应的下标
  2. 遍历数组,将每一个char和对应的下标放入到hashmap
  3. 如果当前char在hashmap中存在,则将下标置为-1
  4. 遍历hashmap,取出下标不为1的最小值返回
class Solution {
    public int firstUniqChar(String s) {
        char[] ss = s.toCharArray();
        Map<Character, Integer> map = new HashMap<>();
        for (int i =0; i < ss.length; i++) {
            if (map.containsKey(ss[i])) {
                map.put(ss[i], -1);
                continue;
            }
            map.put(ss[i], i);
        }
        int index = 99999;
        for (Character key : map.keySet()) {
            if (map.get(key) != -1) {
                if (map.get(key) < index) {
                    index = map.get(key);
                }
            }
        }
        if (index == 99999) {
            return -1;
        }
        return index;
    }
}

解法2

  1. 用一个长度为26的数组list来表示26个字母
  2. 将字符串转换为char数组
  3. 遍历char数组,将每个字母对应的ASCII码-97对应的list下标自增1
  4. 遍历char数组,当遇到第一个对应下标值为1的char时,返回当前元素下标
  5. 否则返回-1
class Solution {
    public int firstUniqChar(String s) {
        int[] list = new int[26];
        char[] ss = s.toCharArray();
        for (char c : ss) {
            list[c - 97]++;
        }
        for (int i = 0; i < ss.length; i++) {
            if (list[ss[i] - 97] == 1) {
                return i;
            }
        }
        return -1;
    }
}

两个数组的交集Ⅱ

解法1

暴力循环

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        int index = 0;
        for (int i = 0; i < nums1.length; i++) {
            for (int j = 0; j < nums2.length; j++){
                if (nums1[i] == nums2[j]) {
                    nums1[index] = nums1[i];
                    nums2[j] = -1;
                    index++;
                    break;
                }
            }
        }
        int[] result = new int[index];
        for (int i = 0; i < index; i++) {
            result[i] = nums1[i];
        }
        return result;
    }
}

解法2

  1. 新建一个数组
  2. 遍历list1,对于list1中的每个元素,都在新数组中对应下标自增1
  3. 新建一个result数组用于存放结果
  4. 遍历list2,对于list2中每个元素,对应新数组中的下标不为0的元素存放入result数组,并把新数组中元素自减1
  5. 用Arrays的copyOfRange方法返回结果效率较高
class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        int[] account = new int[1001];
        for (int i = 0; i < nums1.length; i++) {
            account[nums1[i]]++;
        }
        int[] result = new int[Math.min(nums1.length, nums2.length)];
        int index = 0;
        for (int i = 0; i < nums2.length; i++) {
            int val = nums2[i];
            if(account[val] > 0) {
                account[val]--;
                result[index] = val;
                index++;
            }
        }
        return Arrays.copyOfRange(result, 0, index);
    }
}

存在重复元素Ⅱ

解法1

暴力循环,写着玩的(虽然也能跑出来,but不可取)

class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < Math.min((i + k + 1), nums.length); j++) {
                if (nums[i] == nums[j]) {
                    return true;
                }
            }
        }
        return false;
    }
}

解法2

用HashMap

  1. new一个HashMap用来存放元素和下标
  2. 遍历数组,每一个元素都去HashMap中get一下value
  3. 如果没get到,就将元素和下标作为(key,value)put进map中
  4. 如果get到了,判断是否小于等于k
    • 如果小于等于k则返回
    • 否则put进map中覆盖原来的k,v
  5. 遍历结束返回false
class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (map.get(nums[i]) == null) {
                map.put(nums[i], i);
                continue;
            }
            if (i - map.get(nums[i]) <= k) {
                return true;
            }
            map.put(nums[i], i);
        }
        return false;
    }
}

字母异位词分组

解法1

  1. 将每个字符串排序后放入一个新的字符串数组,与原数组对应
  2. 遍历原数组
  3. 如果对应的排序后的数组对应值存在在map中,则将原数组的元素添加到map的value(List)里
  4. 如果不存在,则将原数组元素作为key,新数组元素作为value(放入list),put进map
  5. 遍历map取出所有value放入result(List)返回
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        String[] newstr = new String[strs.length];
        List<List<String>> result = new ArrayList<>();
        Map<String, List<String>> map = new HashMap<>();
        for (int i = 0; i < strs.length; i++) {
            char[] ss = strs[i].toCharArray();
            Arrays.sort(ss);
            newstr[i] = String.valueOf(ss);
        }
        for (int i = 0; i < strs.length; i++) {
            if (map.containsKey(newstr[i])) {
                map.get(newstr[i]).add(strs[i]);
                continue;
            }
            List<String> list = new ArrayList<>();
            list.add(strs[i]);
            map.put(newstr[i], list);
        }
        // Iterator iterator = map.keySet().iterator();
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            result.add(entry.getValue());
        }
        return result;
    }
}

解法2

  1. 遍历字符串数组
  2. 将元素排序,并在map中get元素的value
  3. 如果value为null,则将字符串和对应的ArrayList(空的)放入map中,并将ArrayList放入result中
  4. 向ArrayList中添加元素
  5. 返回result
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> result = new ArrayList<>();
        Map<String, ArrayList<String>> map = new HashMap<>();
        char[] ccc;
        String sss;
        ArrayList<String> temp = null;
        for (String str : strs) {
            ccc = str.toCharArray();
            Arrays.sort(ccc);
            sss = String.valueOf(ccc);
            temp = map.get(sss);
            if (temp == null) {
                temp = new ArrayList<>();
                map.put(sss, temp);
                result.add(temp);
            }
            temp.add(str);
        }
        return result;
    }
}

有效的数独

解法1

  1. 对行、列、3*3方格,分别new一个HashMap数组
  2. 每个数组有9个HashMap对应9行、9列、9个box
  3. 遍历数组,将每一个元素与每一个map中的key作比较,如果出现重复,则返回false
  4. 否则返回true
class Solution {
    public boolean isValidSudoku(char[][] board) {
        Map<Integer, Integer>[] line = new HashMap[9];
        Map<Integer, Integer>[] row = new HashMap[9];
        Map<Integer, Integer>[] box = new HashMap[9];
        for (int i = 0; i < 9; i++) {
            line[i] = new HashMap<>();
            row[i] = new HashMap<>();
            box[i] = new HashMap<>();
        }
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                char gt = board[i][j];
                if (gt != '.') {
                    int n = (int)gt;
                    int box_index = (i / 3) * 3 + j / 3;
                    row[i].put(n, row[i].getOrDefault(n, 0) + 1);
                    line[j].put(n, line[j].getOrDefault(n, 0) + 1);
                    box[box_index].put(n, box[box_index].getOrDefault(n, 0) + 1);
                    if (row[i].get(n) > 1 || line[j].get(n) > 1 || box[box_index].get(n) > 1) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
}

解法2 位运算

位运算

class Solution {
    public boolean isValidSudoku(char[][] board) {
        int[] row = new int[10];
        int[] line = new int[10];
        int[] box = new int[10];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                char c = board[i][j];
                if (c == '.') {
                    continue;
                }
                int n = c - '0';
                int box_index = (i / 3) * 3 + j / 3;
                if ((((row[i] >> n) & 1) == 1) || (((line[j] >> n) & 1) == 1) || (((box[box_index] >> n) & 1) == 1)) {
                    return false;
                }
                row[i] |= (1<<n);
                line[j] |= (1<<n);
                box[box_index] |= (1<<n);
            }
        }
        return true;
    }
}

寻找重复的子树

解法1

官解:寻找重复的子树 - 寻找重复的子树 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    Map<String, Pair<TreeNode, Integer>> seen = new HashMap<String, Pair<TreeNode, Integer>>();
    Set<TreeNode> repeat = new HashSet<TreeNode>();
    int idx = 0;

    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        dfs(root);
        return new ArrayList<TreeNode>(repeat);
    }

    public int dfs(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int[] tri = {node.val, dfs(node.left), dfs(node.right)};
        String hash = Arrays.toString(tri);
        if (seen.containsKey(hash)) {
            Pair<TreeNode, Integer> pair = seen.get(hash);
            repeat.add(pair.getKey());
            return pair.getValue();
        } else {
            seen.put(hash, new Pair<TreeNode, Integer>(node, ++idx));
            return idx;
        }
    }
}

宝石与石头

解法1

暴力循环

class Solution {
    public int numJewelsInStones(String jewels, String stones) {
        char[] bulingbuling = jewels.toCharArray();
        char[] shitou = stones.toCharArray();
        int count = 0;
        for (int i = 0; i < bulingbuling.length; i++) {
            for (int j = 0; j < shitou.length; j++) {
                if (bulingbuling[i] == shitou[j]) {
                    count++;
                }
            }
        }
        return count;
    }
}

解法2

  1. HashMap存放宝石
  2. 遍历石头,key存在计数器自增1
  3. 返回计数器
class Solution {
    public int numJewelsInStones(String jewels, String stones) {
        char[] bulingbuling = jewels.toCharArray();
        char[] shitou = stones.toCharArray();
        Map<Character, Integer> map = new HashMap<>();
        int count = 0;
        for (int i = 0; i < bulingbuling.length ;i++) {
            map.put(bulingbuling[i], 1);
        }
        for (int i = 0; i < shitou.length; i++) {
            if (map.containsKey(shitou[i])) {
                count++;
            }
        }
        return count;
    }
}

无重复字符的最长子串

解法1

  1. 使用HashMap和快慢指针
  2. 遍历字符串,将每一个字符都放入HashMap中,并计算当前最长子串,如果大于记录,则替换
  3. 返回记录的最大值
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int slow = 0;
        int fast;
        int max = 0;
        HashMap<Character, Integer> map = new HashMap<>();
        for (fast = 0; fast < s.length() ;fast++) {
            if (map.containsKey(s.charAt(fast))) {
                slow = Math.max(slow, map.get(s.charAt(fast)));
            }
            max = Math.max(max, fast - slow + 1);
            map.put(s.charAt(fast), fast + 1);
        }
        return max;
    }
}

解法2

用char+128把char转换为正整数,每一个元素都对应ints的一位

太妙了!太妙了!!!

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int[] ints = new int[256];
        int max = 0;
        int nowLen = 0;
        int itemValue;
        int start = 0;
        char[] chars = s.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            itemValue = chars[i]+128;
            if (ints[itemValue]!=0) {
                if (nowLen > max){
                    max = nowLen;
                }
                for (int i1 = start; i1 <= ints[itemValue] - 1; i1++) {
                    ints[chars[i1]+128] = 0;
                    nowLen--;
                    start++;
                }
            }
            ints[itemValue] = i+1;
            nowLen++;
        }
        return nowLen > max?nowLen:max;
    }
}

四数相加Ⅱ

解法1

  1. 四个数组分为两组
  2. 将AB的和及出现的次数放入到HashMap中
  3. 计算CD的和*(-1)如果在HashMap中出现则计数结果自增对应的value值
  4. 返回计数结果
class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int count = 0;
        Map<Integer, Integer> map = new HashMap<>();
        int sumAB;
        int sumCD;
        for (int i = 0; i < nums1.length; i++) {
            for (int j = 0; j < nums2.length; j++) {
                sumAB = nums1[i] + nums2[j];
                if (map.containsKey(sumAB)) {
                    map.put(sumAB, map.get(sumAB) + 1);
                    continue;
                }
                map.put(sumAB, 1);
            }
        }
        for (int i = 0; i < nums3.length; i++) {
            for (int j = 0; j < nums4.length; j++) {
                sumCD = (nums3[i] + nums4[j]) * (-1);
                if (map.containsKey(sumCD)) {
                    count += map.get(sumCD);
                }
            }
        }
        return count;
    }
}

前K个高频元素

解法1

  1. 用一个HashMap存放每个元素及出现次数
  2. 用一个优先级队列来存放前k个高频元素(小顶堆)
  3. 遍历map,当队列满之后每次都判断从map中取出的元素出现次数和队首的元素出现此数
  4. 保留其中较高的那一个
  5. 循环结束,依次出队,即为前k个高频元素
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            public int compare(int[] m, int[] n) {
                return m[1] - n[1];
            }
        });
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            int key = entry.getKey();
            int value = entry.getValue();
            if (queue.size() == k) {
                if (queue.peek()[1] < value) {
                    queue.poll();
                    queue.offer(new int[]{key, value});
                }
            } else {
                queue.offer(new int[]{key, value});
            }
        }
        int[] result = new int[k];
        for (int i = 0; i < k; i++) {
            result[i] = queue.poll()[0];
        }
        return result;
    }
}
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            public int compare(int[] m, int[] n) {
                return m[1] - n[1];
            }
        });
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            int[] kv = new int[2];
            kv[0] = entry.getKey();
            kv[1] = entry.getValue();
            if (queue.size() == k) {
                if (queue.peek()[1] < kv[1]) {
                    queue.poll();
                    queue.offer(kv);
                }
            } else {
                queue.offer(kv);
            }
        }
        int[] result = new int[k];
        for (int i = 0; i < k; i++) {
            result[i] = queue.poll()[0];
        }
        return result;
    }
}

常数时间插入、删除和获取随机元素

解法1

官解

class RandomizedSet {
    List<Integer> nums;
    Map<Integer, Integer> indices;
    Random random;

    public RandomizedSet() {
        nums = new ArrayList<Integer>();
        indices = new HashMap<Integer, Integer>();
        random = new Random();
    }

    public boolean insert(int val) {
        if (indices.containsKey(val)) {
            return false;
        }
        int index = nums.size();
        nums.add(val);
        indices.put(val, index);
        return true;
    }

    public boolean remove(int val) {
        if (!indices.containsKey(val)) {
            return false;
        }
        int index = indices.get(val);
        int last = nums.get(nums.size() - 1);
        nums.set(index, last);
        indices.put(last, index);
        nums.remove(nums.size() - 1);
        indices.remove(val);
        return true;
    }

    public int getRandom() {
        int randomIndex = random.nextInt(nums.size());
        return nums.get(randomIndex);
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */