mirror of
https://github.com/prdlk/leetcode.git
synced 2026-08-02 17:31:40 +00:00
152 lines
5.8 KiB
JavaScript
152 lines
5.8 KiB
JavaScript
// ============================================================
|
|||
|
|
// ANSWER KEY — open only after narrating all 6 out loud.
|
||
|
|
// Solutions match your repo style exactly:
|
||
|
|
// ~/Developer/github.com/prdlk/leetcode/work/Default
|
||
|
|
// (object freq maps, (freq[n] || 0) + 1, same shapes)
|
||
|
|
// ============================================================
|
||
|
|
|
||
|
|
// problem1 = LC 1365 — How Many Numbers Are Smaller Than Current
|
||
|
|
// (your file: Easy/Array/1365...js — this IS your solution)
|
||
|
|
// RULE: "output[i] = count of elements strictly smaller than arr[i];
|
||
|
|
// ties don't count as smaller (that's the [7,7,7,7] example)."
|
||
|
|
function problem1(nums) {
|
||
|
|
// Step 1: Begin by initializing a [Frequency Map]
|
||
|
|
const freq = {};
|
||
|
|
for (let n of nums) freq[n] = (freq[n] || 0) + 1;
|
||
|
|
|
||
|
|
// Step 2: Sort the numbers by ascending order
|
||
|
|
const sorted = Object.keys(freq).sort((a, b) => a - b);
|
||
|
|
|
||
|
|
// Step 3: Init a count of numbers smaller than the active number
|
||
|
|
let count = 0;
|
||
|
|
|
||
|
|
// Step 4: Init a map to track number of values smaller for each number
|
||
|
|
const smaller = {};
|
||
|
|
|
||
|
|
// Step 5: Iterate over the sorted list
|
||
|
|
for (let num of sorted) {
|
||
|
|
// Set count for active number — BEFORE adding own frequency,
|
||
|
|
// so duplicates only see values strictly below them
|
||
|
|
smaller[num] = count;
|
||
|
|
|
||
|
|
// Update the count by frequency
|
||
|
|
count += freq[num];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 6: Use original list and find number of smaller values than it
|
||
|
|
return nums.map((n) => smaller[n]);
|
||
|
|
}
|
||
|
|
// Narration reminders:
|
||
|
|
// - "record before adding" is WHY [7,7,7,7] => [0,0,0,0]
|
||
|
|
// - Object.keys returns strings; (a, b) => a - b coerces numerically
|
||
|
|
// - quick brute-force alternative if short on time:
|
||
|
|
// nums.map((n) => nums.filter((m) => m < n).length)
|
||
|
|
|
||
|
|
// problem2 = LC 451 — Sort Characters By Frequency (+ alpha tiebreak)
|
||
|
|
// (your file: Medium/Hash Table/451...js — same, tiebreak included)
|
||
|
|
// RULE: "rebuild the string most-frequent chars first; equal counts
|
||
|
|
// break alphabetically."
|
||
|
|
// DISCRIMINATOR: "bookkeeper" — e:3, then k:2/o:2 tie -> k before o
|
||
|
|
// = alphabetical, NOT input order (o appeared first in the input!).
|
||
|
|
function problem2(s) {
|
||
|
|
// count the frequency of each character
|
||
|
|
const freq = {};
|
||
|
|
for (let c of s) freq[c] = (freq[c] || 0) + 1;
|
||
|
|
|
||
|
|
// sort the characters by frequency, ties alphabetical
|
||
|
|
return s
|
||
|
|
.split("")
|
||
|
|
.sort((a, b) => freq[b] - freq[a] || a.localeCompare(b))
|
||
|
|
.join("");
|
||
|
|
}
|
||
|
|
// Note vs your repo file: identical. localeCompare orders lowercase
|
||
|
|
// before uppercase ("bbaA"), which is what the drill examples use.
|
||
|
|
// LC 451 proper accepts any tie order — the tiebreak is the
|
||
|
|
// interview twist Jim's source described.
|
||
|
|
|
||
|
|
// problem3 = LC 1636 — Sort Array by Increasing Frequency
|
||
|
|
// (your file: Easy/Array/1636...js — this IS your solution)
|
||
|
|
// RULE: "sort by frequency ascending; ties by VALUE DESCENDING."
|
||
|
|
// DISCRIMINATOR: [2,3,1,3,2] -> 2 and 3 both appear twice, 3 first.
|
||
|
|
function problem3(nums) {
|
||
|
|
const freq = {};
|
||
|
|
for (let n of nums) freq[n] = (freq[n] || 0) + 1;
|
||
|
|
return nums.sort((a, b) => freq[a] - freq[b] || b - a);
|
||
|
|
}
|
||
|
|
// The idiom to say out loud: "primary key OR tiebreak — when the
|
||
|
|
// frequency difference is 0 (falsy), JS falls through to b - a."
|
||
|
|
|
||
|
|
// problem4 = LC 387 — First Unique Character
|
||
|
|
// (your file: Easy/Hash Table/387...js — this IS your solution)
|
||
|
|
// RULE: "index of the first character appearing exactly once; -1 if none."
|
||
|
|
// SHAPE TELL: output is a NUMBER, not an array.
|
||
|
|
function problem4(s) {
|
||
|
|
const freq = {};
|
||
|
|
for (let c of s) freq[c] = (freq[c] || 0) + 1;
|
||
|
|
|
||
|
|
for (let i = 0; i < s.length; i++) {
|
||
|
|
if (freq[s[i]] === 1) {
|
||
|
|
return i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
// Say it: "two passes — I can't know a char is unique until I've
|
||
|
|
// seen the whole string."
|
||
|
|
|
||
|
|
// problem5 = LC 242 — Valid Anagram
|
||
|
|
// (not in your repo yet — written in your exact style)
|
||
|
|
// RULE: "true iff both strings have the same characters with the
|
||
|
|
// same counts."
|
||
|
|
// DISCRIMINATOR: ("aacc", "ccac") -> same char SET {a,c}, different
|
||
|
|
// counts -> false. Kills set-equality.
|
||
|
|
function problem5(s, t) {
|
||
|
|
if (s.length !== t.length) return false;
|
||
|
|
|
||
|
|
const freq = {};
|
||
|
|
for (let c of s) freq[c] = (freq[c] || 0) + 1;
|
||
|
|
|
||
|
|
// walk t, spending counts down; a missing/exhausted char fails
|
||
|
|
for (let c of t) {
|
||
|
|
if (!freq[c]) return false;
|
||
|
|
freq[c]--;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
// The length guard up front is what lets count-down work without a
|
||
|
|
// final "all zeros" pass.
|
||
|
|
|
||
|
|
// problem6 = LC 347 — Top K Frequent Elements
|
||
|
|
// (your file: Medium/Array/347...js — this IS your solution)
|
||
|
|
// RULE: "return the k values that appear most often, most frequent
|
||
|
|
// first."
|
||
|
|
// SHAPE TELL: second argument k controls output length.
|
||
|
|
// EDGE: [3,0,1,0] k=1 => [0] — value 0 is falsy but valid.
|
||
|
|
function problem6(nums, k) {
|
||
|
|
const freq = {};
|
||
|
|
for (let n of nums) freq[n] = (freq[n] || 0) + 1;
|
||
|
|
|
||
|
|
return Object.keys(freq)
|
||
|
|
.map(Number) // Object.keys gave us strings — convert back
|
||
|
|
.sort((a, b) => freq[b] - freq[a])
|
||
|
|
.slice(0, k);
|
||
|
|
}
|
||
|
|
// The .map(Number) is the classic gotcha to mention: without it you
|
||
|
|
// return ["1","2"] instead of [1,2].
|
||
|
|
|
||
|
|
// ============================================================
|
||
|
|
// THE HAMMER (all six are this skeleton):
|
||
|
|
// 1. BUILD -> const freq = {}; for (let x of input)
|
||
|
|
// freq[x] = (freq[x] || 0) + 1;
|
||
|
|
// 2. ORDER -> sort keys/elements by the criterion the pattern demands
|
||
|
|
// 3. DERIVE -> compute what each key maps to (count / rank / index)
|
||
|
|
// 4. EMIT -> map back to input order / rebuild string / slice k
|
||
|
|
//
|
||
|
|
// SELF-SCORE per problem:
|
||
|
|
// Rule stated in one sentence, verified vs ALL examples: /1
|
||
|
|
// Tiebreak/edge named BEFORE coding (the discriminator): /1
|
||
|
|
// Working code, narrated while typing: /1
|
||
|
|
// 15+/18 = ready. Misses tell you what to re-drill Tuesday
|
||
|
|
// morning (max 2 reps, then stop — rest beats an 11th rep).
|
||
|
|
// ============================================================
|