Post

Map

Java

Methods

NavigableMap

PrefixSuffixParameterComparison
ceilingentry/keykey>=
floorentry/keykey<=
higherentry/keykey>
lowerentry/keykey<
headmaptoKey<
headmaptoKey, inclusive< or <=
tailmapfromKey>=
tailmapfromKey, inclusive> or >=
(poll)firstentry least
(poll)lastentry greatest
descendingmap  
submapfromKey, fromInclusive, toKey, toInclusive 
descendingkeySet  
navigablekeySet  

SortedMap

PrefixSuffixParameterComparison
headmaptoKey<
tailmapfromKey>=
firstkey lowest
lastkey highest
submapfromKey, toKey[)

Note that the ordering maintained by a sorted map (whether or not an explicit comparator is provided) must be consistent with equals if the sorted map is to correctly implement the Map interface.

The ordering imposed by a comparator c on a set of elements S is said to be consistent with equals if and only if c.compare(e1, e2)==0 has the same boolean value as e1.equals(e2) for every e1 and e2 in S.

Sort Map by Values

Ascending

1
2
3
4
5
6
7
Map<String, Integer> unSortedMap = getUnSortedMap();
LinkedHashMap<String, Integer> sortedMap = new LinkedHashMap<>();
 
unSortedMap.entrySet()
    .stream()
    .sorted(Map.Entry.comparingByValue())
    .forEachOrdered(e -> sortedMap.put(e.getKey(), e.getValue()));

Descending

1
2
3
4
5
6
7
Map<String, Integer> unSortedMap = getUnSortedMap();
LinkedHashMap<String, Integer> sortedMap = new LinkedHashMap<>();
 
unSortedMap.entrySet()
    .stream()
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
    .forEachOrdered(e -> sortedMap.put(e.getKey(), e.getValue()));

Sort Map by Values then Keys

1
2
    .sorted(Comparator.comparing(Map.Entry<String, Integer>::getValue)
            .thenComparing(Comparator.comparing(Map.Entry<String, Integer>::getKey)))
1
2
    .sorted(Map.Entry.<String, Integer>comparingByValue()
            .thenComparing(Map.Entry.comparingByKey()))

Key with the max value in a map

1
Collections.max(map.entrySet(), Map.Entry.comparingByValue()).getKey();

Map.Entry

C++

Methods

  • iterator upper_bound(const key_type& k) -> “higher”
  • iterator lower_bound(const key_type& k) -> “ceiling”

  • map::operator[]
    • “find-or-add”
    • Default constructible and assignable
  • map::at: same as [] except throws an exception when the key doesn’t exist
  • map::insert: doesn’t modify the map if the key exists
  • map::emplace: parameters are forwareded to the constructor of the object stored in the container. No copies.

The following example is from this post:

1
2
3
4
5
6
7
8
9
10
K t; V u;
std::map<K,V> m;           // std::map<K,V>::value_type is std::pair<const K,V>
    
m.insert( std::pair<const K,V>(t,u) );
m.insert( std::map<K,V>::value_type(t,u) );
m.insert( std::make_pair<const K,V>(t,u) );

m.insert( std::make_pair(t,u) );

m.emplace(t,u);

@walletfox. (2018). Overview of std::map’s Insertion / Emplacement Methods in C++17

Making File Names Unique

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public String[] getFolderNames(String[] names) {
    String[] folders = new String[names.length];
    // name: smallest valid integer
    Map<String, Integer> map = new HashMap<>();
    for (int i = 0; i < names.length; i++) {
        String curr = names[i];
        int count = map.getOrDefault(curr, 1);
        while (map.containsKey(curr)) {
            curr = names[i] + "(" + count++ + ")";
        }
        map.put(curr, 1);
        map.put(names[i], count);
        folders[i] = curr;
    }
    return folders;
}

Vowel Spellchecker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public String[] spellchecker(String[] wordlist, String[] queries) {
    Set<String> words = new HashSet<>();
    Map<String, String> cap = new HashMap<>(), vowel = new HashMap<>();

    for (String w : wordlist) {
        words.add(w);
        String lower = w.toLowerCase(), devowel = lower.replaceAll("[aeiou]", "#");
        cap.putIfAbsent(lower, w);
        vowel.putIfAbsent(devowel, w);
    }

    int n = queries.length;
    String[] result = new String[n];
    for (int i = 0; i < n; i++) {
        String q = queries[i];
        if (words.contains(q)) {
            result[i] = q;
            continue;
        }

        String lower = q.toLowerCase(), devowel = lower.replaceAll("[aeiou]", "#");
        if (cap.containsKey(lower)) {
            result[i] = cap.get(lower);
            continue;
        }

        if (vowel.containsKey(devowel)) {
            result[i] = vowel.get(devowel);
            continue;
        }
        result[i] = "";
    }
    return result;
}

Tuple with Same Product

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int tupleSameProduct(int[] nums) {
    Map<Integer, Integer> map = new HashMap<>();
    int count = 0;
    for (int i = 0; i < nums.length; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            int p = nums[i] * nums[j];
            int c = map.getOrDefault(p, 0);
            // acculumates the count so we don't need a second pass
            count += c * 8;
            map.put(p, c + 1);
        }
    }
    return count;
}

Buckets

