Skip to content

Commit

Permalink
refactor: js - aGraph
Browse files Browse the repository at this point in the history
  • Loading branch information
aakhtar3 committed Sep 26, 2022
1 parent fc7683e commit f7c7b83
Show file tree
Hide file tree
Showing 6 changed files with 444 additions and 277 deletions.
166 changes: 56 additions & 110 deletions javascript/1584-Min-Cost-to-Connect-all-Points.js
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;
};
}
196 changes: 160 additions & 36 deletions javascript/269-Alien-Dictionary.js
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;
}
Loading

0 comments on commit f7c7b83

Please sign in to comment.