forked from neetcode-gh/leetcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
444 additions
and
277 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,124 +1,70 @@ | ||
// Min Heap Implementation | ||
/** | ||
* Prim's algorithm | ||
* https://leetcode.com/problems/min-cost-to-connect-all-points/solution/ | ||
* @param {number[][]} points | ||
* @return {number} | ||
*/ | ||
const minCostConnectPoints = (points) => { | ||
const isBaseCase = ((points.length === 0) || (1000 <= points.length)); | ||
if (isBaseCase) return 0; | ||
|
||
const { graph, seen, minHeap } = buildGraph(points); | ||
|
||
return search(points, graph, seen, minHeap); | ||
}; | ||
|
||
const initGraph = (points) => ({ | ||
graph: new Array(points.length).fill().map(() => []), | ||
seen: new Array(points.length).fill(false), | ||
minHeap: new MinPriorityQueue() | ||
}) | ||
|
||
const buildGraph = (points) => { | ||
const { graph, seen, minHeap } = initGraph(points); | ||
|
||
for (let src = 0; src < (points.length - 1); src++) { | ||
for (let dst = (src + 1); (dst < points.length); dst++) { | ||
const cost = getCost(points, src, dst); | ||
|
||
class MinHeap { | ||
constructor(nums) { | ||
this.data = []; | ||
for (let i = 0; i < nums.length; i++) { | ||
this.add(nums[i]); | ||
graph[src].push([ dst, cost ]); | ||
graph[dst].push([ src, cost ]); | ||
} | ||
} | ||
|
||
getParentIndex = (i) => Math.floor((i - 1) / 2); | ||
const [ src, cost, priority ] = [ 0, 0, 0 ]; | ||
const node = [ src, cost ]; | ||
|
||
getLeftChildIndex = (i) => i * 2 + 1; | ||
minHeap.enqueue(node, priority); | ||
|
||
getRightChildIndex = (i) => i * 2 + 2; | ||
return { graph, seen, minHeap }; | ||
} | ||
|
||
swap = (i1, i2) => { | ||
const tmp = this.data[i1]; | ||
this.data[i1] = this.data[i2]; | ||
this.data[i2] = tmp; | ||
}; | ||
const getCost = (points, src, dst) => { | ||
const [ [ x1, y1 ], [ x2, y2 ] ] = [ points[src], points[dst] ]; | ||
|
||
add = (e) => { | ||
this.data[this.data.length] = e; | ||
this.heapify(this.data.length - 1); | ||
return this.data[0]; | ||
}; | ||
return (Math.abs(x1 - x2) + Math.abs(y1 - y2)); | ||
} | ||
|
||
heapify = (curIndex) => { | ||
if ( | ||
this.data[this.getParentIndex(curIndex)] !== undefined && | ||
this.data[curIndex][0] < this.data[this.getParentIndex(curIndex)][0] | ||
) { | ||
this.swap(curIndex, this.getParentIndex(curIndex)); | ||
this.heapify(this.getParentIndex(curIndex)); | ||
} | ||
}; | ||
|
||
pop = () => { | ||
const firstElement = this.data[0]; | ||
if (this.data.length > 1) { | ||
// replace it with the last element in heap | ||
this.data[0] = this.data[this.data.length - 1]; | ||
// remove last elem | ||
this.data.pop(); | ||
this.heapifyDown(); | ||
} | ||
const search = (points, graph, seen, minHeap, nodeCount = 0, cost = 0) => { | ||
while (nodeCount < points.length) { | ||
let [ src, srcCost ] = minHeap.dequeue().element; | ||
|
||
return firstElement; | ||
}; | ||
|
||
heapifyDown = () => { | ||
let cur = 0; | ||
|
||
while (this.data[this.getLeftChildIndex(cur)] !== undefined) { | ||
// get the smallest child (right or left) | ||
let smallChildInd = this.getLeftChildIndex(cur); | ||
if ( | ||
this.data[this.getRightChildIndex(cur)] !== undefined && | ||
this.data[this.getRightChildIndex(cur)][0] < | ||
this.data[this.getLeftChildIndex(cur)][0] | ||
) { | ||
smallChildInd = this.getRightChildIndex(cur); | ||
} | ||
// if one child (r or l) is less than curr we swap | ||
if (this.data[smallChildInd][0] < this.data[cur][0]) { | ||
this.swap(cur, smallChildInd); | ||
} | ||
cur = smallChildInd; | ||
} | ||
}; | ||
if (seen[src]) continue; | ||
seen[src] = true; | ||
|
||
cost += srcCost; | ||
nodeCount += 1; | ||
|
||
checkNeighbors(graph, src, seen, minHeap); | ||
} | ||
|
||
return cost; | ||
} | ||
|
||
const minCostConnectPoints = function (points) { | ||
const n = points.length; | ||
let finalCost = 0; | ||
|
||
if (n > 1 && n <= 1000) { | ||
let x1, x2; | ||
let y1, y2; | ||
let dist; | ||
|
||
const adjList = new Map(); | ||
// prepare adjacent list (each node has cost to every other node) | ||
for (let i = 0; i < n - 1; i++) { | ||
[x1, y1] = points[i]; | ||
|
||
for (let j = i + 1; j < n; j++) { | ||
[x2, y2] = points[j]; | ||
dist = Math.abs(x1 - x2) + Math.abs(y1 - y2); | ||
adjList.get(i) | ||
? adjList.get(i).push([dist, j]) | ||
: adjList.set(i, [[dist, j]]); | ||
adjList.get(j) | ||
? adjList.get(j).push([dist, i]) | ||
: adjList.set(j, [[dist, i]]); | ||
} | ||
} | ||
const checkNeighbors = (graph, src, seen, minHeap) => { | ||
for (const [ dst, dstCost ] of graph[src]) { | ||
if (seen[dst]) continue; | ||
|
||
// prim's algorithm | ||
const visited = new Set(); | ||
const minHeap = new MinHeap([[0, 0]]); // [cost,point] | ||
// we gonna visit each node | ||
while (visited.size < n) { | ||
let partialCost = 0, | ||
i = 0; | ||
|
||
// get the least cost & its correspondent node | ||
[partialCost, i] = minHeap.pop(); | ||
|
||
// if the node hasn't been visited | ||
if (!visited.has(i)) { | ||
finalCost += partialCost; | ||
visited.add(i); | ||
for (const neighbourWithCost of adjList.get(i)) { | ||
if (!visited.has(neighbourWithCost[1])) { | ||
minHeap.add(neighbourWithCost); | ||
} | ||
} | ||
} | ||
} | ||
minHeap.enqueue([ dst, dstCost ], dstCost); | ||
} | ||
return finalCost; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,179 @@ | ||
let alienOrder = function (words) { | ||
let graph = {}; | ||
/** | ||
* BFS | ||
* https://leetcode.com/problems/alien-dictionary/ | ||
* @param {string[]} words | ||
* @return {string} | ||
*/ | ||
var alienOrder = function(words) { | ||
const { graph, frequencyMap, queue, buffer } = buildGraph(words); | ||
|
||
for (let i = 0; i < words.length; i++) { | ||
for (let j = 0; j < words[i].length; j++) { | ||
graph[words[i][j]] = new Set(); | ||
if (!canBuildGraph(words, graph, frequencyMap)) return ''; | ||
|
||
queueSources(queue, frequencyMap); | ||
bfs(queue, frequencyMap, graph, buffer); | ||
|
||
return (frequencyMap.size <= buffer.length) | ||
? buffer.join('') | ||
: ''; | ||
} | ||
|
||
var initGraph = () => ({ | ||
graph: new Map(), | ||
frequencyMap: new Map(), | ||
queue: new Queue(), | ||
buffer: [], | ||
}) | ||
|
||
var buildGraph = (words) => { | ||
const { graph, frequencyMap, queue, buffer } = initGraph(); | ||
|
||
for (const word of words) { | ||
for (const char of word) { | ||
frequencyMap.set(char, 0); | ||
graph.set(char, []); | ||
} | ||
} | ||
|
||
for (let i = 0; i < words.length - 1; i++) { | ||
let word1 = words[i]; | ||
let word2 = words[i + 1]; | ||
return { graph, frequencyMap, queue, buffer }; | ||
}; | ||
|
||
var canBuildGraph = (words, graph, frequencyMap) => { | ||
for (let index = 0; (index < words.length - 1); index++) { | ||
const [ word1, word2 ] = [ words[index], words[(index + 1)] ]; | ||
const minLength = Math.min(word1.length, word2.length) | ||
|
||
if (word1.length > word2.length && (word1 + '').startsWith(word2)) { | ||
return ''; | ||
} | ||
const isWord1Longer = (word2.length < word1.length); | ||
const isPrefix = isWord1Longer && word1.startsWith(word2); | ||
|
||
if (isPrefix) return false; | ||
|
||
for (let j = 0; j < Math.min(word1.length, word2.length); j++) { | ||
let c1 = word1[j]; | ||
let c2 = word2[j]; | ||
for (let j = 0; (j < minLength); j++) { | ||
const [ char1, char2 ] = [ word1[j], word2[j] ]; | ||
|
||
if (c1 !== c2) { | ||
graph[c1].add(c2); | ||
break; | ||
} | ||
const isEqual = (char1 === char2); | ||
if (isEqual) continue; | ||
|
||
graph.get(char1).push(char2); | ||
frequencyMap.set(char2, frequencyMap.get(char2) + 1); | ||
|
||
break; | ||
} | ||
} | ||
|
||
let visited = {}; // 'false' = visited, 'true' = current path | ||
let res = []; | ||
return true; | ||
}; | ||
|
||
function dfs(c) { | ||
if (visited[c]) { | ||
return Boolean(visited[c]); | ||
const bfs = (queue, frequencyMap, graph, buffer) => { | ||
while (!queue.isEmpty()) { | ||
for (let level = (queue.size() - 1); (0 <= level); level--) { | ||
checkNeighbors(queue, frequencyMap, graph, buffer) | ||
} | ||
} | ||
}; | ||
|
||
visited[c] = 'true'; | ||
for (let nei of graph[c]) { | ||
if (dfs(nei)) { | ||
return true; | ||
} | ||
} | ||
var checkNeighbors = (queue, frequencyMap, graph, buffer) => { | ||
const char = queue.dequeue(); | ||
|
||
buffer.push(char); | ||
|
||
for (const next of graph.get(char)) { | ||
const value = (frequencyMap.get(next) - 1); | ||
|
||
frequencyMap.set(next, value); | ||
|
||
visited[c] = 'false'; | ||
res.push(c); | ||
const isEmpty = (frequencyMap.get(next) === 0); | ||
if (!isEmpty) continue; | ||
|
||
queue.enqueue(next); | ||
} | ||
} | ||
|
||
const queueSources = (queue, frequencyMap) => { | ||
for (const [ key, value ] of frequencyMap) { | ||
const isEmpty = (frequencyMap.get(key) === 0); | ||
if (!isEmpty) continue; | ||
|
||
queue.enqueue(key); | ||
} | ||
} | ||
|
||
/** | ||
* DFS | ||
* https://leetcode.com/problems/alien-dictionary/ | ||
* @param {string[]} words | ||
* @return {string} | ||
*/ | ||
var alienOrder = function(words) { | ||
const { graph, seen, buffer } = buildGraph(words); | ||
|
||
if (!canBuildGraph(words, graph)) return ''; | ||
|
||
Object.keys(graph).forEach((c) => { | ||
if (dfs(c)) { | ||
return ''; | ||
for (const [ char ] of graph) { | ||
if (!dfs(char, graph, seen, buffer)) return ''; | ||
} | ||
|
||
return buffer.reverse().join('') | ||
} | ||
|
||
var initGraph = () => ({ | ||
graph: new Map(), | ||
seen: new Map(), | ||
buffer: [], | ||
}) | ||
|
||
var buildGraph = (words) => { | ||
const { graph, seen, buffer } = initGraph(); | ||
|
||
for (const word of words) { | ||
for (const char of word) { | ||
graph.set(char, []); | ||
} | ||
}); | ||
} | ||
|
||
return res.reverse().join(''); | ||
return { graph, seen, buffer }; | ||
}; | ||
|
||
var canBuildGraph = (words, graph) => { | ||
for (let index = 0; (index < words.length - 1); index++) { | ||
const [ word1, word2 ] = [ words[index], words[(index + 1)] ]; | ||
const minLength = Math.min(word1.length, word2.length) | ||
|
||
const isWord1Longer = (word2.length < word1.length); | ||
const isPrefix = isWord1Longer && word1.startsWith(word2); | ||
|
||
if (isPrefix) return false; | ||
|
||
for (let j = 0; (j < minLength); j++) { | ||
const [ char1, char2 ] = [ word1[j], word2[j] ]; | ||
|
||
const isEqual = (char1 === char2); | ||
if (isEqual) continue; | ||
|
||
graph.get(char1).push(char2); | ||
|
||
break; | ||
} | ||
} | ||
|
||
return true; | ||
}; | ||
|
||
const dfs = (char, graph, seen, buffer) => { | ||
if (seen.has(char)) return seen.get(char); | ||
|
||
if (!backTrack(char, graph, seen, buffer)) return false; | ||
|
||
buffer.push(char); | ||
|
||
return true; | ||
} | ||
|
||
const backTrack = (char, graph, seen, buffer) => { | ||
seen.set(char, false); | ||
for (const neighbor of graph.get(char)) { | ||
if (!dfs(neighbor, graph, seen, buffer)) return false; | ||
} | ||
seen.set(char, true); | ||
|
||
return true; | ||
} |
Oops, something went wrong.