Contains Duplicate III

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
    Map<Long, Long> buckets = new HashMap<>();
    // capacity of each bucket
    long capacity = (long)t + 1;

    for (int i = 0; i < nums.length; i++) {
        long bucket = Math.floorDiv(nums[i], capacity);

        // checks if the bucket already contains a number
        if (buckets.containsKey(bucket)) {
            return true;
        }

        // checks the neighbor buckets
        if (buckets.containsKey(bucket - 1) && Math.abs(nums[i] - buckets.get(bucket - 1)) < capacity) {
            return true;
        }

        if (buckets.containsKey(bucket + 1) && Math.abs(nums[i] - buckets.get(bucket + 1)) < capacity) {
            return true;
        }

        buckets.put(bucket, (long)nums[i]);

        // removes out-of-window buckets
        if (i >= k) {
            buckets.remove(Math.floorDiv(nums[i - k], capacity));
        }
    }

    return false;
}

TreeMap

Depth of BST Given Insertion Order

Array of Doubled Pairs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public boolean canReorderDoubled(int[] arr) {
    Map<Integer, Integer> count = new TreeMap<>();
    for (int a : arr) {
        count.put(a, count.getOrDefault(a, 0) + 1);
    }

    for (int a : count.keySet()) {
        if (count.get(a) == 0) {
            continue;
        }

        int pair = a < 0 ? a / 2 : a * 2;
        // if a is an odd negative, we can't find a pair that pair * 2 == a
        // if count(a) > count(pair), there's not enough pair to cancel a out
        // since we are scanning in ascending order
        if ((a < 0 && a % 2 != 0) || count.get(a) > count.getOrDefault(pair, 0)) {
            return false;
        }

        count.put(pair, count.get(pair) - count.get(a));
    }
    return true;
}

Find Array Given Subset Sums

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public int[] recoverArray(int n, int[] sums) {
    Arrays.sort(sums);

    return recover(sums).stream().mapToInt(i -> i).toArray();
}

private List<Integer> recover(int[] sums) {
    if (sums.length == 1) {
        return new ArrayList<>();
    }

    Map<Integer, Long> count = Arrays.stream(sums).boxed()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

    // considers the subset sums of arr [x, y, z]
    // sums can be split into two groups:
    //     A             B
    // [0, y, z] + [x, x + y, x + z]

    // sorts sums
    // d = sums[1] - sums[0]
    // then x == -d or x == d

    // proof:
    // s0, s1, ..., s_k, where k = 2 ^ n
    // since one of the subsets is empty, there always exists s_i == 0

    // - if there's at least one negative subset sum s_0
    //   - if there's only one negative subset sum
    //     - d = arr[m], where arr[m] is the least non-negative
    //       x = d
    //       e.g. arr = [-3, 1]
    //     - d = -arr[0], x = -d
    //       e.g. arr = [-3, 4]
    //   - if there're more than one negative subset sum
    //     - d = -arr[m], where arr[m] < 0 and |arr[m]| is the least
    //       x = -d
    // - else
    //   - if there're more than one subset sum == 0
    //     d = 0, x = d = 0
    //   - else
    //     d = arr[m], where arr[m] is the least
    //     x = d
    int d = sums[1] - sums[0];

    // splits the subset sums into two groups A and B
    int[] groupA = new int[sums.length / 2], groupB = new int[sums.length / 2];
    // by default, Group A + x == Group B
    boolean toSwap = false;
    // iterates the sums ascendingly, finds the matching b == a + d
    int i = 0;
    for (int a : sums) {
        int b = a + d;
        if (count.getOrDefault(a, 0l) > 0) {
            count.put(b, count.get(b) - 1);
            count.put(a, count.get(a) - 1);

            // when b == 0, we can swap Group B and Group A
            // and take -d as the new x
            if (b == 0) {
                toSwap = true;
            }

            groupA[i] = a;
            groupB[i] = b;
            i++;
        }
    }

    // arr is sorted
    List<Integer> arr = recover(toSwap ? groupB : groupA);
    // arr.add(x)
    arr.add(toSwap ? -d : d);

    return arr;
}

Dynamic Programming

Odd Even Jump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public int oddEvenJumps(int[] arr) {
    int n  = arr.length;
    // whether has higher or lower later elements
    boolean[] hasHigher = new boolean[n], hasLower = new boolean[n];
    hasHigher[n - 1] = hasLower[n - 1] = true;

    // {element, index}
    TreeMap<Integer, Integer> map = new TreeMap<>();
    map.put(arr[n - 1], n - 1);

    // in reverse order
    int count = 1;
    for (int i = n - 2; i >= 0; i--) {
        var c = map.ceilingEntry(arr[i]);
        var f = map.floorEntry(arr[i]);
        if (c != null) {
            hasHigher[i] = hasLower[c.getValue()];
        }
        if (f != null) {
            hasLower[i] = hasHigher[f.getValue()];
        }

        // odd numbered jumps are higher
        if (hasHigher[i]) {
            count++;
        }
        map.put(arr[i], i);
    }
    return count;
}

Another solution is using Stack (next greater/less element).

Non Generic Map

Word Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public boolean wordPattern(String pattern, String s) {
    String[] words = s.split(" ");

    if (words.length != pattern.length()) {
        return false;
    }

    // non generic map
    // <Object, Object>
    Map map = new HashMap<>();
    // the type of i must be Integer
    // since there's no auto boxing for non generic map integer values
    for (Integer i = 0; i < words.length; i++) {
        char c = pattern.charAt(i);
        String w = words[i];

        map.putIfAbsent(c, i);
        map.putIfAbsent(w, i);

        if (map.get(c) != map.get(w)) {
            return false;
        }
    }
    return true;
}
This post is licensed under CC BY 4.0 by the author.