哈希集合哈希映射

哈希集合和哈希映射是经常使用的两个类。

HashMap设计键的总结  https://leetcode-cn.com/leetbook/read/hash-table/xxavl2/

当字符串 / 数组中每个元素的顺序不重要时,可以使用排序后的字符串 / 数组作为键。

如果只关心每个值的偏移量,通常是第一个值的偏移量,则可以使用偏移量作为键。

在树中,你有时可能会希望直接使用 TreeNode 作为键。 但在大多数情况下,采用子树的序列化表述可能是一个更好的主意。

在矩阵中,你可能希望使用行索引或列索引作为键。

在数独中,可以将行索引和列索引组合来标识此元素属于哪个块。( rowIndex * block行数量 / block行数量 + colIndex / block列数量)

有时,在矩阵中,您可能希望将值聚合在同一对角线中。

 

1. 同构字符串

给定两个字符串 s 和 t,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

 

示例 1:

输入:s = "egg", t = "add"
输出:true
示例 2:

输入:s = "foo", t = "bar"
输出:false
示例 3:

输入:s = "paper", t = "title"
输出:true
 

提示:

可以假设 s 和 t 长度相同。

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/hash-table/xhjvbj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解题思路:把源字符串中的每个字符和目的字符串的每个字符串做映射,一一对应。通过HashMap来映射源到目的,通过HashSet来判断目的字符已经存在映射。 此题可以扩展为单词pattern。

class Solution {
    public boolean isIsomorphic(String s, String t) {
        if(s.length() != t.length()) {
            return false;
        }
        Map<Character, Character> dict = new HashMap<>();
        Set<Character> used = new HashSet<>();

        for(int i = 0; i < s.length(); i++){
            char sch = s.charAt(i);
            char tch = t.charAt(i);
            if(dict.containsKey(sch)) {
                char och = dict.get(sch);
                if(och != tch) {
                    return false;
                }
            } else {
                if(used.contains(tch)) {
                    return false;
                }
                dict.put(sch, tch);
                used.add(tch);
            }
        }
        return true;
    }
}
 
2. 有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

 

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

输入:
[
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: true
示例 2:

输入:
[
  ["8","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:

一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 '.' 。
给定数独永远是 9x9 形式的。

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/hash-table/xxpit5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法1:

解题思路: 每一行,每一列, 每个3*3 box都定义一个HashMap来存储元素,如果其中有一个hashMap中出现重复数字,则返回false, 否则返回true. Box的下标通过 (rowIndex / 3 * 3 + colIndex / 3) 获得。 

class Solution {
    public boolean isValidSudoku(char[][] board) {
        HashMap<Integer, Integer>[] rowMaps = new HashMap[9];
        HashMap<Integer, Integer>[] colMaps = new HashMap[9];
        HashMap<Integer, Integer>[] boxMaps = new HashMap[9];

        for(int i = 0 ; i < 9; i++) {
            rowMaps[i] = new HashMap<>();
            colMaps[i] = new HashMap<>();
            boxMaps[i] = new HashMap<>();
        }
        
        for(int i = 0; i < 9; i++) {
            for(int j = 0; j < 9; j++) {
               if(board[i][j] == '.') {
                   continue;
               }
               int num = board[i][j];
               
               HashMap<Integer, Integer> rowMap = rowMaps[i];
               HashMap<Integer, Integer> colMap = colMaps[j];
               HashMap<Integer, Integer> boxMap = boxMaps[(i/3) * 3 + j / 3];

               rowMap.put(num, rowMap.getOrDefault(num, 0) + 1);
               colMap.put(num, colMap.getOrDefault(num,0) + 1);
               boxMap.put(num, boxMap.getOrDefault(num, 0) + 1);
               
               if(rowMap.get(num) > 1 || colMap.get(num) > 1 || boxMap.get(num) > 1) {
                   return false;
               }
                
            }
        }    
        return true;
    }
}
 
 方法2: 定义9*9的行,列,box布尔数组,用来存储是否是有过该数字。如果出现两次则返回false.
class Solution {
    public boolean isValidSudoku(char[][] board) {
        boolean[][] rowFill = new boolean[9][9];
        boolean[][] colFill = new boolean[9][9];
        boolean[][] boxFill = new boolean[9][9];

        for(int i = 0; i < 9; i++) {
            for(int j = 0; j < 9; j++) {
                if(board[i][j] == '.') {
                    continue;
                }

                int num = board[i][j] - '1';
                if(rowFill[i][num] || colFill[j][num] || boxFill[i/3 * 3 + j/3][num]) {
                    return false;
                }

                rowFill[i][num] = true;
                colFill[j][num] = true;
                boxFill[i/3 * 3 + j/3][num] = true;

            }
        }
        return true;
    }
}
 
3. 我的日程安排

实现一个 MyCalendar 类来存放你的日程安排。如果要添加的时间内没有其他安排,则可以存储这个新的日程安排。

MyCalendar 有一个 book(int start, int end)方法。它意味着在 start 到 end 时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end), 实数 x 的范围为,  start <= x < end。

当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生重复预订。

每次调用 MyCalendar.book方法时,如果可以将日程安排成功添加到日历中而不会导致重复预订,返回 true。否则,返回 false 并且不要将该日程安排添加到日历中。

请按照以下步骤调用 MyCalendar 类: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end)

示例 1:

MyCalendar();
MyCalendar.book(10, 20); // returns true
MyCalendar.book(15, 25); // returns false
MyCalendar.book(20, 30); // returns true
解释:
第一个日程安排可以添加到日历中. 第二个日程安排不能添加到日历中,因为时间 15 已经被第一个日程安排预定了。
第三个日程安排可以添加到日历中,因为第一个日程安排并不包含时间 20 。
说明:

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/my-calendar-i
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:把每个日程放入TreeMap, 开始时间作为key, 结束时间作为值。TreeNap具有天然的排序功能。每次放入日程时找到开始时间的上下日程进行比价。

 
class MyCalendar {
    TreeMap<Integer, Integer> treeMap;

    public MyCalendar() {
        treeMap = new TreeMap<>();
    }

    public boolean book(int start, int end) {
        Integer preStart = treeMap.floorKey(start);
        Integer postStart = treeMap.ceilingKey(start);

        if((preStart == null || treeMap.get(preStart) <= start)
                && (postStart == null || end <= postStart )) {
            treeMap.put(start, end);
            return true;
        }

        return false;
    }
}
 
4. 我的日程安排2

实现一个 MyCalendar 类来存放你的日程安排。如果要添加的时间内不会导致三重预订时,则可以存储这个新的日程安排。

MyCalendar 有一个 book(int start, int end)方法。它意味着在 start 到 end 时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end), 实数 x 的范围为,  start <= x < end。

