Sparse Vector Class
Problem
A sparse vector has many zeros, so it keeps only the nonzero entries as index:value pairs (indexes 0 to 99). Commands: vec NAME [i:v ...] (create; entries with value 0 are not kept; there may be no entries), dot A B (print the dot product), add C A B (store the sum A + B as C, dropping zeros), norm2 NAME (the sum of squares of the entries), show NAME (print the entries as index:value in increasing index order, separated by spaces; print empty if none).
Constraints
- 1 <= commands <= 100
- 0 <= index < 100
- |value| <= 1000
Examples
Input
vec a 0:1 3:2 vec b 3:4 5:1 dot a b
Output
8
Solutions
Only the nonzero entries are kept, so a vector with 3 nonzero numbers out of a million uses 3 map entries. The dot product loops over one map and looks up the other, so the cost depends on the nonzero count, not on the length.
/*
* Problem : Run the commands.
* Input : Command lines.
* Output : One line per answer.
* Approach: Sparse vector class
* Time : O(n x nnz)
* Space : O(nnz)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: Only the nonzero entries are stored, in a sorted map from index to value.
static class SparseVector {
private final java.util.TreeMap<Integer, Long> entries = new java.util.TreeMap<Integer, Long>();
void set(int index, long value) {
if (value == 0) {
entries.remove(index);
} else {
entries.put(index, value);
}
}
// Step 2: The dot product only needs indexes that exist in both vectors. Loop over the smaller map.
long dot(SparseVector other) {
long total = 0;
for (java.util.Map.Entry<Integer, Long> e : entries.entrySet()) {
Long match = other.entries.get(e.getKey());
if (match != null) {
total += e.getValue() * match;
}
}
return total;
}
// Step 3: The sum starts as a copy of this vector, then the other entries are added. Zero results are dropped by set.
SparseVector plus(SparseVector other) {
SparseVector result = new SparseVector();
for (java.util.Map.Entry<Integer, Long> e : entries.entrySet()) {
result.set(e.getKey(), e.getValue());
}
for (java.util.Map.Entry<Integer, Long> e : other.entries.entrySet()) {
long old = result.entries.containsKey(e.getKey()) ? result.entries.get(e.getKey()) : 0;
result.set(e.getKey(), old + e.getValue());
}
return result;
}
String text() {
StringBuilder sb = new StringBuilder();
for (java.util.Map.Entry<Integer, Long> e : entries.entrySet()) {
sb.append(sb.length() == 0 ? "" : " ").append(e.getKey()).append(":").append(e.getValue());
}
return sb.length() == 0 ? "empty" : sb.toString();
}
}
// This method solves the task. It receives the input and returns the answer.
static String[] solve(String[] lines) {
java.util.Map<String, SparseVector> named = new java.util.HashMap<String, SparseVector>();
java.util.List<String> out = new java.util.ArrayList<String>();
for (String line : lines) {
String[] part = line.split(" ");
if (part[0].equals("vec")) {
SparseVector v = new SparseVector();
for (int i = 2; i < part.length; i++) {
String[] pair = part[i].split(":");
v.set(Integer.parseInt(pair[0]), Long.parseLong(pair[1]));
}
named.put(part[1], v);
} else if (part[0].equals("dot")) {
out.add(String.valueOf(named.get(part[1]).dot(named.get(part[2]))));
} else if (part[0].equals("add")) {
named.put(part[1], named.get(part[2]).plus(named.get(part[3])));
} else if (part[0].equals("norm2")) {
out.add(String.valueOf(named.get(part[1]).dot(named.get(part[1]))));
} else {
out.add(named.get(part[1]).text());
}
}
return out.toArray(new String[0]);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
java.util.List<String> all = new java.util.ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
all.add(line);
}
}
String[] lines = all.toArray(new String[0]);
System.out.println(String.join("\n", solve(lines)));
}
}
Hints (2)
- Why are zero entries removed in set?
- What is the cost of dot when both vectors have few nonzero entries?