Below is the implementation of the terrain navigator:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147/** * DFS with Memoization * Time Complexity: O(m * n) - We visit each cell exactly once and perform constant work for each cell * Space Complexity: O(m * n) - We use a memo table of size m * n to store the longest path starting from each cell * * @param {number[][]} matrix - The input matrix * @return {number} - The length of the longest increasing path */function longestIncreasingPath(matrix) { if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return 0; } const m = matrix.length; const n = matrix[0].length; const memo = Array(m).fill().map(() => Array(n).fill(0)); const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; // up, down, left, right function dfs(i, j) { // If we've already computed the result for this cell, return it if (memo[i][j] !== 0) { return memo[i][j]; } // Initialize the longest path from this cell as 1 (counting the current cell) let longest = 1; // Explore all four adjacent cells for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; // Check if the adjacent cell is within bounds and has a higher value if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] > matrix[i][j]) { // Update the longest path if a longer path is found longest = Math.max(longest, 1 + dfs(ni, nj)); } } // Memoize the result and return it memo[i][j] = longest; return longest; } // Find the longest path starting from each cell let result = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { result = Math.max(result, dfs(i, j)); } } return result;} /** * Topological Sort (Kahn's Algorithm) * Time Complexity: O(m * n) - We process each cell exactly once and perform constant work for each cell * Space Complexity: O(m * n) - We use additional space for the graph, in-degree array, queue, and distances array * * @param {number[][]} matrix - The input matrix * @return {number} - The length of the longest increasing path */function longestIncreasingPathTopological(matrix) { if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return 0; } const m = matrix.length; const n = matrix[0].length; const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; // up, down, left, right // Calculate in-degree of each cell const inDegree = Array(m).fill().map(() => Array(n).fill(0)); for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] > matrix[i][j]) { inDegree[i][j]++; } } } } // Initialize queue with cells that have in-degree 0 const queue = []; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { if (inDegree[i][j] === 0) { queue.push([i, j]); } } } // Initialize distances array const distances = Array(m).fill().map(() => Array(n).fill(1)); // Process cells in topological order while (queue.length > 0) { const [i, j] = queue.shift(); for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] < matrix[i][j]) { distances[ni][nj] = Math.max(distances[ni][nj], distances[i][j] + 1); inDegree[ni][nj]--; if (inDegree[ni][nj] === 0) { queue.push([ni, nj]); } } } } // Find the maximum distance let result = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { result = Math.max(result, distances[i][j]); } } return result;} // Test casesconst matrix1 = [ [9, 9, 4], [6, 6, 8], [2, 1, 1]];console.log(longestIncreasingPath(matrix1)); // 4 const matrix2 = [ [3, 4, 5], [3, 2, 6], [2, 2, 1]];console.log(longestIncreasingPath(matrix2)); // 4 const matrix3 = [[1]];console.log(longestIncreasingPath(matrix3)); // 1
Let's break down the implementation:
Implement the terrain navigator solution in different programming languages.
Below is the implementation of the terrain navigator in different programming languages. Select a language tab to view the corresponding code.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147/** * DFS with Memoization * Time Complexity: O(m * n) - We visit each cell exactly once and perform constant work for each cell * Space Complexity: O(m * n) - We use a memo table of size m * n to store the longest path starting from each cell * * @param {number[][]} matrix - The input matrix * @return {number} - The length of the longest increasing path */function longestIncreasingPath(matrix) { if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return 0; } const m = matrix.length; const n = matrix[0].length; const memo = Array(m).fill().map(() => Array(n).fill(0)); const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; // up, down, left, right function dfs(i, j) { // If we've already computed the result for this cell, return it if (memo[i][j] !== 0) { return memo[i][j]; } // Initialize the longest path from this cell as 1 (counting the current cell) let longest = 1; // Explore all four adjacent cells for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; // Check if the adjacent cell is within bounds and has a higher value if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] > matrix[i][j]) { // Update the longest path if a longer path is found longest = Math.max(longest, 1 + dfs(ni, nj)); } } // Memoize the result and return it memo[i][j] = longest; return longest; } // Find the longest path starting from each cell let result = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { result = Math.max(result, dfs(i, j)); } } return result;} /** * Topological Sort (Kahn's Algorithm) * Time Complexity: O(m * n) - We process each cell exactly once and perform constant work for each cell * Space Complexity: O(m * n) - We use additional space for the graph, in-degree array, queue, and distances array * * @param {number[][]} matrix - The input matrix * @return {number} - The length of the longest increasing path */function longestIncreasingPathTopological(matrix) { if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return 0; } const m = matrix.length; const n = matrix[0].length; const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; // up, down, left, right // Calculate in-degree of each cell const inDegree = Array(m).fill().map(() => Array(n).fill(0)); for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] > matrix[i][j]) { inDegree[i][j]++; } } } } // Initialize queue with cells that have in-degree 0 const queue = []; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { if (inDegree[i][j] === 0) { queue.push([i, j]); } } } // Initialize distances array const distances = Array(m).fill().map(() => Array(n).fill(1)); // Process cells in topological order while (queue.length > 0) { const [i, j] = queue.shift(); for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] < matrix[i][j]) { distances[ni][nj] = Math.max(distances[ni][nj], distances[i][j] + 1); inDegree[ni][nj]--; if (inDegree[ni][nj] === 0) { queue.push([ni, nj]); } } } } // Find the maximum distance let result = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { result = Math.max(result, distances[i][j]); } } return result;} // Test casesconst matrix1 = [ [9, 9, 4], [6, 6, 8], [2, 1, 1]];console.log(longestIncreasingPath(matrix1)); // 4 const matrix2 = [ [3, 4, 5], [3, 2, 6], [2, 2, 1]];console.log(longestIncreasingPath(matrix2)); // 4 const matrix3 = [[1]];console.log(longestIncreasingPath(matrix3)); // 1
First, understand that we need to find the length of the longest increasing path in the matrix, where we can move in four directions (up, down, left, right) and can only move to cells with higher values.
Model the problem as finding the longest path in a directed acyclic graph (DAG), where each cell is a node, and there's an edge from cell A to cell B if B is adjacent to A and has a higher value.
We can use either DFS with memoization or topological sort to solve this problem. Both approaches have their advantages.
For each cell, explore all four adjacent cells with higher values and recursively find the longest path starting from those cells. Use memoization to avoid redundant calculations.
Calculate the in-degree of each cell, start with cells that have an in-degree of 0, and process cells in topological order to find the longest path.
Consider edge cases like an empty matrix or a matrix with only one cell.
Verify the solution with the provided examples and edge cases.
Modify the code to implement an alternative approach and test it with the same examples.
Implement a function that solves the terrain navigator problem using a different approach than shown above.
If the matrix is empty, the function should return 0.
If the matrix has only one cell, the function should return 1.
If all cells in the matrix have the same value, the function should return 1.
If the matrix has decreasing values in all directions, the function should return 1.
Below is the implementation of the terrain navigator:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147/** * DFS with Memoization * Time Complexity: O(m * n) - We visit each cell exactly once and perform constant work for each cell * Space Complexity: O(m * n) - We use a memo table of size m * n to store the longest path starting from each cell * * @param {number[][]} matrix - The input matrix * @return {number} - The length of the longest increasing path */function longestIncreasingPath(matrix) { if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return 0; } const m = matrix.length; const n = matrix[0].length; const memo = Array(m).fill().map(() => Array(n).fill(0)); const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; // up, down, left, right function dfs(i, j) { // If we've already computed the result for this cell, return it if (memo[i][j] !== 0) { return memo[i][j]; } // Initialize the longest path from this cell as 1 (counting the current cell) let longest = 1; // Explore all four adjacent cells for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; // Check if the adjacent cell is within bounds and has a higher value if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] > matrix[i][j]) { // Update the longest path if a longer path is found longest = Math.max(longest, 1 + dfs(ni, nj)); } } // Memoize the result and return it memo[i][j] = longest; return longest; } // Find the longest path starting from each cell let result = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { result = Math.max(result, dfs(i, j)); } } return result;} /** * Topological Sort (Kahn's Algorithm) * Time Complexity: O(m * n) - We process each cell exactly once and perform constant work for each cell * Space Complexity: O(m * n) - We use additional space for the graph, in-degree array, queue, and distances array * * @param {number[][]} matrix - The input matrix * @return {number} - The length of the longest increasing path */function longestIncreasingPathTopological(matrix) { if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return 0; } const m = matrix.length; const n = matrix[0].length; const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; // up, down, left, right // Calculate in-degree of each cell const inDegree = Array(m).fill().map(() => Array(n).fill(0)); for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] > matrix[i][j]) { inDegree[i][j]++; } } } } // Initialize queue with cells that have in-degree 0 const queue = []; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { if (inDegree[i][j] === 0) { queue.push([i, j]); } } } // Initialize distances array const distances = Array(m).fill().map(() => Array(n).fill(1)); // Process cells in topological order while (queue.length > 0) { const [i, j] = queue.shift(); for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] < matrix[i][j]) { distances[ni][nj] = Math.max(distances[ni][nj], distances[i][j] + 1); inDegree[ni][nj]--; if (inDegree[ni][nj] === 0) { queue.push([ni, nj]); } } } } // Find the maximum distance let result = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { result = Math.max(result, distances[i][j]); } } return result;} // Test casesconst matrix1 = [ [9, 9, 4], [6, 6, 8], [2, 1, 1]];console.log(longestIncreasingPath(matrix1)); // 4 const matrix2 = [ [3, 4, 5], [3, 2, 6], [2, 2, 1]];console.log(longestIncreasingPath(matrix2)); // 4 const matrix3 = [[1]];console.log(longestIncreasingPath(matrix3)); // 1
Let's break down the implementation:
Implement the terrain navigator solution in different programming languages.
Below is the implementation of the terrain navigator in different programming languages. Select a language tab to view the corresponding code.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147/** * DFS with Memoization * Time Complexity: O(m * n) - We visit each cell exactly once and perform constant work for each cell * Space Complexity: O(m * n) - We use a memo table of size m * n to store the longest path starting from each cell * * @param {number[][]} matrix - The input matrix * @return {number} - The length of the longest increasing path */function longestIncreasingPath(matrix) { if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return 0; } const m = matrix.length; const n = matrix[0].length; const memo = Array(m).fill().map(() => Array(n).fill(0)); const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; // up, down, left, right function dfs(i, j) { // If we've already computed the result for this cell, return it if (memo[i][j] !== 0) { return memo[i][j]; } // Initialize the longest path from this cell as 1 (counting the current cell) let longest = 1; // Explore all four adjacent cells for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; // Check if the adjacent cell is within bounds and has a higher value if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] > matrix[i][j]) { // Update the longest path if a longer path is found longest = Math.max(longest, 1 + dfs(ni, nj)); } } // Memoize the result and return it memo[i][j] = longest; return longest; } // Find the longest path starting from each cell let result = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { result = Math.max(result, dfs(i, j)); } } return result;} /** * Topological Sort (Kahn's Algorithm) * Time Complexity: O(m * n) - We process each cell exactly once and perform constant work for each cell * Space Complexity: O(m * n) - We use additional space for the graph, in-degree array, queue, and distances array * * @param {number[][]} matrix - The input matrix * @return {number} - The length of the longest increasing path */function longestIncreasingPathTopological(matrix) { if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return 0; } const m = matrix.length; const n = matrix[0].length; const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; // up, down, left, right // Calculate in-degree of each cell const inDegree = Array(m).fill().map(() => Array(n).fill(0)); for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] > matrix[i][j]) { inDegree[i][j]++; } } } } // Initialize queue with cells that have in-degree 0 const queue = []; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { if (inDegree[i][j] === 0) { queue.push([i, j]); } } } // Initialize distances array const distances = Array(m).fill().map(() => Array(n).fill(1)); // Process cells in topological order while (queue.length > 0) { const [i, j] = queue.shift(); for (const [di, dj] of directions) { const ni = i + di; const nj = j + dj; if (ni >= 0 && ni < m && nj >= 0 && nj < n && matrix[ni][nj] < matrix[i][j]) { distances[ni][nj] = Math.max(distances[ni][nj], distances[i][j] + 1); inDegree[ni][nj]--; if (inDegree[ni][nj] === 0) { queue.push([ni, nj]); } } } } // Find the maximum distance let result = 0; for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { result = Math.max(result, distances[i][j]); } } return result;} // Test casesconst matrix1 = [ [9, 9, 4], [6, 6, 8], [2, 1, 1]];console.log(longestIncreasingPath(matrix1)); // 4 const matrix2 = [ [3, 4, 5], [3, 2, 6], [2, 2, 1]];console.log(longestIncreasingPath(matrix2)); // 4 const matrix3 = [[1]];console.log(longestIncreasingPath(matrix3)); // 1
First, understand that we need to find the length of the longest increasing path in the matrix, where we can move in four directions (up, down, left, right) and can only move to cells with higher values.
Model the problem as finding the longest path in a directed acyclic graph (DAG), where each cell is a node, and there's an edge from cell A to cell B if B is adjacent to A and has a higher value.
We can use either DFS with memoization or topological sort to solve this problem. Both approaches have their advantages.
For each cell, explore all four adjacent cells with higher values and recursively find the longest path starting from those cells. Use memoization to avoid redundant calculations.
Calculate the in-degree of each cell, start with cells that have an in-degree of 0, and process cells in topological order to find the longest path.
Consider edge cases like an empty matrix or a matrix with only one cell.
Verify the solution with the provided examples and edge cases.
Modify the code to implement an alternative approach and test it with the same examples.
Implement a function that solves the terrain navigator problem using a different approach than shown above.
If the matrix is empty, the function should return 0.
If the matrix has only one cell, the function should return 1.
If all cells in the matrix have the same value, the function should return 1.
If the matrix has decreasing values in all directions, the function should return 1.