当三个日程安排有一些时间上的交叉时(例如三个日程安排都在同一时间内),就会产生三重预订。

每次调用 MyCalendar.book方法时,如果可以将日程安排成功添加到日历中而不会导致三重预订,返回 true。否则,返回 false 并且不要将该日程安排添加到日历中。

请按照以下步骤调用MyCalendar 类: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end)

 

示例:

MyCalendar();
MyCalendar.book(10, 20); // returns true
MyCalendar.book(50, 60); // returns true
MyCalendar.book(10, 40); // returns true
MyCalendar.book(5, 15); // returns false
MyCalendar.book(5, 10); // returns true
MyCalendar.book(25, 55); // returns true
解释:
前两个日程安排可以添加至日历中。 第三个日程安排会导致双重预订,但可以添加至日历中。
第四个日程安排活动(5,15)不能添加至日历中,因为它会导致三重预订。
第五个日程安排(5,10)可以添加至日历中,因为它未使用已经双重预订的时间10。
第六个日程安排(25,55)可以添加至日历中,因为时间 [25,40] 将和第三个日程安排双重预订;
时间 [40,50] 将单独预订,时间 [50,55)将和第二个日程安排双重预订。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/my-calendar-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 

解题思路:要检查是否是三重预定,先找出双重预定的时间点,再用日程安排去和双重预定时间比较。

方法1:

public class MyCalendarTwo {
private List<int[]> calendarList;
private List<int[]> overlapList;

public MyCalendarTwo() {
calendarList = new ArrayList<>();
overlapList = new ArrayList<>();
}

public boolean book(int start, int end) {
for(int[] cal : overlapList) {
if(start < cal[1] && end > cal[0]) {
return false;
}
}

for(int[] cal : calendarList) {
if(start < cal[1] && end > cal[0]) {
overlapList.add(new int[] {Math.max(start, cal[0]), Math.min(end, cal[1])});
}
}

int[] calendar = new int[2];
calendar[0] = start;
calendar[1] = end;
calendarList.add(calendar);

return true;
}
}

方法2:使用TreeMap存储日程的开始和结束时间的key,开始记为1,结束记为-1. TreeMap是排序的,所以日程都是按时间进行的。如果有3个在同时开的会议,则返回false,否则为true.
class MyCalendarTwo {

    TreeMap<Integer, Integer> calendars;

    public MyCalendarTwo() {
        calendars = new TreeMap<>();
    }

    public boolean book(int start, int end) {
        calendars.put(start, calendars.getOrDefault(start,0) + 1);
        calendars.put(end, calendars.getOrDefault(end, 0) - 1);

        int active = 0;
        for(int val : calendars.values()) {
            active += val;
            if(active >= 3) {
      //如果超过3个则把添加的日程删掉。
                calendars.put(start, calendars.get(start) - 1);
                calendars.put(end, calendars.get(end) + 1);
                if(calendars.get(start) == 0) {
                    calendars.remove(start);
                }
                return false;
            }
        }

        return true;
    }
}
 
