ASAPUtils Logo ASAPUtils
Foundation · Week 0

Memory Model — C++ vs JavaScript

What a vector, an array, a hash map and a string actually are in memory, and how C++ and JavaScript differ. Knowing this is why "just use a hash map" stops being a magic phrase and starts being a trade you can price.

Why a foundations page about memory

Because “just use a hash map” is advice, not understanding. A hash map buys you O(1) lookup and costs you O(n) space plus a hash computation per access. Once you can price that trade, you stop guessing at solutions and start choosing them.

The structures, physically

Array / vector<int> / JS array — a contiguous block. Index access is one pointer arithmetic operation, hence O(1). Inserting in the middle shifts everything after it, hence O(n). Appending is amortised O(1): most appends are free, and the occasional doubling-resize is paid off across all the cheap ones.

Difference: a C++ vector<int> really is a contiguous block of ints. A JS array is an object that the engine usually optimises into a contiguous block — until you make it sparse or mix types, at which point it silently degrades to a dictionary. Keep JS arrays dense and single-typed in hot loops.

Hash map — an array of buckets plus a hash function. Average O(1), worst case O(n) if everything collides. The cost you’re paying is memory plus hashing per access.

Difference: C++ unordered_map hashes; map is a balanced tree with O(log n) ops but keeps keys sorted. JS Map preserves insertion order and takes any key type; a plain {} coerces all keys to strings, which is a real bug source when your keys are numbers.

Linked list — nodes scattered in memory, each pointing to the next. O(1) insert/delete at a known node, but O(n) to find that node, and terrible cache behaviour. That’s why arrays beat linked lists in practice far more often than the complexity table suggests.

String — in C++ std::string is mutable and s += c is amortised O(1). In JavaScript strings are immutable, so s += c in a loop can be O(n²). Push characters into an array and join('') at the end.

The translation table

NeedC++JavaScript
Dynamic arrayvector<int> v; v.push_back(x);const v = []; v.push(x);
2-D arrayvector<vector<int>> g(R, vector<int>(C, 0));Array.from({length:R},()=>Array(C).fill(0))
Hash mapunordered_map<int,int> m; m[k]++;const m = new Map(); m.set(k,(m.get(k)??0)+1);
Hash setunordered_set<int> s; s.insert(x);const s = new Set(); s.add(x);
Sortsort(v.begin(), v.end());v.sort((a,b)=>a-b);
Sort by keysort(v.begin(),v.end(),[](auto&a,auto&b){return a[0]<b[0];});v.sort((a,b)=>a[0]-b[0]);
Min-heappriority_queue<int, vector<int>, greater<int>> pq;none built in — write a MinHeap once
Stackstack<int> st;array + push/pop
Queuequeue<int> q;array + an index pointer (not shift())
Dequedeque<int> dq;array with push/pop/shift/unshift
Build a stringstring s; s += c;const parts=[]; parts.push(c); parts.join('')
Char to indexc - 'a'c.charCodeAt(0) - 97
Integer limitsINT_MAX, LLONG_MAXNumber.MAX_SAFE_INTEGER (2⁵³−1)
Integer divisiona / b for intsMath.trunc(a/b)

JavaScript gotchas that cost interviews

  1. [10, 9, 1].sort() gives [1, 10, 9]. The default sort is lexicographic. Always pass a comparator.
  2. arr.shift() is O(n). A BFS queue built on shift() is O(n²). Use an index pointer: let head = 0; while (head < q.length) { const u = q[head++]; ... }
  3. Strings are immutable — build with an array and join('').
  4. No integer type. Bitwise operators coerce to 32-bit signed, so 1 << 31 goes negative and large XOR results surprise you.
  5. {} stringifies keys. obj[1] and obj['1'] are the same slot. Use Map when keys are numbers or objects.
  6. No built-in heap. Write a MinHeap class once, memorise it, reuse it all quarter.

C++ gotchas that cost interviews

  1. m[k] on a map inserts. Testing membership with if (m[k]) silently creates the entry. Use m.count(k) or m.find(k) != m.end().
  2. Signed overflow is undefined behaviour. Use long long when summing or multiplying anything that could exceed ~2·10⁹.
  3. (l + r) / 2 can overflow. Write l + (r - l) / 2. Always.
  4. priority_queue is a max-heap by default. A min-heap needs priority_queue<int, vector<int>, greater<int>>.
  5. Passing containers by value copies them. Use const vector<int>& for parameters and vector<int>& when you mean to modify.

The rule for this quarter

Solve in one language, immediately port to the other, before writing anything up. If the port takes three minutes, you understood the algorithm. If it takes twenty, you memorised syntax — which means you should redo the problem, not the translation.

Related Topics