Skip to content

Latest commit

 

History

History
329 lines (261 loc) · 7.37 KB

patterns.md

File metadata and controls

329 lines (261 loc) · 7.37 KB

10 Common methods of pattern use to solve algorithms

Brute Force: The simplest approach, tries all possible solutions.

function bruteForceSearch(arr, target) {
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) {
            return i;
        }
    }
    return -1;
}

Divide and Conquer: Breaks down a problem into smaller sub-problems until they become simple enough to solve directly.

function binarySearch(arr, x) {
    let l = 0;
    let r = arr.length - 1;

    while (l <= r) {
        let m = Math.floor((l + r) / 2);
        if (arr[m] === x) {
            return m;
        } else if (arr[m] < x) {
            l = m + 1;
        } else {
            r = m - 1;
        }
    }
    return -1;
}

Dynamic Programming: Solves complex problems by breaking them down into simpler overlapping sub-problems.

function fibonacci(n) {
    let fib = [];
    fib[0] = 0;
    fib[1] = 1;

    for (let i = 2; i <= n; i++) {
        fib[i] = fib[i - 1] + fib[i - 2];
    }
    return fib[n];
}

Greedy Algorithms: Makes locally optimal choices at each stage to find a global optimum.

function coinChange(coins, amount) {
    coins.sort((a, b) => b - a); // Sort coins in descending order
    let count = 0;

    for (let i = 0; i < coins.length; i++) {
        while (amount >= coins[i]) {
            amount -= coins[i];
            count++;
        }
    }
    return count;
}

Backtracking: Systematically searches for a solution by trying all possible options and backtracks when it hits a dead end.

function solveNQueens(n) {
    const result = [];

    function backtrack(board, row) {
        if (row === n) {
            result.push([...board]);
            return;
        }

        for (let col = 0; col < n; col++) {
            if (isSafe(board, row, col)) {
                board[row][col] = 'Q';
                backtrack(board, row + 1);
                board[row][col] = '.';
            }
        }
    }

    function isSafe(board, row, col) {
        // Check if placing a queen at board[row][col] is safe
        // (Check row, column, and diagonals)
    }

    const board = Array.from({ length: n }, () => Array(n).fill('.'));
    backtrack(board, 0);
    return result;
}

Depth-First Search (DFS) and Breadth-First Search (BFS): Traversal algorithms used in graphs and trees.

Depth-First Search (DFS) - Graph Traversal:
class Graph {
    constructor() {
        this.adjacencyList = {};
    }

    addVertex(vertex) {
        if (!this.adjacencyList[vertex]) {
            this.adjacencyList[vertex] = [];
        }
    }

    addEdge(v1, v2) {
        this.adjacencyList[v1].push(v2);
        this.adjacencyList[v2].push(v1);
    }

    dfsRecursive(start) {
        const visited = {};
        const result = [];

        const dfs = (vertex) => {
            if (!vertex) return null;
            visited[vertex] = true;
            result.push(vertex);
            this.adjacencyList[vertex].forEach((neighbor) => {
                if (!visited[neighbor]) {
                    return dfs(neighbor);
                }
            });
        };

        dfs(start);
        return result;
    }

    // Iterative DFS can be implemented using a stack
    dfsIterative(start) {
        const stack = [start];
        const result = [];
        const visited = {};
        let currentVertex;

        visited[start] = true;
        while (stack.length) {
            currentVertex = stack.pop();
            result.push(currentVertex);

            this.adjacencyList[currentVertex].forEach((neighbor) => {
                if (!visited[neighbor]) {
                    visited[neighbor] = true;
                    stack.push(neighbor);
                }
            });
        }

        return result;
    }
}

// Usage:
const graph = new Graph();
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
console.log(graph.dfsRecursive('A'));
console.log(graph.dfsIterative('A'));
Breadth-First Search (BFS) - Graph Traversal:

class Graph {
    // (Same Graph class as above)

    bfs(start) {
        const queue = [start];
        const result = [];
        const visited = {};
        let currentVertex;

        visited[start] = true;
        while (queue.length) {
            currentVertex = queue.shift();
            result.push(currentVertex);

            this.adjacencyList[currentVertex].forEach((neighbor) => {
                if (!visited[neighbor]) {
                    visited[neighbor] = true;
                    queue.push(neighbor);
                }
            });
        }

        return result;
    }
}

// Usage:
const graph = new Graph();
// (Add vertices and edges similar to previous example)
console.log(graph.bfs('A'));

Binary Search: Searches a sorted array by repeatedly dividing the search interval in half.

function binarySearch(arr, target) {
    let left = 0;
    let right = arr.length - 1;

    while (left <= right) {
        let mid = Math.floor((left + right) / 2);
        if (arr[mid] === target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

Sliding Window: Solves problems where you maintain a subset (window) of elements within an array or string.

function maxSubarraySum(arr, num) {
    if (arr.length < num) return null;

    let maxSum = 0;
    let tempSum = 0;

    for (let i = 0; i < num; i++) {
        maxSum += arr[i];
    }
    tempSum = maxSum;

    for (let i = num; i < arr.length; i++) {
        tempSum = tempSum - arr[i - num] + arr[i];
        maxSum = Math.max(maxSum, tempSum);
    }

    return maxSum;
}

Two Pointers: Uses two pointers to solve problems, often in arrays, linked lists, or strings.

function sumZero(arr) {
    let left = 0;
    let right = arr.length - 1;

    while (left < right) {
        let sum = arr[left] + arr[right];
        if (sum === 0) {
            return [arr[left], arr[right]];
        } else if (sum > 0) {
            right--;
        } else {
            left++;
        }
    }
    return undefined;
}

Graph Algorithms: Various algorithms like Dijkstra's, Floyd-Warshall, etc., used in graph-related problems.

function dijkstra(graph, start, end) {
    const distances = {};
    const visited = {};
    const queue = new PriorityQueue();

    for (let vertex in graph) {
        if (vertex === start) {
            distances[vertex] = 0;
            queue.enqueue(vertex, 0);
        } else {
            distances[vertex] = Infinity;
            queue.enqueue(vertex, Infinity);
        }
    }

    while (!queue.isEmpty()) {
        const current = queue.dequeue().val;
        if (current === end) {
            // Found the shortest path
            // (Return distances[end] or reconstruct the path)
            break;
        }

        if (!visited[current]) {
            visited[current] = true;
            for (let neighbor in graph[current]) {
                let distance = distances[current] + graph[current][neighbor];
                if (distance < distances[neighbor]) {
                    distances[neighbor] = distance;
                    queue.enqueue(neighbor, distance);
                }
            }
        }
    }

    // Return distances[end] or the shortest path
}