4. 我的日程安排3

实现一个 MyCalendar 类来存放你的日程安排,你可以一直添加新的日程安排。

MyCalendar 有一个 book(int start, int end)方法。它意味着在start到end时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end), 实数 x 的范围为,  start <= x < end。

当 K 个日程安排有一些时间上的交叉时(例如K个日程安排都在同一时间内),就会产生 K 次预订。

每次调用 MyCalendar.book方法时,返回一个整数 K ,表示最大的 K 次预订。

请按照以下步骤调用MyCalendar 类: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end)

示例 1:

MyCalendarThree();
MyCalendarThree.book(10, 20); // returns 1
MyCalendarThree.book(50, 60); // returns 1
MyCalendarThree.book(10, 40); // returns 2
MyCalendarThree.book(5, 15); // returns 3
MyCalendarThree.book(5, 10); // returns 3
MyCalendarThree.book(25, 55); // returns 3
解释:
前两个日程安排可以预订并且不相交,所以最大的K次预订是1。
第三个日程安排[10,40]与第一个日程安排相交,最高的K次预订为2。
其余的日程安排的最高K次预订仅为3。
请注意,最后一次日程安排可能会导致局部最高K次预订为2,但答案仍然是3,原因是从开始到最后,时间[10,20],[10,40]和[5,15]仍然会导致3次预订。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/my-calendar-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路: 使用TreeMap,记录下同时段开会的最大值。

class MyCalendarThree {
    TreeMap<Integer, Integer> calendars;

    public MyCalendarThree() {
        calendars = new TreeMap<>();
    }
    
    public int book(int start, int end) {
        calendars.put(start, calendars.getOrDefault(start, 0) + 1);
        calendars.put(end, calendars.getOrDefault(end, 0) -1);
        int count = 0;
        int result = 0;
        for(int val : calendars.values()) {
            count += val;
            result = Math.max(count, result);
        }
        return result;
    }
}
 
5. 四数相加
 

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。

例如:

输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]

输出:
2

解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:把四数分为两两相加后再相加。两两相加的和作为key,并统计次数放入hashMap. 如果另外两两相加的和为-key,这找到组合。

class Solution {
    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
        HashMap<Integer, Integer> abMap = new HashMap<>();

        for(int v : A) {
            for(int k : B) {
                abMap.put(v + k, abMap.getOrDefault(v + k, 0) + 1);
            }
        }
        
        int answer = 0;

        for(int v : C) {
            for(int k : D) {
                if(abMap.containsKey(-v-k)) {
                    answer += abMap.get(-v-k);
                }
            }
        }
        return answer;
    }
}
 
6. 常数时间插入、删除和获取随机元素

 

设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。

insert(val):当元素 val 不存在时,向集合中插入该项。
remove(val):元素 val 存在时,从集合中移除该项。
getRandom:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。
示例 :

// 初始化一个空的集合。
RandomizedSet randomSet = new RandomizedSet();

// 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomSet.insert(1);

// 返回 false ,表示集合中不存在 2 。
randomSet.remove(2);

// 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomSet.insert(2);

// getRandom 应随机返回 1 或 2 。
randomSet.getRandom();

// 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomSet.remove(1);

// 2 已在集合中,所以返回 false 。
randomSet.insert(2);

// 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
randomSet.getRandom();

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/hash-table/xx0zpt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解题思路:此题的关键点是要求在O(1)下操作。添加删除,hashMap都能O(1),但是random没法达到目的,所以需要List来存储元素,hashmap做位置索引。

class RandomizedSet {
    HashMap<Integer, Integer> set;
    List<Integer> list;
    Random random;
    /** Initialize your data structure here. */
    public RandomizedSet() {
        set = new HashMap<>();
        list = new ArrayList<>();
        random = new Random();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if(set.containsKey(val)) {
            return false;
        } else {
            list.add(val);
            set.put(val, list.size() - 1);
            return true;
        }
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
        if(!set.containsKey(val)){
            return false;
        }

        int index = set.get(val);
        list.set(index,list.get(list.size() - 1));
        set.put(list.get(list.size() - 1),index);
        
        list.remove(list.size() - 1);      
        set.remove(val);

        return true;
    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        return list.get(random.nextInt(list.size()));
    }
}

/**
 * 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();
 */
 
7. 给定一个非空的整数数组,返回其中出现频率前 高的元素。

class Solution {
  public int[] topKFrequent(int[] nums, int k) {
    HashMap<Integer, Integer> numCount = new HashMap<>();

    for(int num : nums) {
      numCount.put(num, numCount.getOrDefault(num, 0) + 1);
    }

    PriorityQueue<int[]> preKNum = new PriorityQueue<>((num1, num2) -> {
      return num1[1] - num2[1];
    });

    for(Map.Entry<Integer, Integer> entry : numCount.entrySet()) {
      int num = entry.getKey();
      int count = entry.getValue();
      if(preKNum.size() < k) {
        preKNum.offer(new int[]{num, count});
        continue;
      }
      if(preKNum.peek()[1] < count) {
        preKNum.poll();
        preKNum.add(new int[]{num, count});
      }
    }
    int size = preKNum.size();
    int[] result = new int[size];
    for(int i = size -1 ; i >=0; i--){
    result[i] = preKNum.poll()[0];
    }
    return result;
  }
}

 

8. 寻找重复的子树

给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。

两棵树重复是指它们具有相同的结构以及相同的结点值。

示例 1:

          1
         /    \
       2      3
      /       /  \
    4      2    4
           /
         4
下面是两个重复的子树:

      2
     /
   4

  4
因此,你需要以列表的形式返回上述重复子树的根结点。

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/hash-table/xxm0i6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

思路:把每条路径和子路径都作为key存入HashMap。重复出现的路径进行技计数统计。空节点返回#.

 

class Solution {
 public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        Map<String, Integer> treeMap = new HashMap<>();
        List<TreeNode> result = new ArrayList<>();

        findDupSubTrees(root, treeMap, result);
        return result;
    }

    private String findDupSubTrees(TreeNode node, Map<String, Integer> treeMap, List<TreeNode> result)    {
        if(node == null) {
            return "#";
        }
        String serial = node.val + "," + findDupSubTrees(node.left, treeMap, result) + "," + findDupSubTrees(node.right, treeMap, result);

        treeMap.put(serial, treeMap.getOrDefault(serial, 0) + 1);
        if(treeMap.get(serial) == 2) {
            result.add(node);
        }
        return serial;
    }
}
 
9. 最长不含重复字符的子字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

 

示例 1:

输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
  请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

作者:Krahets
链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5dgr0c/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> charMap = new HashMap<>();
        int maxLength = 0; 
        int pre = -1;
        for(int i = 0; i < s.length(); i++) {
            if(charMap.containsKey(s.charAt(i))) {
                pre = Math.max(pre, charMap.get(s.charAt(i)));
            }
            charMap.put(s.charAt(i), i);
            maxLength = Math.max(maxLength, i - pre);
        }

        return maxLength;
    }
}
 
 

763. 划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

 

示例:

输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。

 

提示:

  • S的长度在[1, 500]之间。
  • S只包含小写字母 'a' 到 'z' 。
class Solution {
    public List<Integer> partitionLabels(String s) {
        List<Integer> result = new ArrayList<Integer>();
        Map<Character, Integer> charMap = new HashMap<>();
        char[] chars = s.toCharArray();

        for(int i = 0; i < chars.length; i++) {
            charMap.put(chars[i], i);
        }
        int start = 0;
        int end = -1;
        for(int i = 0; i < chars.length; i++) {   
            int lastIndex = charMap.get(chars[i]);
            if(lastIndex < end) {
                continue;
            }

            end = lastIndex;
            if(i == end) {
                result.add(end - start + 1);
                start = end + 1;
            }
        }

        return result;

    }
}

 

大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 2 的幂。

你可以搭配 任意 两道餐品做一顿大餐。

给你一个整数数组 deliciousness ,其中 deliciousness[i] 是第 i​​​​​​​​​​​​​​ 道餐品的美味程度,返回你可以用数组中的餐品做出的不同 大餐 的数量。结果需要对 109 + 7 取余。

注意,只要餐品下标不同,就可以认为是不同的餐品,即便它们的美味程度相同。

 

示例 1:

输入:deliciousness = [1,3,5,7,9]
输出:4
解释:大餐的美味程度组合为 (1,3) 、(1,7) 、(3,5) 和 (7,9) 。
它们各自的美味程度之和分别为 4 、8 、8 和 16 ,都是 2 的幂。

class Solution {
    public int countPairs(int[] deliciousness) {
        int mod = 1000000007;
        int len = deliciousness.length;
        int[] checks = new int[22];

        for(int i = 0; i < 22; i++) {
            checks[i] = (1 << i);
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        int pairs  = 0;
        for(int j = 0; j < len; j++) {
            int num = deliciousness[j];

            for(int k = 0; k < 22; k++) {
                int count = map.getOrDefault(checks[k] - num, 0);
                pairs = (pairs + count) % mod;
            }
            map.put(num, map.getOrDefault(num, 0) + 1);
        }

        return pairs;
    }
}

 

posted @ 2020-12-29 17:07  闪闪的星光  阅读(645)  评论(0)    收藏  举报