diff --git a/.gitignore b/.gitignore index 8cd0df3a..d93b707e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode -.idea \ No newline at end of file +.idea +*.exe \ No newline at end of file diff --git a/2D Arrays (Matrix)/2D_sorted.go b/2D Arrays (Matrix)/2D_sorted.go new file mode 100644 index 00000000..fbb74145 --- /dev/null +++ b/2D Arrays (Matrix)/2D_sorted.go @@ -0,0 +1,25 @@ +func searchMatrix(matrix [][]int, target int) bool { + if len(matrix) == 0 || len(matrix[0]) == 0 { + return false + } + + rows, cols := len(matrix), len(matrix[0]) + left, right := 0, rows*cols-1 + + for left <= right { + mid := left + (right-left)/2 + // Convert the 1D index back to 2D coordinates + row, col := mid/cols, mid%cols + midValue := matrix[row][col] + + if midValue == target { + return true + } else if midValue < target { + left = mid + 1 + } else { + right = mid - 1 + } + } + + return false +} diff --git a/2D Arrays (Matrix)/2D_sorted_array.java b/2D Arrays (Matrix)/2D_sorted_array.java new file mode 100644 index 00000000..bffdfeac --- /dev/null +++ b/2D Arrays (Matrix)/2D_sorted_array.java @@ -0,0 +1,53 @@ +/* + +The algorithm starts by initializing two pointers, left and right, which represent the start and end indices of the search range. The range spans from the first element (matrix[0][0]) to the last element (matrix[m - 1][n - 1]) in the matrix, where m is the number of rows and n is the number of columns. + +The function then enters a while loop that continues as long as left is less than or equal to right. In each iteration, it calculates the middle index mid using the formula mid = left + (right - left) / 2. This ensures that mid is always rounded down to the lower integer if the range is odd. + +The middle index mid is then mapped to its corresponding row and column indices in the matrix using the formulas row = mid / n and col = mid % n. + +The number num at the middle position (matrix[row][col]) is retrieved and compared with the target. If num is equal to the target, the function returns true as the target is found in the matrix. If num is less than the target, the left pointer is updated to mid + 1 to search in the upper half of the range. If num is greater than the target, the right pointer is updated to mid - 1 to search in the lower half of the range. + +If the while loop terminates without finding the target, the function returns false. + +The algorithm achieves a time complexity of O(log(m * n)), where m is the number of rows and n is the number of columns in the matrix, as it uses binary search to efficiently search within the given range. + +*/ + + + +#include +class Solution { +public: + /** + * Searches for a target integer in a matrix with specific properties. + * + * @param matrix The input matrix. + * @param target The target integer to search for. + * @return True if the target is found in the matrix, false otherwise. + */ + bool searchMatrix(std::vector>& matrix, int target) { + int m = matrix.size(); + int n = matrix[0].size(); + + int left = 0; // Start index of the search range + int right = m * n - 1; // End index of the search range + + while (left <= right) { + int mid = left + (right - left) / 2; // Calculate middle index + int row = mid / n; // Calculate row index + int col = mid % n; // Calculate column index + int num = matrix[row][col]; // Get the number at the middle position + + if (num == target) { + return true; // Target found in the matrix + } else if (num < target) { + left = mid + 1; // Search in the upper half of the range + } else { + right = mid - 1; // Search in the lower half of the range + } + } + + return false; // Target not found in the matrix + } +}; diff --git a/2D-Arrays/binarySearch.cpp b/2D Arrays (Matrix)/binary_search.cpp similarity index 56% rename from 2D-Arrays/binarySearch.cpp rename to 2D Arrays (Matrix)/binary_search.cpp index a8e29950..ad272b50 100644 --- a/2D-Arrays/binarySearch.cpp +++ b/2D Arrays (Matrix)/binary_search.cpp @@ -1,17 +1,19 @@ -/*You are given an m x n integer matrix matrix with the following two properties: +/* + You are given an m x n integer matrix matrix with the following two properties: -Each row is sorted in non-decreasing order. -The first integer of each row is greater than the last integer of the previous row. -Given an integer target, return true if target is in matrix or false otherwise. + Each row is sorted in non-decreasing order. + The first integer of each row is greater than the last integer of the previous row. + Given an integer target, return true if target is in matrix or false otherwise. -You must write a solution in O(log(m * n)) time complexity. + You must write a solution in O(log(m * n)) time complexity. -1.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 -Output: true + 1.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 + Output: true -2.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 -Output: false*/ + 2.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 + Output: false +*/ class Solution { bool costumBinarySearch(const vector>& matrix, int target, int low, int high){ diff --git a/2D Arrays (Matrix)/count_negatives_in_sorted_matrix.cpp b/2D Arrays (Matrix)/count_negatives_in_sorted_matrix.cpp new file mode 100644 index 00000000..27817bfb --- /dev/null +++ b/2D Arrays (Matrix)/count_negatives_in_sorted_matrix.cpp @@ -0,0 +1,39 @@ +/* + Given a m x n matrix grid which is sorted in non-increasing order both row-wise and column-wise, return the number of negative numbers in grid. + + Example 1: + Input: grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]] + Output: 8 + Explanation: There are 8 negatives number in the matrix. + + Example 2: + Input: grid = [[3,2],[1,0]] + Output: 0 + + Constraints: + m == grid.length + n == grid[i].length + 1 <= m, n <= 100 + -100 <= grid[i][j] <= 100 + + Follow up: Could you find an O(n + m) solution? +*/ + +class Solution { +public: + int countNegatives(vector>& grid) { + int len = grid.size(), m = grid[0].size(); + int ans = 0; + int j = 0, i = len - 1; + while(j < m && i >= 0){ + if(grid[i][j] < 0){ + ans += (m - j); + i--; + } + else{ + j++; + } + } + return ans; + } +}; \ No newline at end of file diff --git a/2D Arrays (Matrix)/matrix_diagonal_sum.cpp b/2D Arrays (Matrix)/matrix_diagonal_sum.cpp new file mode 100644 index 00000000..cdc7948c --- /dev/null +++ b/2D Arrays (Matrix)/matrix_diagonal_sum.cpp @@ -0,0 +1,44 @@ +/* + Given a square matrix mat, return the sum of the matrix diagonals. + + Only include the sum of all the elements on the primary diagonal and all the elements on the secondary diagonal that are not part of the primary diagonal. + + Example 1: + Input: mat = [[1,2,3], + [4,5,6], + [7,8,9]] + Output: 25 + Explanation: Diagonals sum: 1 + 5 + 9 + 3 + 7 = 25 + Notice that element mat[1][1] = 5 is counted only once. + + Example 2: + Input: mat = [[1,1,1,1], + [1,1,1,1], + [1,1,1,1], + [1,1,1,1]] + Output: 8 + + Example 3: + Input: mat = [[5]] + Output: 5 + + Constraints: + n == mat.length == mat[i].length + 1 <= n <= 100 + 1 <= mat[i][j] <= 100 +*/ + +class Solution { +public: + int diagonalSum(vector>& mat) { + int len = mat.size(), sum = 0; + for(int i = 0; i < len; i++){ + sum += mat[i][i] + mat[i][len - 1 - i]; + } + // if length is odd then subtract mid element, because its added twice + if(len & 1){ + sum -= mat[len / 2][len / 2]; + } + return sum; + } +}; \ No newline at end of file diff --git a/Random_Problems/matrix_wave_print.cpp b/2D Arrays (Matrix)/matrix_wave_print.cpp similarity index 95% rename from Random_Problems/matrix_wave_print.cpp rename to 2D Arrays (Matrix)/matrix_wave_print.cpp index a71a4d16..8092e35d 100644 --- a/Random_Problems/matrix_wave_print.cpp +++ b/2D Arrays (Matrix)/matrix_wave_print.cpp @@ -1,33 +1,33 @@ -// Prints a matrix in wave form -#include -using namespace std; -void wave_print(int Mat[][10], int R, int C){ - for(int j = 0; j < C; j++){ - if(j & 1){ - for(int i = R - 1; i >= 0; i--){ - cout << Mat[i][j] << " "; - } - } - else{ - for(int i = 0; i < R; i++){ - cout << Mat[i][j] << " "; - } - } - } -} -int main(){ - int Mat[10][10], R, C; - cin >> R >> C; - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cin >> Mat[i][j]; - } - } - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cout << Mat[i][j] << " "; - } - cout << endl; - } - wave_print(Mat, R, C); +// Prints a matrix in wave form +#include +using namespace std; +void wave_print(int Mat[][10], int R, int C){ + for(int j = 0; j < C; j++){ + if(j & 1){ + for(int i = R - 1; i >= 0; i--){ + cout << Mat[i][j] << " "; + } + } + else{ + for(int i = 0; i < R; i++){ + cout << Mat[i][j] << " "; + } + } + } +} +int main(){ + int Mat[10][10], R, C; + cin >> R >> C; + for(int i = 0; i < R; i++){ + for(int j = 0; j < C; j++){ + cin >> Mat[i][j]; + } + } + for(int i = 0; i < R; i++){ + for(int j = 0; j < C; j++){ + cout << Mat[i][j] << " "; + } + cout << endl; + } + wave_print(Mat, R, C); } \ No newline at end of file diff --git a/2D Arrays (Matrix)/rotate_matrix.cpp b/2D Arrays (Matrix)/rotate_matrix.cpp new file mode 100644 index 00000000..47704d9b --- /dev/null +++ b/2D Arrays (Matrix)/rotate_matrix.cpp @@ -0,0 +1,72 @@ +// Rotate clockwise and anti-clockwise +/* +This implementation first defines two functions, rotateClockwise and rotateCounterclockwise, +to rotate the image by 90 degrees clockwise and counterclockwise, respectively. +Each function takes a 2D vector image as input and modifies it in place. + +The printImage function is used to print the image for demonstration purposes. + +Finally, the main function initializes an example image, prints it, rotates +it by 90 degrees clockwise, prints it again, rotates it by 90 degrees counterclockwise, +and prints it one last time to verify that the rotations worked as expected. +*/ +#include +#include + +using namespace std; + +// Function to rotate the image by 90 degrees clockwise +void rotateClockwise(vector>& image) { + int n = image.size(); + for (int i = 0; i < n / 2; i++) { + for (int j = i; j < n - i - 1; j++) { + int temp = image[i][j]; + image[i][j] = image[n - j - 1][i]; + image[n - j - 1][i] = image[n - i - 1][n - j - 1]; + image[n - i - 1][n - j - 1] = image[j][n - i - 1]; + image[j][n - i - 1] = temp; + } + } +} + +// Function to rotate the image by 90 degrees counterclockwise +void rotateCounterclockwise(vector>& image) { + int n = image.size(); + for (int i = 0; i < n / 2; i++) { + for (int j = i; j < n - i - 1; j++) { + int temp = image[i][j]; + image[i][j] = image[j][n - i - 1]; + image[j][n - i - 1] = image[n - i - 1][n - j - 1]; + image[n - i - 1][n - j - 1] = image[n - j - 1][i]; + image[n - j - 1][i] = temp; + } + } +} + +// Function to print the image +void printImage(vector>& image) { + for (auto row : image) { + for (auto pixel : row) { + cout << pixel << " "; + } + cout << endl; + } +} + +// Driver code +int main() { + vector> image = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + + cout << "Original Image:" << endl; + printImage(image); + + rotateClockwise(image); + cout << "Image rotated by 90 degrees clockwise:" << endl; + printImage(image); + + rotateCounterclockwise(image); + cout << "Image rotated by 90 degrees counterclockwise:" << endl; + printImage(image); + + return 0; +} diff --git a/2D Arrays (Matrix)/rotate_matrix.go b/2D Arrays (Matrix)/rotate_matrix.go new file mode 100644 index 00000000..5acb8558 --- /dev/null +++ b/2D Arrays (Matrix)/rotate_matrix.go @@ -0,0 +1,63 @@ +// Rotate clockwise and anti-clockwise +/* + The rotateClockwise function takes an image array as input and returns the image rotated by 90 degrees clockwise. + It creates a new rotated array with the same dimensions as the original image, and then iterates over + each element of the image, assigning it to a new position in the rotated array. + + The rotateCounterClockwise function works similarly, but it rotates the image counterclockwise instead. + The n-j-1 and n-i-1 indices are used to swap the rows and columns, respectively. + + In the main function, we create an example image, and then call the rotateClockwise and + rotateCounterClockwise functions to rotate the image by 90 degrees in each direction. + Finally, we print the rotated images. +*/ +package main + +import "fmt" + +func rotateClockwise(image [][]int) [][]int { + n := len(image) + rotated := make([][]int, n) + for i := 0; i < n; i++ { + rotated[i] = make([]int, n) + for j := 0; j < n; j++ { + rotated[i][j] = image[n-j-1][i] + } + } + return rotated +} + +func rotateCounterClockwise(image [][]int) [][]int { + n := len(image) + rotated := make([][]int, n) + for i := 0; i < n; i++ { + rotated[i] = make([]int, n) + for j := 0; j < n; j++ { + rotated[i][j] = image[j][n-i-1] + } + } + return rotated +} + +func main() { + // example image + image := [][]int{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + // rotate clockwise + rotatedClockwise := rotateClockwise(image) + fmt.Println("Rotated Clockwise:") + for _, row := range rotatedClockwise { + fmt.Println(row) + } + + // rotate counterclockwise + rotatedCounterClockwise := rotateCounterClockwise(image) + fmt.Println("Rotated Counterclockwise:") + for _, row := range rotatedCounterClockwise { + fmt.Println(row) + } +} diff --git a/2D Arrays (Matrix)/rotate_matrix.java b/2D Arrays (Matrix)/rotate_matrix.java new file mode 100644 index 00000000..89676238 --- /dev/null +++ b/2D Arrays (Matrix)/rotate_matrix.java @@ -0,0 +1,70 @@ +// Rotate clockwise and anti-clockwise +/* + This program takes a 2D array (matrix) and performs two types of 90-degree rotations: clockwise and + anti-clockwise. The rotateClockwise method takes a matrix and returns a new matrix with its + elements rotated 90 degrees clockwise. The rotateAntiClockwise method takes a matrix and + returns a new matrix with its elements rotated 90 degrees anti-clockwise. + + The printMatrix method is used to print the matrix elements in a readable format. + Finally, the main method initializes a test matrix and performs the two rotations on it. +*/ +public class RotateMatrix { + + // Rotate matrix by 90 degrees clockwise + public static int[][] rotateClockwise(int[][] matrix) { + int n = matrix.length; + int[][] result = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + result[i][j] = matrix[n - j - 1][i]; + } + } + + return result; + } + + // Rotate matrix by 90 degrees anti-clockwise + public static int[][] rotateAntiClockwise(int[][] matrix) { + int n = matrix.length; + int[][] result = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + result[i][j] = matrix[j][n - i - 1]; + } + } + + return result; + } + + // Print matrix + public static void printMatrix(int[][] matrix) { + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[i].length; j++) { + System.out.print(matrix[i][j] + " "); + } + System.out.println(); + } + } + + // Test program + public static void main(String[] args) { + int[][] matrix = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + System.out.println("Original Matrix:"); + printMatrix(matrix); + + int[][] rotatedClockwise = rotateClockwise(matrix); + System.out.println("Matrix after 90 degree clockwise rotation:"); + printMatrix(rotatedClockwise); + + int[][] rotatedAntiClockwise = rotateAntiClockwise(matrix); + System.out.println("Matrix after 90 degree anti-clockwise rotation:"); + printMatrix(rotatedAntiClockwise); + } +} diff --git a/2D Arrays (Matrix)/rotate_matrix.js b/2D Arrays (Matrix)/rotate_matrix.js new file mode 100644 index 00000000..7994d733 --- /dev/null +++ b/2D Arrays (Matrix)/rotate_matrix.js @@ -0,0 +1,57 @@ +// Rotate clockwise and anti-clockwise +/* + Here, we first get the number of rows and columns in the matrix. Then, for rotating the matrix clockwise, + we iterate over the columns in reverse order and create a new row in the rotated matrix by iterating + over each row in the original matrix and adding the corresponding element to the new row. + Finally, we add the new row to the rotated matrix. + + For rotating the matrix anti-clockwise, we again iterate over the columns but this time in the + forward order and create a new row in the rotated matrix by iterating over each row in the original + matrix in reverse order and adding the corresponding element to the new row. Finally, we add the + new row to the rotated matrix. +*/ +function rotateClockwise(matrix) { + // Get the number of rows and columns in the matrix + const rows = matrix.length; + const cols = matrix[0].length; + + // Create a new matrix to store the rotated matrix + const rotated = []; + + // Iterate over the columns in reverse order and create a new row in the rotated matrix + for (let j = cols - 1; j >= 0; j--) { + const newRow = []; + // Iterate over each row in the matrix and add the corresponding element to the new row + for (let i = 0; i < rows; i++) { + newRow.push(matrix[i][j]); + } + // Add the new row to the rotated matrix + rotated.push(newRow); + } + + // Return the rotated matrix + return rotated; +} + +function rotateAntiClockwise(matrix) { + // Get the number of rows and columns in the matrix + const rows = matrix.length; + const cols = matrix[0].length; + + // Create a new matrix to store the rotated matrix + const rotated = []; + + // Iterate over the columns in reverse order and create a new row in the rotated matrix + for (let j = 0; j < cols; j++) { + const newRow = []; + // Iterate over each row in the matrix in reverse order and add the corresponding element to the new row + for (let i = rows - 1; i >= 0; i--) { + newRow.push(matrix[i][j]); + } + // Add the new row to the rotated matrix + rotated.push(newRow); + } + + // Return the rotated matrix + return rotated; +} diff --git a/2D Arrays (Matrix)/rotate_matrix.py b/2D Arrays (Matrix)/rotate_matrix.py new file mode 100644 index 00000000..0c8b07fa --- /dev/null +++ b/2D Arrays (Matrix)/rotate_matrix.py @@ -0,0 +1,46 @@ +# Rotate clockwise and anti-clockwise +''' + The rotate_clockwise() function takes a matrix as input and returns the matrix rotated by 90 degrees + clockwise. It does this by first transposing the matrix (swapping the elements across the diagonal), + and then reversing each row of the transposed matrix. + + The rotate_counterclockwise() function takes a matrix as input and returns the matrix rotated by 90 + degrees counterclockwise. It also transposes the matrix first, and then reverses each column of + the transposed matrix. + + Both functions use two nested loops to iterate through the matrix and perform the required operations. + The n variable represents the size of the matrix, and is used to control the range of the loops. +''' +def rotate_clockwise(matrix): + """ + Function to rotate the given matrix by 90 degrees clockwise + """ + n = len(matrix) + # Transpose the matrix + for i in range(n): + for j in range(i, n): + matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] + + # Reverse each row to get the final rotated matrix + for i in range(n): + matrix[i] = matrix[i][::-1] + + return matrix + + +def rotate_counterclockwise(matrix): + """ + Function to rotate the given matrix by 90 degrees counterclockwise + """ + n = len(matrix) + # Transpose the matrix + for i in range(n): + for j in range(i, n): + matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] + + # Reverse each column to get the final rotated matrix + for i in range(n//2): + for j in range(n): + matrix[j][i], matrix[j][n-i-1] = matrix[j][n-i-1], matrix[j][i] + + return matrix diff --git a/2D Arrays (Matrix)/search_element.cpp b/2D Arrays (Matrix)/search_element.cpp new file mode 100644 index 00000000..7e608a70 --- /dev/null +++ b/2D Arrays (Matrix)/search_element.cpp @@ -0,0 +1,35 @@ +/* + You are given an m x n integer matrix matrix with the following two properties: + + Each row is sorted in non-decreasing order. + The first integer of each row is greater than the last integer of the previous row. + Given an integer target, return true if target is in matrix or false otherwise. + + You must write a solution in O(log(m * n)) time complexity. + + + 1.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 + Output: true + + 2.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 + Output: false +*/ + +class Solution { +public: + bool searchMatrix(vector> &matrix,int target) { + int i = 0; + int j = matrix[0].size() - 1; + + while(i < matrix.size() && j >= 0) + { + if(target == matrix[i][j]) + return true; + else if(target < matrix[i][j]) + j--; + else + i++; + } + return false; + } +}; diff --git a/2D Arrays (Matrix)/searching_in_sorted_array.cpp b/2D Arrays (Matrix)/searching_in_sorted_array.cpp new file mode 100644 index 00000000..c2c3d350 --- /dev/null +++ b/2D Arrays (Matrix)/searching_in_sorted_array.cpp @@ -0,0 +1,92 @@ +/* Name : Rajeev Kumar +Github username : Tonystart121 +Repository name : data-structures-and-algorithms +Problem : Searching in 2D sorted array in C++ +Issue Number : #273 +Problem statement : Given a sorted matrix mat[n][m] and an element ‘x’. Find the position of x in the matrix if it is present, else print -1. + +Sample testcases: + +Testcase 1 --> + +Input: number of rows(n) and column(m).Let n=3,m=3 and value to search x = 20 . +arr[n][m] = { {1, 5, 9}, + {14, 20, 21}, + {30, 34, 43} } + +Output: found at (1,2); + +Testcase 2 --> +Input: number of rows(n) and column(m).Let n=3,m=4 and value to search x = 43 +arr[n][m] = { {1, 5, 9, 11}, + {14, 20, 21, 26}, + {30, 34, 43, 50} } +Output: Found at (2,3); + +Time Complexity = O(n+m) +Space Complexity = O(n+m) + + +Explanation: +This code asks the user to enter the number of rows and column in the array and element to find in the array, and then prompts them to enter each element of the array one at a time. Once the array is complete, the code applies the linear search/mapping algorithm to find the element within the array, and then prints the position of that element to the console. + +Start at the top left corner of the matrix. +Compare the target element to the element at the current position. +If the target element is equal to the element at the current position, then return the current position. +If the target element is less than the element at the current position, then move down one row. +If the target element is greater than the element at the current position, then move right one column. +Repeat steps 2-5 until the target element is found or the entire matrix has been searched. +*/ + +// ----------------------------------------------------------------------------- code begins now! + + +#include +using namespace std; + +int main(){ + + // enter array rows and column + int n,m; + cin>>n>>m; + + // taking input array. + int arr[n][m]; + for(int i=0;i>arr[i][j]; + } + } + + // taking input value to search. + int key; + cin>>key; + + // initializing rightmost element as current element. + int cr_row = 0, cr_col=m-1; + bool ans = false; + while(cr_row=0){ + + // if key==curr output its position + if(arr[cr_row][cr_col]==key){ + cout<arr[cr_row][cr_col]){ + cr_row++; + } + + // if key less than array elements, col decreasing. + else if(key>In this code, we have the Solution class with the searchMatrix method that takes a 2D matrix and a target value as parameters. +>>It iterates through each element in the matrix and checks if the current element is equal to the target value. +>>If a match is found, it returns true. If no match is found after checking all elements, it returns false. +>>In the main function, we create an instance of the Solution class and define a sample matrix and target value. +>>We then call the searchMatrix method with the provided matrix and target, and store the result in the found variable. +>>Finally, we print whether the target was found or not. +>>In this example, the output will be "Target found: true" since the target value 5 exists in the matrix. +*/ + +class Solution +{ +public boolean searchMatrix(int[][] matrix, int target) +{ + int m = matrix.length; + int i = 0; + for (i = 0; i < m; i++) + { + for (int j = 0; j < matrix[i].length; j++) + { + if (matrix[i][j] == target) + return true; + } + } +return false; +} + +public static void main(String[] args) +{ + Solution solution = new Solution(); + int[][] matrix = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + int target = 5; + + boolean found = solution.searchMatrix(matrix, target); + System.out.println("Target found: " + found); +} +} + diff --git a/2D Arrays (Matrix)/searching_in_sorted_array.js b/2D Arrays (Matrix)/searching_in_sorted_array.js new file mode 100644 index 00000000..161212be --- /dev/null +++ b/2D Arrays (Matrix)/searching_in_sorted_array.js @@ -0,0 +1,67 @@ +/*Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Search in 2D sorted array in Javascript +Issue Number : #272 +Problem statement : + +Explanation of the below Javascript code : + +The function searchMatrix takes in a matrix (matrix) and a target integer (target) as parameters. It returns true if the target is found in the matrix and false otherwise. + +The function first checks for edge cases where the matrix is empty or the rows are empty, and immediately returns false in such cases. + +It then initializes variables for the number of rows (rows) and columns (cols) in the matrix, and sets the left and right indices for the binary search. + +The binary search is performed using a while loop, with the left and right indices as the condition. Inside the loop, the middle index (mid) is calculated using Math.floor((left + right) / 2), and the corresponding row and column indices are derived from the mid index. + +The element at the mid index is compared with the target, and based on the comparison, the search space is narrowed down by updating the left and right indices accordingly. + +If the target is found, the function returns true. If the loop completes without finding the target, the function returns false. + +The time complexity of this JavaScript solution is O(log(m * n)), as it performs a binary search on a list of size m * n. + + +*/ + +-------------------------------------------------------------------------//Javascript code begins here----------------------------------------------------------------------- + + +/** + * Searches for a target integer in a matrix. + * @param {number[][]} matrix - The matrix to search in. + * @param {number} target - The target integer to search for. + * @return {boolean} - True if the target is found, false otherwise. + */ +function searchMatrix(matrix, target) { + // Check for empty matrix or empty rows + if (!matrix || matrix.length === 0 || matrix[0].length === 0) { + return false; + } + + const rows = matrix.length; + const cols = matrix[0].length; + let left = 0; + let right = rows * cols - 1; + + // Perform binary search on the matrix + while (left <= right) { + const mid = Math.floor((left + right) / 2); + const row = Math.floor(mid / cols); + const col = mid % cols; + + if (matrix[row][col] === target) { + // Target found + return true; + } else if (matrix[row][col] < target) { + // Target is in the right half of the matrix + left = mid + 1; + } else { + // Target is in the left half of the matrix + right = mid - 1; + } + } + + // Target not found + return false; +} diff --git a/2D Arrays (Matrix)/searching_in_sorted_array.py b/2D Arrays (Matrix)/searching_in_sorted_array.py new file mode 100644 index 00000000..f5117b30 --- /dev/null +++ b/2D Arrays (Matrix)/searching_in_sorted_array.py @@ -0,0 +1,77 @@ +''' +Date:28/6/23 +About:Search in 2D sorted array in Python + +Input: +You are given an m x n integer matrix matrix with the following two properties: +Each row is sorted in non-decreasing order. +The first integer of each row is greater than the last integer of the previous row. +Given an integer target, return true if target is in matrix or false otherwise. + +Time Complexity: +You must write a solution in O(log(m * n)) time complexity. +Space Complexity:O(1) + +//Example 1: +Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 +Output: true +//Example 2: +Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 +Output: false + +//Explanation +The method takes two parameters: matrix, which represents the sorted matrix, and target, which is the value we want to find in the matrix. + +The code initializes variables nRows and nCols to store the number of rows and columns in the matrix, respectively. + +The starting position for the search is set to the bottom-left corner of the matrix (row = nRows - 1, col = 0). + +The code enters a while loop that continues as long as the current row index (row) is within the bounds of the matrix (0 to nRows - 1 +and the current column index (col) is within the bounds of the matrix (0 to nCols - 1). + +Inside the loop, the code retrieves the value at the current position in the matrix (val = matrix[row][col]). + +If the current value (val) == value, the method returns True, indicating that the target is found in the matrix. + +If the current value (val) < target value, it means the target can only be found in the rows above the current row. +Therefore, the column index (col) is incremented to move to the next column. + +If the current value (val) >target value, it means the target can only be found in the columns to the left of the current column. +Therefore, the row index (row) is decremented to move to the previous row. + +If the loop completes without finding the target value, the method returns False. + +''' +class Solution(object): + def searchMatrix(self, matrix, target): + nRows = len(matrix) + nCols = len(matrix[0]) + row = nRows - 1 + col = 0 + while 0 <= row < nRows and 0 <= col < nCols: + val = matrix[row][col] + if val == target: + return True + elif val < target: + col += 1 + else: + row -= 1 + return False + + +# Create an instance of the Solution class +solution = Solution() + +# Define the matrix and target value +matrix = [[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60]] +target = 3 + +# Call the searchMatrix method and print the result +result = solution.searchMatrix(matrix, target) +print(result) + + + + + + diff --git a/2D Arrays (Matrix)/set_matrix_0.cpp b/2D Arrays (Matrix)/set_matrix_0.cpp new file mode 100644 index 00000000..edb6f992 --- /dev/null +++ b/2D Arrays (Matrix)/set_matrix_0.cpp @@ -0,0 +1,89 @@ +/* +Problem: +You are given a 2D matrix, and the task is to modify it such that if an element in the matrix is zero, you need to set all the elements in the +corresponding row and column to zero as well. + +Solution: +To solve this problem, you can follow the steps below: + +1. Create two sets, rows and columns, to keep track of the rows and columns that need to be set to zero. + +2. Iterate through the matrix row by row, and for each element, if it is zero, add its row index to the rows set and its column index to the columns set. + +3. Iterate through the matrix again, and for each element, check if its row index or column index exists in the respective sets (rows or columns). +If either index exists, set the element to zero. + +4. Finally, iterate through the matrix one more time, and for each row or column index in the rows or columns sets, set all the elements in that +row or column to zero. + +5. Return the modified matrix. + +Complexity: +Let's assume the matrix has dimensions MxN. + +Step 2: In this step, we iterate through the matrix once, which takes O(M*N) time. + +Step 3: In this step, we again iterate through the matrix once, which takes O(M*N) time. + +Step 4: In this step, we iterate through the rows and columns sets, which contain at most M+N elements. Therefore, this step takes O(M+N) time. + +Overall, the time complexity of the solution is O(MN + M + N), which can be simplified to O(MN). The space complexity is O(M+N) since we are +using two sets to store the rows and columns that need to be set to zero. +*/ + +#include +#include +#include + +using namespace std; + +void setZeroes(vector>& matrix) { + unordered_set rows; + unordered_set columns; + + // Step 1: Find the rows and columns that need to be set to zero + for (int i = 0; i < matrix.size(); i++) { + for (int j = 0; j < matrix[0].size(); j++) { + if (matrix[i][j] == 0) { + rows.insert(i); + columns.insert(j); + } + } + } + + // Step 2: Set the corresponding rows and columns to zero + for (int i = 0; i < matrix.size(); i++) { + for (int j = 0; j < matrix[0].size(); j++) { + if (rows.count(i) || columns.count(j)) { + matrix[i][j] = 0; + } + } + } +} + +void printMatrix(const vector>& matrix) { + for (const auto& row : matrix) { + for (int num : row) { + cout << num << " "; + } + cout << endl; + } +} + +int main() { + vector> matrix = { + {1, 1, 1}, + {1, 0, 1}, + {1, 1, 1} + }; + + cout << "Original Matrix:" << endl; + printMatrix(matrix); + + setZeroes(matrix); + + cout << "Modified Matrix:" << endl; + printMatrix(matrix); + + return 0; +} diff --git a/2D Arrays (Matrix)/set_matrix_0.go b/2D Arrays (Matrix)/set_matrix_0.go new file mode 100644 index 00000000..a7f82c4f --- /dev/null +++ b/2D Arrays (Matrix)/set_matrix_0.go @@ -0,0 +1,87 @@ +/* +Problem: +You are given a 2D matrix, and the task is to modify it such that if an element in the matrix is zero, you need to set all the elements in the +corresponding row and column to zero as well. + +Solution: +To solve this problem, you can follow the steps below: + +1. Create two sets, rows and columns, to keep track of the rows and columns that need to be set to zero. + +2. Iterate through the matrix row by row, and for each element, if it is zero, add its row index to the rows set and its column index to the columns set. + +3. Iterate through the matrix again, and for each element, check if its row index or column index exists in the respective sets (rows or columns). +If either index exists, set the element to zero. + +4. Finally, iterate through the matrix one more time, and for each row or column index in the rows or columns sets, set all the elements in that +row or column to zero. + +5. Return the modified matrix. + +Complexity: +Let's assume the matrix has dimensions MxN. + +Step 2: In this step, we iterate through the matrix once, which takes O(M*N) time. + +Step 3: In this step, we again iterate through the matrix once, which takes O(M*N) time. + +Step 4: In this step, we iterate through the rows and columns sets, which contain at most M+N elements. Therefore, this step takes O(M+N) time. + +Overall, the time complexity of the solution is O(MN + M + N), which can be simplified to O(MN). The space complexity is O(M+N) since we are +using two sets to store the rows and columns that need to be set to zero. +*/ + +package main + +import ( + "fmt" +) + +func setZeroes(matrix [][]int) { + rows := make(map[int]bool) + columns := make(map[int]bool) + + // Step 1: Find the rows and columns that need to be set to zero + for i := 0; i < len(matrix); i++ { + for j := 0; j < len(matrix[0]); j++ { + if matrix[i][j] == 0 { + rows[i] = true + columns[j] = true + } + } + } + + // Step 2: Set the corresponding rows and columns to zero + for i := 0; i < len(matrix); i++ { + for j := 0; j < len(matrix[0]); j++ { + if rows[i] || columns[j] { + matrix[i][j] = 0 + } + } + } +} + +func main() { + matrix := [][]int{ + {1, 1, 1}, + {1, 0, 1}, + {1, 1, 1}, + } + + fmt.Println("Original Matrix:") + printMatrix(matrix) + + setZeroes(matrix) + + fmt.Println("Modified Matrix:") + printMatrix(matrix) +} + +func printMatrix(matrix [][]int) { + for i := 0; i < len(matrix); i++ { + for j := 0; j < len(matrix[0]); j++ { + fmt.Print(matrix[i][j], " ") + } + fmt.Println() + } +} diff --git a/2D Arrays (Matrix)/set_matrix_0.java b/2D Arrays (Matrix)/set_matrix_0.java new file mode 100644 index 00000000..3fb28f41 --- /dev/null +++ b/2D Arrays (Matrix)/set_matrix_0.java @@ -0,0 +1,158 @@ +/* SET MATRIX ZERO - JAVA LANGUAGE + Problem Link1 : https://leetcode.com/problems/set-matrix-zeroes/ + Problem Link2 : https://www.codingninjas.com/codestudio/problems/zero-matrix_1171153 + +Problem: +You are given a 2D matrix, and the task is to modify it such that if an element in the matrix is zero, you need to set all the elements in the +corresponding row and column to zero as well. + +E.g- Input: | 1 1 1 | + | 1 0 1 | + | 1 1 1 | + + Output: | 1 0 1 | + | 0 0 0 | + | 1 0 1 | + +METHOD 1] Brute Force Approach for Set Matrix Zeroes +Step 1. Create an array answer of size (n X m) and initialize every element as 1. +Step 2. Traverse the matrix array row-wise and set the current row as 0 in answer array if the current row contains an element equals to 0. +Step 3. Traverse the matrix array column-wise and set the current column as 0 in answer array if the current column contains an element equals to 0. +Step 4. Now traverse the answer array, if the current element is 0, then set this element as 0 in a matrix array. +Step 5. Return matrix array + +Complexity Analysis: +Time Complexity = O(n * m) +Space Complexity = O(n * m) +where n is the number of rows in the matrix and m is the number of columns in the matrix. + +METHOD 2] Optimal Approach for Set Matrix Zeroes +If we assume that -1, do not occur in the matrix array, then + +Step 1. Traverse the matrix array row-wise and set all the elements of current row which are not 0 as -1, if the current row contains an element equals to 0. +Step 2. Traverse the matrix array column-wise and set all the elements of the current column which are not 0 as -1, if the current column contains an element equals to 0. +Step 3. Again traverse the matrix and set all the elements that are -1 to 0. +Step 4. Return matrix array. + +Complexity Analysis: +Time Complexity = O(n * m) +Space Complexity = O(1) +where n is the number of rows in the matrix and m is the number of columns in the matrix. +*/ + + +public class setMatrixZero { + + // Method 1] Brute Force Approach + private static void setZeroes_bruteForce(int[][] matrix, int n, int m) { + int answer[][] = new int[n][m]; // Step 1 + // Set all elements of answer array as 1 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + answer[i][j] = 1; // making each element as 1 + } + } + // Traverse row wise --> Step 2 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (matrix[i][j] == 0) { + // Set this row as zero in answer array + for (int k = 0; k < m; k++) { + answer[i][k] = 0; + } + break; + } + } + } + // Traverse column wise --> Step 3 + for (int j = 0; j < m; j++) { + for (int i = 0; i < n; i++) { + if (matrix[i][j] == 0) { + // Set this column as 0 in answer array + for (int k = 0; k < n; k++) { + answer[k][j] = 0; + } + } + } + } + // Update the elements in matrix array --> Step 4 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (answer[i][j] == 0) { + matrix[i][j] = 0; + } + } + } + } + + // Method 2] Optimal Approach + private static void setZeroes_optimalMethod(int[][] matrix, int n, int m) { + // Traverse row wise + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (matrix[i][j] == 0) { + // Set all the elements that are not zero as -1 + for (int k = 0; k < m; k++) { + if (matrix[i][k] != 0) { + matrix[i][k] = -1; + } + } + } + } + } + // Traverse column wise + for (int j = 0; j < m; j++) { + for (int i = 0; i < n; i++) { + if (matrix[i][j] == 0) { + // Set all the elements that are not zero as -1 + for (int k = 0; k < n; k++) { + if (matrix[k][j] != 0) { + matrix[k][j] = -1; + } + } + } + } + } + + // Update all -1 as 0 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (matrix[i][j] == -1) { + matrix[i][j] = 0; + } + } + } + } + + public static void main(String[] args) { + // Example using Method 1 - Brute Force + int[][] matrix1 = new int[][] {{1, 1, 1}, {1, 0, 1}, {1, 1, 1}}; // Defining Matrix + int n = matrix1.length; + int m = matrix1[0].length; + + setZeroes_bruteForce(matrix1, n, m); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + System.out.print(matrix1[i][j] + " "); // Printing Matrix + } + System.out.println(); + } + + System.out.println("-----------------"); + + // Example using Method 2 - Optimal + int[][] matrix2 = new int[][] {{0, 0, 6, 0}, {1, 4, 9, 0}, {1, 8, 1, 8}}; // Defining Matrix + n = matrix2.length; + m = matrix2[0].length; + + setZeroes_optimalMethod(matrix2, n, m); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + System.out.print(matrix2[i][j] + " "); // Printing Matrix + } + System.out.println(); + } + } +} \ No newline at end of file diff --git a/2D Arrays (Matrix)/spiral_traverse.cpp b/2D Arrays (Matrix)/spiral_traverse.cpp new file mode 100644 index 00000000..da8b879f --- /dev/null +++ b/2D Arrays (Matrix)/spiral_traverse.cpp @@ -0,0 +1,90 @@ +/* + Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and + returns a one-dimensional array of all the array's elements in spiral order. + + Spiral order starts at the top left corner of the two-dimensional array, goes to the right, and proceeds + in a spiral pattern all the way until every element has been visited. + + Explanation: + + The SpiralTraverse function takes a 2D integer array array and returns a 1D integer slice that contains the + elements of array traversed in a spiral order, starting from the top-left corner and moving clockwise. + + The function first initializes an empty slice result to hold the elements of the spiral traversal. + If the input array is empty, the function immediately returns the empty result. + + Next, the function initializes variables startRow, endRow, startCol, and endCol to keep track of the + boundaries of the matrix. These variables will be updated as the function traverses the matrix. + + The function then enters a loop that traverses the matrix in a spiral order. The loop continues + as long as startRow <= endRow and startCol <= endCol, which means that there are still elements + in the matrix to be traversed. + + The first step in the loop is to traverse the top row of the matrix from left to right, and append + each element to the result slice. The next step is to traverse the rightmost column of the matrix from top to bottom, + and append each element to the result slice. If there is more than one row in the matrix, the function then traverses + the bottom row of the matrix from right to left, and appends each element to the result slice. If there is only one row left, + the loop is broken to avoid duplicating the elements. Finally, if there is more than one column in the matrix, + the function traverses the left + + O(n) time | O(n) space - where n is the total number of elements in the array +*/ +#include +using namespace std; + +#include +using namespace std; + +vector SpiralTraverse(vector> array) { + vector result; // vector to store the spiral traversal + int rows = array.size(); // number of rows in the input array + int cols = array[0].size(); // number of columns in the input array + int startRow = 0, endRow = rows - 1; // indices for the start and end row of the current subarray + int startCol = 0, endCol = cols - 1; // indices for the start and end column of the current subarray + + // loop until the entire input array is traversed + while (startRow <= endRow && startCol <= endCol) { + // traverse the top row from left to right + for (int col = startCol; col <= endCol; col++) { + result.push_back(array[startRow][col]); + } + // traverse the right column from top to bottom + for (int row = startRow + 1; row <= endRow; row++) { + result.push_back(array[row][endCol]); + } + // traverse the bottom row from right to left + for (int col = endCol - 1; col >= startCol; col--) { + // check if there is only one row in the subarray + if (startRow == endRow) { + break; + } + result.push_back(array[endRow][col]); + } + // traverse the left column from bottom to top + for (int row = endRow - 1; row > startRow; row--) { + // check if there is only one column in the subarray + if (startCol == endCol) { + break; + } + result.push_back(array[row][startCol]); + } + // update the indices for the next subarray to be traversed + startRow++; + endRow--; + startCol++; + endCol--; + } + + return result; +} + +int main() { + vector> array = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + vector result = SpiralTraverse(array); + cout << "Spiral traversal: "; + for (int i = 0; i < result.size(); i++) { + cout << result[i] << " "; + } + cout << endl; + return 0; +} \ No newline at end of file diff --git a/2D Arrays (Matrix)/spiral_traverse.go b/2D Arrays (Matrix)/spiral_traverse.go new file mode 100644 index 00000000..8287b6cd --- /dev/null +++ b/2D Arrays (Matrix)/spiral_traverse.go @@ -0,0 +1,96 @@ +/* + Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and + returns a one-dimensional array of all the array's elements in spiral order. + + Spiral order starts at the top left corner of the two-dimensional array, goes to the right, and proceeds + in a spiral pattern all the way until every element has been visited. + + Explanation: + + The SpiralTraverse function takes a 2D integer array array and returns a 1D integer slice that contains the + elements of array traversed in a spiral order, starting from the top-left corner and moving clockwise. + + The function first initializes an empty slice result to hold the elements of the spiral traversal. + If the input array is empty, the function immediately returns the empty result. + + Next, the function initializes variables startRow, endRow, startCol, and endCol to keep track of the + boundaries of the matrix. These variables will be updated as the function traverses the matrix. + + The function then enters a loop that traverses the matrix in a spiral order. The loop continues + as long as startRow <= endRow and startCol <= endCol, which means that there are still elements + in the matrix to be traversed. + + The first step in the loop is to traverse the top row of the matrix from left to right, and append + each element to the result slice. The next step is to traverse the rightmost column of the matrix from top to bottom, + and append each element to the result slice. If there is more than one row in the matrix, the function then traverses + the bottom row of the matrix from right to left, and appends each element to the result slice. If there is only one row left, + the loop is broken to avoid duplicating the elements. Finally, if there is more than one column in the matrix, + the function traverses the left + + O(n) time | O(n) space - where n is the total number of elements in the array +*/ +package main + +import "fmt" + +func SpiralTraverse(array [][]int) []int { + // Initialize an empty slice to hold the result + result := []int{} + // If the input array is empty, return the empty result + if len(array) == 0 { + return result + } + // Initialize variables to keep track of the boundaries of the matrix + startRow, endRow := 0, len(array)-1 + startCol, endCol := 0, len(array[0])-1 + + // Traverse the matrix in a spiral order + for startRow <= endRow && startCol <= endCol { + // Traverse the top row from left to right + for col := startCol; col <= endCol; col++ { + result = append(result, array[startRow][col]) + } + // Traverse the rightmost column from top to bottom + for row := startRow + 1; row <= endRow; row++ { + result = append(result, array[row][endCol]) + } + // Traverse the bottom row from right to left, if there is more than one row + for col := endCol - 1; col >= startCol; col-- { + // If there is only one row left, break the loop to avoid duplicating the elements + if startRow == endRow { + break + } + result = append(result, array[endRow][col]) + } + // Traverse the leftmost column from bottom to top, if there is more than one column + for row := endRow - 1; row > startRow; row-- { + // If there is only one column left, break the loop to avoid duplicating the elements + if startCol == endCol { + break + } + result = append(result, array[row][startCol]) + } + // Update the boundaries of the matrix + startRow++ + endRow-- + startCol++ + endCol-- + } + // Return the result slice + return result +} + +func main() { + // Example 2D array + array := [][]int{ + {1, 2, 3, 4}, + {10, 11, 12, 5}, + {9, 8, 7, 6}, + } + + // Call SpiralTraverse function on array + result := SpiralTraverse(array) + + // Print the result to console + fmt.Println(result) +} \ No newline at end of file diff --git a/Arrays/spiral_traverse.java b/2D Arrays (Matrix)/spiral_traverse.java similarity index 67% rename from Arrays/spiral_traverse.java rename to 2D Arrays (Matrix)/spiral_traverse.java index 29cb5506..d9d88cb5 100644 --- a/Arrays/spiral_traverse.java +++ b/2D Arrays (Matrix)/spiral_traverse.java @@ -1,26 +1,35 @@ -import java.util.*; +/* + Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and + returns a one-dimensional array of all the array's elements in spiral order. -/** - * + Spiral order starts at the top left corner of the two-dimensional array, goes to the right, and proceeds + in a spiral pattern all the way until every element has been visited. - Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in spiral order. + Explanation: - Spiral order starts at the top left corner of the two-dimensional array, goes to the right, and proceeds in a spiral pattern all the way until every element has been visited. - Sample Input + The SpiralTraverse function takes a 2D integer array array and returns a 1D integer slice that contains the + elements of array traversed in a spiral order, starting from the top-left corner and moving clockwise. - array = [ - [1, 2, 3, 4], - [12, 13, 14, 5], - [11, 16, 15, 6], - [10, 9, 8, 7], - ] + The function first initializes an empty slice result to hold the elements of the spiral traversal. + If the input array is empty, the function immediately returns the empty result. - Sample Output + Next, the function initializes variables startRow, endRow, startCol, and endCol to keep track of the + boundaries of the matrix. These variables will be updated as the function traverses the matrix. - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + The function then enters a loop that traverses the matrix in a spiral order. The loop continues + as long as startRow <= endRow and startCol <= endCol, which means that there are still elements + in the matrix to be traversed. + The first step in the loop is to traverse the top row of the matrix from left to right, and append + each element to the result slice. The next step is to traverse the rightmost column of the matrix from top to bottom, + and append each element to the result slice. If there is more than one row in the matrix, the function then traverses + the bottom row of the matrix from right to left, and appends each element to the result slice. If there is only one row left, + the loop is broken to avoid duplicating the elements. Finally, if there is more than one column in the matrix, + the function traverses the left - */ + O(n) time | O(n) space - where n is the total number of elements in the array +*/ +import java.util.*; public class SpiralTraverse { public static void main(String[] args) { diff --git a/2D Arrays (Matrix)/spiral_traverse.py b/2D Arrays (Matrix)/spiral_traverse.py new file mode 100644 index 00000000..29b02024 --- /dev/null +++ b/2D Arrays (Matrix)/spiral_traverse.py @@ -0,0 +1,90 @@ +##### LEETCODE 54. Spiral Matrix #### + +##### INPUT OUTPUT ##### + +# Given an m x n matrix, return all elements of the matrix in spiral order. +# Input: matrix = [[1,2,3],[4,5,6],[7,8,9]] +# Output: [1,2,3,6,9,8,7,4,5] +# Input: matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] +# Output: [1,2,3,4,8,12,11,10,9,5,6,7] +# Write python code along with explanation and comments use meaningful variables + +##### EXPLAINATION ##### + +# The given problem statement is to return all elements of the matrix in spiral order. We can start by defining the four boundaries of the matrix - top, bottom, left and right. We can then traverse the matrix in a spiral order by following these four steps: + +# Traverse from left to right along the top boundary +# Traverse from top to bottom along the right boundary +# Traverse from right to left along the bottom boundary +# Traverse from bottom to top along the left boundary +# After each traversal, we need to update the corresponding boundary and change the direction of traversal. + +# We can implement this algorithm using a while loop that runs as long as the top boundary is less than or equal to the bottom boundary and the left boundary is less than or equal to the right boundary. Within the while loop, we can use an if-else ladder to check the current direction of traversal and perform the corresponding traversal along the boundary. + +# Finally, we can return the result list containing all the spiral order elements of the matrix. + + +#### DRY RUN: #### + +# Initially, we have the matrix: +# 1 2 3 +# 4 5 6 +# 7 8 9 + +# We initialize the variables top, bottom, left, and right to 0, 2, 0, and 2 respectively. We also set the direction variable to 0. +# Now, we enter the while loop since top<=bottom and left<=right. +# In the first iteration of the while loop, direction=0 means we need to traverse from left to right along the top boundary. +# We iterate the for loop from left=0 to right=2 and append the elements 1, 2, 3 to the result list. After this, we increment top by 1 to mark that the top boundary is now done. +# The result list now contains [1, 2, 3]. +# In the second iteration of the while loop, direction=1 means we need to traverse from top to bottom along the right boundary. +# We iterate the for loop from top=1 to bottom=2 and append the elements 6, 9 to the result list. After this, we decrement right by 1 to mark that the right boundary is now done. +# The result list now contains [1, 2, 3, 6, 9]. +# In the third iteration of the while loop, direction=2 means we need to traverse from right to left along the bottom boundary. +# We iterate the for loop from right=1 to left=0 and append the elements 8, 7 to the result list. After this, we decrement bottom by 1 to mark that the bottom boundary is now done. +# The result list now contains [1, 2, 3, 6, 9, 8, 7]. +# In the fourth iteration of the while loop, direction=3 means we need to traverse from bottom to top along the left boundary. +# We iterate the for loop from bottom=1 to top=1 and append the element 4 and 5 to the result list. After this, we increment left by 1 to mark that the left boundary is now done. +# The result list now contains [1, 2, 3, 6, 9, 8, 7, 4, 5]. +# Now, we have completed one full spiral traversal of the matrix. The direction variable is updated as (0+1)%4=1 which sets it to 1 for the next iteration. +# We continue with the while loop since top<=bottom and left<=right. In the second iteration, the process continues in the same way as described above until all elements of the matrix are visited. +# Finally, the function returns the result list containing all the spiral order elements of the matrix. + +def spiralOrder(matrix): + # Initialize variables to keep track of indices and boundaries + top, bottom = 0, len(matrix) - 1 + left, right = 0, len(matrix[0]) - 1 + direction = 0 # 0 = right, 1 = down, 2 = left, 3 = up + + # Initialize an empty result list to store the spiral order elements + result = [] + + while top <= bottom and left <= right: + if direction == 0: + # Traverse right + for i in range(left, right+1): + result.append(matrix[top][i]) + top += 1 + elif direction == 1: + # Traverse down + for i in range(top, bottom+1): + result.append(matrix[i][right]) + right -= 1 + elif direction == 2: + # Traverse left + for i in range(right, left-1, -1): + result.append(matrix[bottom][i]) + bottom -= 1 + else: + # Traverse up + for i in range(bottom, top-1, -1): + result.append(matrix[i][left]) + left += 1 + + # Update direction after completing one full traversal + direction = (direction + 1) % 4 + + return result + + +matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] +print(spiralOrder(matrix)) diff --git a/2D Arrays (Matrix)/zigzag_traversal.cpp b/2D Arrays (Matrix)/zigzag_traversal.cpp new file mode 100644 index 00000000..bf7e944d --- /dev/null +++ b/2D Arrays (Matrix)/zigzag_traversal.cpp @@ -0,0 +1,121 @@ +/* + + Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) + and returns a one-dimensional array of all the array's elements in zigzag order. + + Sample Input:= [ + [1, 3, 4, 10], + [2, 5, 9, 11], + [6, 8, 12, 15], + [7, 13, 14, 16], + ] + Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + Explanation: + The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and + returns the elements in a 1D array. + + Here's an explanation of the code: + + 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing + the elements traversed in zigzag order. + + 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. + The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). + + 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. + + 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal + is in the downward direction; otherwise, it is in the upward direction. + + 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. + + 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). + + 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. + + 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. + If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). + + 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the + 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). + + 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of + the 2D array in zigzag order. + + The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches + the boundary or the corners of the array, allowing it to cover all elements in zigzag order. + + O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array +*/ + +#include + +using namespace std; + +vector ZigzagTraverse(vector>& array) { + // Get the height (number of rows) and width (number of columns) of the array. + int height = array.size(); + int width = array[0].size(); + + // Initialize the row and column pointers to start from the top-left element (0,0). + int row = 0; + int col = 0; + + // Initialize a flag to track the direction of traversal (goingDown). + // true means moving down, false means moving up. + bool goingDown = true; + + // Initialize a vector to store the elements in zigzag order. + vector result; + + // Loop until the current position is within the bounds of the array. + while (!isOutOfBounds(row, col, height, width)) { + // Append the current element to the result vector. + result.push_back(array[row][col]); + + // If moving down, check if we reached the bottom row or leftmost column. + if (goingDown) { + if (col == 0 || row == height - 1) { + // Change direction if we reached the bottom row or leftmost column. + goingDown = false; + if (row == height - 1) { + // Move right if we reached the bottom row. + col++; + } else { + // Move down if we reached the leftmost column. + row++; + } + } else { + // Move diagonally down-left. + row++; + col--; + } + } else { + // If moving up, check if we reached the top row or rightmost column. + if (row == 0 || col == width - 1) { + // Change direction if we reached the top row or rightmost column. + goingDown = true; + if (col == width - 1) { + // Move down if we reached the rightmost column. + row++; + } else { + // Move right if we reached the top row. + col++; + } + } else { + // Move diagonally up-right. + row--; + col++; + } + } + } + + // Return the vector containing the elements in zigzag order. + return result; +} + +bool isOutOfBounds(int row, int col, int height, int width) { + // Check if the current position is outside the bounds of the array. + return row < 0 || col < 0 || row >= height || col >= width; +} diff --git a/2D Arrays (Matrix)/zigzag_traversal.go b/2D Arrays (Matrix)/zigzag_traversal.go new file mode 100644 index 00000000..66106082 --- /dev/null +++ b/2D Arrays (Matrix)/zigzag_traversal.go @@ -0,0 +1,116 @@ +/* + + Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) + and returns a one-dimensional array of all the array's elements in zigzag order. + + Sample Input:= [ + [1, 3, 4, 10], + [2, 5, 9, 11], + [6, 8, 12, 15], + [7, 13, 14, 16], + ] + Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + Explanation: + The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and + returns the elements in a 1D array. + + Here's an explanation of the code: + + 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing + the elements traversed in zigzag order. + + 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. + The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). + + 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. + + 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal + is in the downward direction; otherwise, it is in the upward direction. + + 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. + + 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). + + 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. + + 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. + If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). + + 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the + 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). + + 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of + the 2D array in zigzag order. + + The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches + the boundary or the corners of the array, allowing it to cover all elements in zigzag order. + + O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array +*/ +package main + +// ZigzagTraverse traverses a 2D array in a zigzag pattern and returns the elements in a 1D array. +func ZigzagTraverse(array [][]int) []int { + // Get the height and width of the 2D array. + height := len(array) - 1 + width := len(array[0]) - 1 + + // Initialize variables to keep track of the current position while traversing. + row, col := 0, 0 + + // Initialize a boolean variable to determine the direction of traversal. + goingDown := true + + // Initialize an array to store the elements traversed in zigzag order. + result := []int{} + + // The main loop runs until the current position is within bounds (not out of the 2D array). + for !isOutOfBounds(row, col, height, width) { + // Append the current element at position (row, col) to the result array. + result = append(result, array[row][col]) + + // Traversal logic: Decide the next position for traversal based on the current position and the goingDown flag. + if goingDown { + if col == 0 || row == height { + // Change direction to upward if at the top-left or bottom-right corner. + goingDown = false + + // Decide the next position based on whether we are at the bottom or right boundary. + if row == height { + col++ + } else { + row++ + } + } else { + // Continue diagonally downward. + row++ + col-- + } + } else { + if row == 0 || col == width { + // Change direction to downward if at the top-right or bottom-left corner. + goingDown = true + + // Decide the next position based on whether we are at the top or right boundary. + if col == width { + row++ + } else { + col++ + } + } else { + // Continue diagonally upward. + row-- + col++ + } + } + } + + // Return the final result, which contains the elements of the 2D array in zigzag order. + return result +} + +// isOutOfBounds checks if the current position (row, col) is out of bounds of the 2D array. +func isOutOfBounds(row, col, height, width int) bool { + return row < 0 || col < 0 || row > height || col > width +} diff --git a/2D Arrays (Matrix)/zigzag_traversal.java b/2D Arrays (Matrix)/zigzag_traversal.java new file mode 100644 index 00000000..70337d41 --- /dev/null +++ b/2D Arrays (Matrix)/zigzag_traversal.java @@ -0,0 +1,134 @@ +/* + + Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) + and returns a one-dimensional array of all the array's elements in zigzag order. + + Sample Input:= [ + [1, 3, 4, 10], + [2, 5, 9, 11], + [6, 8, 12, 15], + [7, 13, 14, 16], + ] + Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + Explanation: + The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and + returns the elements in a 1D array. + + Here's an explanation of the code: + + 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing + the elements traversed in zigzag order. + + 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. + The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). + + 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. + + 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal + is in the downward direction; otherwise, it is in the upward direction. + + 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. + + 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). + + 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. + + 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. + If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). + + 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the + 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). + + 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of + the 2D array in zigzag order. + + The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches + the boundary or the corners of the array, allowing it to cover all elements in zigzag order. + + O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array +*/ +import java.util.ArrayList; +import java.util.List; + +public class Main { + + public static List ZigzagTraverse(int[][] array) { + // Get the height (number of rows) and width (number of columns) of the array. + int height = array.length; + int width = array[0].length; + + // Initialize the row and column pointers to start from the top-left element (0,0). + int row = 0; + int col = 0; + + // Initialize a flag to track the direction of traversal (goingDown). + // true means moving down, false means moving up. + boolean goingDown = true; + + // Initialize a list to store the elements in zigzag order. + List result = new ArrayList<>(); + + // Loop until the current position is within the bounds of the array. + while (!isOutOfBounds(row, col, height, width)) { + // Append the current element to the result list. + result.add(array[row][col]); + + // If moving down, check if we reached the bottom row or leftmost column. + if (goingDown) { + if (col == 0 || row == height - 1) { + // Change direction if we reached the bottom row or leftmost column. + goingDown = false; + if (row == height - 1) { + // Move right if we reached the bottom row. + col++; + } else { + // Move down if we reached the leftmost column. + row++; + } + } else { + // Move diagonally down-left. + row++; + col--; + } + } else { + // If moving up, check if we reached the top row or rightmost column. + if (row == 0 || col == width - 1) { + // Change direction if we reached the top row or rightmost column. + goingDown = true; + if (col == width - 1) { + // Move down if we reached the rightmost column. + row++; + } else { + // Move right if we reached the top row. + col++; + } + } else { + // Move diagonally up-right. + row--; + col++; + } + } + } + + // Return the list containing the elements in zigzag order. + return result; + } + + public static boolean isOutOfBounds(int row, int col, int height, int width) { + // Check if the current position is outside the bounds of the array. + return row < 0 || col < 0 || row >= height || col >= width; + } + + public static void main(String[] args) { + int[][] array = { + {1, 3, 4, 10}, + {2, 5, 9, 11}, + {6, 8, 12, 15}, + {7, 13, 14, 16} + }; + + List result = ZigzagTraverse(array); + System.out.println(result); + } +} diff --git a/2D Arrays (Matrix)/zigzag_traversal.js b/2D Arrays (Matrix)/zigzag_traversal.js new file mode 100644 index 00000000..e645a36c --- /dev/null +++ b/2D Arrays (Matrix)/zigzag_traversal.js @@ -0,0 +1,116 @@ +/* + + Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) + and returns a one-dimensional array of all the array's elements in zigzag order. + + Sample Input:= [ + [1, 3, 4, 10], + [2, 5, 9, 11], + [6, 8, 12, 15], + [7, 13, 14, 16], + ] + Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + Explanation: + The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and + returns the elements in a 1D array. + + Here's an explanation of the code: + + 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing + the elements traversed in zigzag order. + + 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. + The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). + + 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. + + 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal + is in the downward direction; otherwise, it is in the upward direction. + + 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. + + 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). + + 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. + + 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. + If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). + + 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the + 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). + + 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of + the 2D array in zigzag order. + + The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches + the boundary or the corners of the array, allowing it to cover all elements in zigzag order. + + O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array +*/ +function ZigzagTraverse(array) { + // Get the height (number of rows) and width (number of columns) of the array. + const height = array.length; + const width = array[0].length; + + // Initialize the row and column pointers to start from the top-left element (0,0). + let row = 0; + let col = 0; + + // Initialize a flag to track the direction of traversal (goingDown). + // true means moving down, false means moving up. + let goingDown = true; + + // Initialize an array to store the elements in zigzag order. + const result = []; + + // Loop until the current position is within the bounds of the array. + while (!isOutOfBounds(row, col, height, width)) { + // Append the current element to the result array. + result.push(array[row][col]); + + // If moving down, check if we reached the bottom row or leftmost column. + if (goingDown) { + if (col === 0 || row === height - 1) { + // Change direction if we reached the bottom row or leftmost column. + goingDown = false; + if (row === height - 1) { + // Move right if we reached the bottom row. + col++; + } else { + // Move down if we reached the leftmost column. + row++; + } + } else { + // Move diagonally down-left. + row++; + col--; + } + } else { + // If moving up, check if we reached the top row or rightmost column. + if (row === 0 || col === width - 1) { + // Change direction if we reached the top row or rightmost column. + goingDown = true; + if (col === width - 1) { + // Move down if we reached the rightmost column. + row++; + } else { + // Move right if we reached the top row. + col++; + } + } else { + // Move diagonally up-right. + row--; + col++; + } + } + } + + // Return the array containing the elements in zigzag order. + return result; +} + +function isOutOfBounds(row, col, height, width) { + // Check if the current position is outside the bounds of the array. + return row < 0 || col < 0 || row >= height || col >= width; +} diff --git a/2D Arrays (Matrix)/zigzag_traversal.py b/2D Arrays (Matrix)/zigzag_traversal.py new file mode 100644 index 00000000..3a23c6f3 --- /dev/null +++ b/2D Arrays (Matrix)/zigzag_traversal.py @@ -0,0 +1,108 @@ +''' + + Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) + and returns a one-dimensional array of all the array's elements in zigzag order. + + Sample Input:= [ + [1, 3, 4, 10], + [2, 5, 9, 11], + [6, 8, 12, 15], + [7, 13, 14, 16], + ] + Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + Explanation: + The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and + returns the elements in a 1D array. + + Here's an explanation of the code: + + 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing + the elements traversed in zigzag order. + + 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. + The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). + + 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. + + 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal + is in the downward direction; otherwise, it is in the upward direction. + + 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. + + 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). + + 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. + + 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. + If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). + + 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the + 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). + + 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of + the 2D array in zigzag order. + + The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches + the boundary or the corners of the array, allowing it to cover all elements in zigzag order. + + O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array +''' + +def ZigzagTraverse(array): + # Get the height (number of rows) and width (number of columns) of the array. + height = len(array) + width = len(array[0]) + + # Initialize the row and column pointers to start from the top-left element (0,0). + row, col = 0, 0 + + # Initialize a flag to track the direction of traversal (goingDown). + # True means moving down, False means moving up. + goingDown = True + + # Initialize a list to store the elements in zigzag order. + result = [] + + # Loop until the current position is within the bounds of the array. + while not isOutOfBounds(row, col, height, width): + # Append the current element to the result list. + result.append(array[row][col]) + + # If moving down, check if we reached the bottom row or leftmost column. + if goingDown: + if col == 0 or row == height - 1: + # Change direction if we reached the bottom row or leftmost column. + goingDown = False + if row == height - 1: + # Move right if we reached the bottom row. + col += 1 + else: + # Move down if we reached the leftmost column. + row += 1 + else: + # Move diagonally down-left. + row += 1 + col -= 1 + else: + # If moving up, check if we reached the top row or rightmost column. + if row == 0 or col == width - 1: + # Change direction if we reached the top row or rightmost column. + goingDown = True + if col == width - 1: + # Move down if we reached the rightmost column. + row += 1 + else: + # Move right if we reached the top row. + col += 1 + else: + # Move diagonally up-right. + row -= 1 + col += 1 + + # Return the list containing the elements in zigzag order. + return result + +def isOutOfBounds(row, col, height, width): + # Check if the current position is outside the bounds of the array. + return row < 0 or col < 0 or row >= height or col >= width diff --git a/2D-Arrays/search_el.cpp b/2D-Arrays/search_el.cpp deleted file mode 100644 index 533fb2bb..00000000 --- a/2D-Arrays/search_el.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/*You are given an m x n integer matrix matrix with the following two properties: - -Each row is sorted in non-decreasing order. -The first integer of each row is greater than the last integer of the previous row. -Given an integer target, return true if target is in matrix or false otherwise. - -You must write a solution in O(log(m * n)) time complexity. - - -1.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 -Output: true - -2.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 -Output: false*/ - -class Solution { -public: - bool searchMatrix(vector> &matrix,int target) { - int i = 0; - int j = matrix[0].size() - 1; - - while(i < matrix.size() && j >= 0) - { - if(target == matrix[i][j]) - return true; - else if(target < matrix[i][j]) - j--; - else - i++; - } - return false; - } -}; diff --git a/Arrays/Jobassign.cpp b/Arrays/Jobassign.cpp new file mode 100644 index 00000000..ddda1007 --- /dev/null +++ b/Arrays/Jobassign.cpp @@ -0,0 +1,95 @@ +#include +#include + +#define N 4 // Number of workers and tasks + +void hungarianAlgorithm(int costMatrix[N][N]); + +int main() { + int costMatrix[N][N] = { + {9, 2, 7, 8}, + {6, 4, 3, 7}, + {5, 8, 1, 8}, + {7, 6, 9, 4} + }; + + hungarianAlgorithm(costMatrix); + + return 0; +} + +void hungarianAlgorithm(int costMatrix[N][N]) { + int i, j; + int numWorkers = N, numTasks = N; + + int minCost, minCostIdx; + int rowCover[N] = {0}; + int colCover[N] = {0}; + int assignment[N][2] = {0}; // Stores the assignment + + // Step 1: Subtract the smallest value in each row from all elements in that row + for (i = 0; i < numWorkers; i++) { + minCost = INT_MAX; + for (j = 0; j < numTasks; j++) { + if (costMatrix[i][j] < minCost) { + minCost = costMatrix[i][j]; + } + } + for (j = 0; j < numTasks; j++) { + costMatrix[i][j] -= minCost; + } + } + + // Step 2: Find a zero in the cost matrix and mark the row and column + for (i = 0; i < numWorkers; i++) { + for (j = 0; j < numTasks; j++) { + if (costMatrix[i][j] == 0 && !rowCover[i] && !colCover[j]) { + assignment[i][0] = i; + assignment[i][1] = j; + rowCover[i] = 1; + colCover[j] = 1; + } + } + } + + // Step 3: Check if all rows are covered + int rowCoveredCount = 0; + for (i = 0; i < numWorkers; i++) { + rowCoveredCount += rowCover[i]; + } + + if (rowCoveredCount == numWorkers) { + // All rows are covered, we have the optimal assignment + printf("Optimal Assignment:\n"); + for (i = 0; i < numWorkers; i++) { + printf("Worker %d -> Task %d\n", assignment[i][0] + 1, assignment[i][1] + 1); + } + return; + } else { + // Proceed to step 4 + } + + // Step 4: Find the minimum uncovered value (minCost) in the cost matrix + minCost = INT_MAX; + for (i = 0; i < numWorkers; i++) { + for (j = 0; j < numTasks; j++) { + if (!rowCover[i] && !colCover[j] && costMatrix[i][j] < minCost) { + minCost = costMatrix[i][j]; + } + } + } + + // Step 5: Subtract minCost from all uncovered elements and add it to all elements at the intersection of covering lines + for (i = 0; i < numWorkers; i++) { + for (j = 0; j < numTasks; j++) { + if (!rowCover[i] && !colCover[j]) { + costMatrix[i][j] -= minCost; + } else if (rowCover[i] && colCover[j]) { + costMatrix[i][j] += minCost; + } + } + } + + // Continue to step 3 + hungarianAlgorithm(costMatrix); +} diff --git a/Arrays/Maximum_freq.py b/Arrays/Maximum_freq.py new file mode 100644 index 00000000..d0868caf --- /dev/null +++ b/Arrays/Maximum_freq.py @@ -0,0 +1,25 @@ +class Solution { + public int majorityElement(int[] nums) { + int candidate = nums[0]; // Initialize the candidate as the first element + int count = 1; // Initialize the count of the current candidate as 1 + + // Iterate through the array starting from the second element + for (int i = 1; i < nums.length; i++) { + if (nums[i] == candidate) { + // If the current element is the same as the candidate, increment the count + count++; + } else if (count > 0) { + // If the current element is different from the candidate and count is positive, + // decrement the count since we have a matching pair (candidate vs. current element) + count--; + } else { + // If count becomes zero, update the candidate to the current element and set count to 1 + candidate = nums[i]; + count = 1; + } + } + + // At the end, the candidate will be the majority element + return candidate; + } +} diff --git a/Arrays/RemoveDuplicates.java b/Arrays/RemoveDuplicates.java deleted file mode 100644 index a2936bbb..00000000 --- a/Arrays/RemoveDuplicates.java +++ /dev/null @@ -1,53 +0,0 @@ -/* -Given an integer array nums sorted in non-decreasing order, -remove the duplicates in-place such that each unique element appears only once. - The relative order of the elements should be kept the same. -Since it is impossible to change the length of the array in some languages, -you must instead have the result be placed in the first part of the array nums. -More formally, if there are k elements after removing the duplicates, -then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements. -Return k after placing the final result in the first k slots of nums. -Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory. - -Example 1: - -Input: nums = [1,1,2] -Output: 2, nums = [1,2,_] -Explanation: Your function should return k = 2, with the first two elements of nums being 1 and 2 respectively. -It does not matter what you leave beyond the returned k (hence they are underscores). -Example 2: - -Input: nums = [0,0,1,1,1,2,2,3,3,4] -Output: 5, nums = [0,1,2,3,4,_,_,_,_,_] -Explanation: Your function should return k = 5, with the first five elements of nums being 0, 1, 2, 3, and 4 respectively. -It does not matter what you leave beyond the returned k (hence they are underscores). - - */ - -class Solution { - public int removeDuplicates(int[] nums) { - int n = nums.length; - - int j = 0; - if(n == 0 || n == 1 ) - return n; - - for (int i = 0; i < n-1; i++) - { - if(nums[i] != nums[i+1]) - nums[j++]= nums[i]; - } - nums[j++]=nums[n-1]; - return j; - } -} -class RemoveDuplicates { - - public static void main(String[] args) { - - Solution s = new Solution(); - int nums [] = {1 ,2 ,2 ,2 ,45 ,67 ,89 ,89 ,89 }; - - System.out.println( s.removeDuplicates(nums)); - } -} \ No newline at end of file diff --git a/Arrays/Rotatedarr.cpp b/Arrays/Rotatedarr.cpp new file mode 100644 index 00000000..6d7ca494 --- /dev/null +++ b/Arrays/Rotatedarr.cpp @@ -0,0 +1,30 @@ +class Solution { +public: + int search(std::vector& nums, int target) { + int low = 0, high = nums.size() - 1; + + while (low <= high) { + int mid = (low + high) / 2; + + if (nums[mid] == target) { + return mid; + } + + if (nums[low] <= nums[mid]) { + if (nums[low] <= target && target < nums[mid]) { + high = mid - 1; + } else { + low = mid + 1; + } + } else { + if (nums[mid] < target && target <= nums[high]) { + low = mid + 1; + } else { + high = mid - 1; + } + } + } + + return -1; + } +}; diff --git a/Arrays/StockTrading.java b/Arrays/StockTrading.java deleted file mode 100644 index f5ac112f..00000000 --- a/Arrays/StockTrading.java +++ /dev/null @@ -1,57 +0,0 @@ -/* -Best Time to Buy and Sell Stoks 2 - -You are given an integer array prices where prices[i] is the price of a given stock on the ith day. - -On each day, you may decide to buy and/or sell the stock. You can only hold at most one share of the stock at any time. However, you can buy it then immediately sell it on the same day. - -Find and return the maximum profit you can achieve. - - - -Example 1: - -Input: prices = [7,1,5,3,6,4] -Output: 7 -Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4. -Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3. -Total profit is 4 + 3 = 7. -Example 2: - -Input: prices = [1,2,3,4,5] -Output: 4 -Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. -Total profit is 4. -Example 3: - -Input: prices = [7,6,4,3,1] -Output: 0 -Explanation: There is no way to make a positive profit, so we never buy the stock to achieve the maximum profit of 0. - - -Constraints: - -1 <= prices.length <= 3 * 104 -0 <= prices[i] <= 104 -*/ -class Solution { - public int maxProfit(int[] prices) { - - int n=prices.length; - int sum=0; - - for(int i=0;i array = new ArrayList<>( - Arrays.asList(1, 0, 1, 0, 1) - ); - int b = 1; - ArrayList ans = solve(array, b); - System.out.println(ans); - - } - public static ArrayList solve(ArrayList array, int b) { - /** - * len = 2*b + 1 --> time --> O(b) - * (b*(n-b)) time | O(n) space - */ - - ArrayList result = new ArrayList<>(); - int n = array.size(); - int len = 2 * b + 1; - int subArrays = n - len + 1; - - for (int i = 0; i < subArrays; i++) { - int prevNum = array.get(i); - int flag = 1; - for (int j = i + 1; j < i + len; j++) { - int currentNum = array.get(j); - if (prevNum == currentNum) { - flag = 0; - break; - } - prevNum = currentNum; - } - if (flag == 1) result.add(i + b); // add center of subarray - } - return result; - } -} diff --git a/Arrays/anti_diagonals.java b/Arrays/anti_diagonals.java deleted file mode 100644 index 2fe6e90e..00000000 --- a/Arrays/anti_diagonals.java +++ /dev/null @@ -1,102 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Give a N * N square matrix A, return an array of its anti-diagonals. Look at the example for more details. - * - * - * Problem Constraints - * 1<= N <= 1000 - * 1<= A[i][j] <= 1e9 - * - * - * Input Format - * Only argument is a 2D array A of size N * N. - * - * - * Output Format - * Return a 2D integer array of size (2 * N-1) * N, representing the anti-diagonals of input array A. - * The vacant spaces in the grid should be assigned to 0. - * - * - * Example Input - * Input 1: - * 1 2 3 - * 4 5 6 - * 7 8 9 - * Input 2: - * - * 1 2 - * 3 4 - * - * - * Example Output - * Output 1: - * 1 0 0 - * 2 4 0 - * 3 5 7 - * 6 8 0 - * 9 0 0 - * Output 2: - * - * 1 0 - * 2 3 - * 4 0 - * - * - * Example Explanation - * For input 1: - * The first anti diagonal of the matrix is [1 ], the rest spaces shoud be filled with 0 making the row as [1, 0, 0]. - * The second anti diagonal of the matrix is [2, 4 ], the rest spaces shoud be filled with 0 making the row as [2, 4, 0]. - * The third anti diagonal of the matrix is [3, 5, 7 ], the rest spaces shoud be filled with 0 making the row as [3, 5, 7]. - * The fourth anti diagonal of the matrix is [6, 8 ], the rest spaces shoud be filled with 0 making the row as [6, 8, 0]. - * The fifth anti diagonal of the matrix is [9 ], the rest spaces shoud be filled with 0 making the row as [9, 0, 0]. - * For input 2: - * - * The first anti diagonal of the matrix is [1 ], the rest spaces shoud be filled with 0 making the row as [1, 0, 0]. - * The second anti diagonal of the matrix is [2, 4 ], the rest spaces shoud be filled with 0 making the row as [2, 4, 0]. - * The third anti diagonal of the matrix is [3, 0, 0 ], the rest spaces shoud be filled with 0 making the row as [3, 0, 0]. - */ -public class AntiDiagonals { - public static void main(String[] args) { - List> array = new ArrayList<>(); - array.add(Arrays.asList(1, 2, 3)); - array.add(Arrays.asList(4, 5, 6)); - array.add(Arrays.asList(7 ,8, 9)); - - List> result = solve(array); - for (List list: result) { - System.out.println(list); - } - } - public static List> solve(List> array) { - // O(row * col) time | (row * col) space - List> result = new ArrayList<>(); - int row = array.size(); - - // 0th row - for (int col = 0; col < array.get(0).size(); col++) { - printDiagonal(0, col, array, result); //row is fixed - } - - // last col - int col = array.get(0).size() - 1; - for (int i = 1; i < row; i++) { - printDiagonal(i, col, array, result); //col is fixed - } - return result; - } - public static void printDiagonal(int row, int col, List> array, List> result) { - List ans = new ArrayList<>(); - for (var num : array) { - ans.add(0); - } - int i = 0; - while (row < array.size() && col > -1) { - int currentNum = array.get(row++).get(col--); - ans.set(i++, currentNum); - } - result.add(ans); - } -} diff --git a/Arrays/array_of_products.cpp b/Arrays/array_of_products.cpp new file mode 100644 index 00000000..20bbcc4e --- /dev/null +++ b/Arrays/array_of_products.cpp @@ -0,0 +1,77 @@ +/* + Given an array of integers A, find and return the product array of the same size where the ith + element of the product array will be equal to the product of all the elements divided by the ith + element of the array. + + Note: It is always possible to form the product array with integer (32 bit) values. + Solve it without using the division operator. + + Input: [1, 2, 3, 4, 5] + Output : [120, 60, 40, 30, 24] + + Explanation: + The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: + + 1. It initializes an empty result array with the same length as the input array to store the final products. + 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. + 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. + 4. It iterates over the input array from left to right using a `for` loop. + 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the + `leftRunningProduct` by multiplying it with the corresponding element in the input array. + 6. After the loop, the result array will contain the product of all elements to the left of each index. + 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and + multiply it with the corresponding left product in the result array. + 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. + 9. It iterates over the input array from right to left using a `for` loop. + 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array + and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. + 11. After the loop, the result array will contain the product of all elements to the left and right of each index, + except for the element at that index. + 12. Finally, it returns the result array. + + This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. +*/ + +#include +#include + +using namespace std; + +// Given an array of integers, returns an array where each element +// is the product of all the integers in the input array except for the one at that index. +vector ArrayOfProducts(vector& array) { + int n = array.size(); + vector result(n, 1); + + // Compute the running product of all elements to the left of each index + // and store it in the result array. + int leftRunningProduct = 1; + for (int i = 0; i < n; i++) { + result[i] = leftRunningProduct; // Store left product in the result array + leftRunningProduct *= array[i]; // Update left product + } + + // Compute the running product of all elements to the right of each index + // and multiply it with the corresponding left product in the result array. + int rightRunningProduct = 1; + for (int i = n - 1; i >= 0; i--) { + result[i] *= rightRunningProduct; // Multiply the right product with the corresponding left product + rightRunningProduct *= array[i]; // Update right product + } + + return result; +} + +int main() { + // Example usage + vector input = {1, 2, 3, 4, 5}; + vector output = ArrayOfProducts(input); + + // Print the result + for (int num : output) { + cout << num << " "; + } + cout << endl; + + return 0; +} diff --git a/Arrays/array_of_products.go b/Arrays/array_of_products.go index 812c8594..cfdf2e31 100644 --- a/Arrays/array_of_products.go +++ b/Arrays/array_of_products.go @@ -1,58 +1,60 @@ -package main +/* + Given an array of integers A, find and return the product array of the same size where the ith + element of the product array will be equal to the product of all the elements divided by the ith + element of the array. + + Note: It is always possible to form the product array with integer (32 bit) values. + Solve it without using the division operator. -import "fmt" + Input: [1, 2, 3, 4, 5] + Output : [120, 60, 40, 30, 24] -/** - * Given an array of integers A, find and return the product array of the same size where the ith element of the product array will be equal to the product of all the elements divided by the ith element of the array. - * - * Note: It is always possible to form the product array with integer (32 bit) values. Solve it without using the division operator. - * - * - * Input Format - * - * The only argument given is the integer array A. - * Output Format - * - * Return the product array. - * Constraints - * - * 2 <= length of the array <= 1000 - * 1 <= A[i] <= 10 - * For Example - * - * Input 1: - * A = [1, 2, 3, 4, 5] - * Output 1: - * [120, 60, 40, 30, 24] - * - * Input 2: - * A = [5, 1, 10, 1] - * Output 2: - * [10, 50, 5, 50] - */ + Explanation: + The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: -func ArrayOfProducts(Arr[]int) { - Result := make([]int, len(Arr)) - product := 1 - // fill left prefix product in result array - for i := 0; i < len(Arr); i++ { - Result[i] = product - product = product * Arr[i] - } + 1. It initializes an empty result array with the same length as the input array to store the final products. + 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. + 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. + 4. It iterates over the input array from left to right using a `for` loop. + 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the + `leftRunningProduct` by multiplying it with the corresponding element in the input array. + 6. After the loop, the result array will contain the product of all elements to the left of each index. + 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and + multiply it with the corresponding left product in the result array. + 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. + 9. It iterates over the input array from right to left using a `for` loop. + 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array + and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. + 11. After the loop, the result array will contain the product of all elements to the left and right of each index, + except for the element at that index. + 12. Finally, it returns the result array. - // fill right prefix product in result array - product = 1 - for i := len(Arr) - 1; i >= 0; i-- { - Result[i] = Result[i] * product - product = product * Arr[i] - } + This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. +*/ - for i := 0; i < len(Arr); i++ { - fmt.Print(Result[i]," ") - } -} +package main - func main() { - Arr := []int{5, 1, 10, 1} - ArrayOfProducts(Arr) - } \ No newline at end of file +// Given an array of integers, returns an array where each element +// is the product of all the integers in the input array except for the one at that index. + func ArrayOfProducts(array []int) []int { + result := make([]int, len(array)) + + // Compute the running product of all elements to the left of each index + // and store it in the result array. + leftRunningProduct := 1 + for i := 0; i < len(array); i++ { + result[i] = leftRunningProduct // Store left product in the result array + leftRunningProduct *= array[i] // Update left product + } + + // Compute the running product of all elements to the right of each index + // and multiply it with the corresponding left product in the result array. + rightRunningProduct := 1 + for i := len(array) - 1; i >= 0; i-- { + result[i] = rightRunningProduct * result[i] // Multiply the right product with the corresponding left product + rightRunningProduct *= array[i] // Update right product + } + + return result + } + diff --git a/Arrays/array_of_products.java b/Arrays/array_of_products.java index 65bfe30e..b1f65e82 100644 --- a/Arrays/array_of_products.java +++ b/Arrays/array_of_products.java @@ -1,59 +1,68 @@ -/** - * Given an array of integers A, find and return the product array of the same size where the ith element of the product array will be equal to the product of all the elements divided by the ith element of the array. - * - * Note: It is always possible to form the product array with integer (32 bit) values. Solve it without using the division operator. - * - * - * Input Format - * - * The only argument given is the integer array A. - * Output Format - * - * Return the product array. - * Constraints - * - * 2 <= length of the array <= 1000 - * 1 <= A[i] <= 10 - * For Example - * - * Input 1: - * A = [1, 2, 3, 4, 5] - * Output 1: - * [120, 60, 40, 30, 24] - * - * Input 2: - * A = [5, 1, 10, 1] - * Output 2: - * [10, 50, 5, 50] - */ -package PrefixSum; +/* + Given an array of integers A, find and return the product array of the same size where the ith + element of the product array will be equal to the product of all the elements divided by the ith + element of the array. + Note: It is always possible to form the product array with integer (32 bit) values. + Solve it without using the division operator. + + Input: [1, 2, 3, 4, 5] + Output : [120, 60, 40, 30, 24] + + Explanation: + The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: + + 1. It initializes an empty result array with the same length as the input array to store the final products. + 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. + 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. + 4. It iterates over the input array from left to right using a `for` loop. + 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the + `leftRunningProduct` by multiplying it with the corresponding element in the input array. + 6. After the loop, the result array will contain the product of all elements to the left of each index. + 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and + multiply it with the corresponding left product in the result array. + 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. + 9. It iterates over the input array from right to left using a `for` loop. + 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array + and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. + 11. After the loop, the result array will contain the product of all elements to the left and right of each index, + except for the element at that index. + 12. Finally, it returns the result array. + + This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. +*/ import java.util.Arrays; public class ArrayOfProducts { - public static void main(String[] args) { - int[] array = {5, 1, 4, 2}; - int[] ans = solve(array); - System.out.println(Arrays.toString(ans)); - } - public static int[] solve(int[] array) { - // O(n) time | O(n) space - int length = array.length; - int products[] = new int[length]; + public static int[] arrayOfProducts(int[] array) { + int n = array.length; + int[] result = new int[n]; + // Compute the running product of all elements to the left of each index + // and store it in the result array. int leftRunningProduct = 1; - for (int i = 0; i < length; i++) { - int currentNum = array[i]; - products[i] = leftRunningProduct; - leftRunningProduct *= currentNum; + for (int i = 0; i < n; i++) { + result[i] = leftRunningProduct; // Store left product in the result array + leftRunningProduct *= array[i]; // Update left product } + // Compute the running product of all elements to the right of each index + // and multiply it with the corresponding left product in the result array. int rightRunningProduct = 1; - for (int i = length - 1; i > -1; i--) { - int currentNum = array[i]; - products[i] *= rightRunningProduct; - rightRunningProduct *= currentNum; + for (int i = n - 1; i >= 0; i--) { + result[i] *= rightRunningProduct; // Multiply the right product with the corresponding left product + rightRunningProduct *= array[i]; // Update right product } - return products; + + return result; + } + + public static void main(String[] args) { + // Example usage + int[] input = {1, 2, 3, 4, 5}; + int[] output = arrayOfProducts(input); + + // Print the result + System.out.println(Arrays.toString(output)); } } diff --git a/Arrays/array_of_products.js b/Arrays/array_of_products.js new file mode 100644 index 00000000..55c38276 --- /dev/null +++ b/Arrays/array_of_products.js @@ -0,0 +1,62 @@ +/* + Given an array of integers A, find and return the product array of the same size where the ith + element of the product array will be equal to the product of all the elements divided by the ith + element of the array. + + Note: It is always possible to form the product array with integer (32 bit) values. + Solve it without using the division operator. + + Input: [1, 2, 3, 4, 5] + Output : [120, 60, 40, 30, 24] + + Explanation: + The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: + + 1. It initializes an empty result array with the same length as the input array to store the final products. + 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. + 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. + 4. It iterates over the input array from left to right using a `for` loop. + 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the + `leftRunningProduct` by multiplying it with the corresponding element in the input array. + 6. After the loop, the result array will contain the product of all elements to the left of each index. + 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and + multiply it with the corresponding left product in the result array. + 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. + 9. It iterates over the input array from right to left using a `for` loop. + 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array + and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. + 11. After the loop, the result array will contain the product of all elements to the left and right of each index, + except for the element at that index. + 12. Finally, it returns the result array. + + This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. +*/ +function arrayOfProducts(array) { + const n = array.length; + const result = new Array(n).fill(1); + + // Compute the running product of all elements to the left of each index + // and store it in the result array. + let leftRunningProduct = 1; + for (let i = 0; i < n; i++) { + result[i] = leftRunningProduct; // Store left product in the result array + leftRunningProduct *= array[i]; // Update left product + } + + // Compute the running product of all elements to the right of each index + // and multiply it with the corresponding left product in the result array. + let rightRunningProduct = 1; + for (let i = n - 1; i >= 0; i--) { + result[i] *= rightRunningProduct; // Multiply the right product with the corresponding left product + rightRunningProduct *= array[i]; // Update right product + } + + return result; +} + +// Example usage +const inputArray = [1, 2, 3, 4, 5]; +const outputArray = arrayOfProducts(inputArray); + +// Print the result +console.log(outputArray); diff --git a/Arrays/array_of_products.py b/Arrays/array_of_products.py new file mode 100644 index 00000000..2972deea --- /dev/null +++ b/Arrays/array_of_products.py @@ -0,0 +1,59 @@ +''' + Given an array of integers A, find and return the product array of the same size where the ith + element of the product array will be equal to the product of all the elements divided by the ith + element of the array. + + Note: It is always possible to form the product array with integer (32 bit) values. + Solve it without using the division operator. + + Input: [1, 2, 3, 4, 5] + Output : [120, 60, 40, 30, 24] + + Explanation: + The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: + + 1. It initializes an empty result array with the same length as the input array to store the final products. + 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. + 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. + 4. It iterates over the input array from left to right using a `for` loop. + 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the + `leftRunningProduct` by multiplying it with the corresponding element in the input array. + 6. After the loop, the result array will contain the product of all elements to the left of each index. + 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and + multiply it with the corresponding left product in the result array. + 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. + 9. It iterates over the input array from right to left using a `for` loop. + 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array + and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. + 11. After the loop, the result array will contain the product of all elements to the left and right of each index, + except for the element at that index. + 12. Finally, it returns the result array. + + This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. +''' +def array_of_products(array): + n = len(array) + result = [1] * n + + # Compute the running product of all elements to the left of each index + # and store it in the result array. + left_running_product = 1 + for i in range(n): + result[i] = left_running_product # Store left product in the result array + left_running_product *= array[i] # Update left product + + # Compute the running product of all elements to the right of each index + # and multiply it with the corresponding left product in the result array. + right_running_product = 1 + for i in range(n - 1, -1, -1): + result[i] *= right_running_product # Multiply the right product with the corresponding left product + right_running_product *= array[i] # Update right product + + return result + +# Example usage +input_array = [1, 2, 3, 4, 5] +output_array = array_of_products(input_array) + +# Print the result +print(output_array) diff --git a/Arrays/bulbs.go b/Arrays/bulbs.go deleted file mode 100644 index 7abebd0f..00000000 --- a/Arrays/bulbs.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - N light bulbs are connected by a wire. - Each bulb has a switch associated with it, however due to faulty wiring, a switch also changes the state of all the bulbs to the right of current bulb. - Given an initial state of all bulbs, find the minimum number of switches you have to press to turn on all the bulbs. - You can press the same switch multiple times. - Note : 0 represents the bulb is off and 1 represents the bulb is on. - - Input: A = [0 1 0 1] - Output: 4 -*/ -package main - -import "fmt" - -func FindMinSwitch(Arr []int) int { - cost := 0 - for i := 0; i < len(Arr); i++ { - if cost & 1 != 1 { - } else { - Arr[i] = 1 - Arr[i] - } - if Arr[i] & 1 == 1 { - continue - } else { - cost += 1 - } - } - return cost -} - -func main() { - Arr := []int{1, 0, 1, 0} - fmt.Println("-->",FindMinSwitch(Arr)) - Arr = []int{0, 1, 0, 1} - fmt.Println("-->",FindMinSwitch(Arr)) - Arr = []int{1, 1, 1, 1} - fmt.Println("-->",FindMinSwitch(Arr)) - Arr = []int{1, 0, 1, 0, 1, 0, 1, 0, 0} - fmt.Println("-->",FindMinSwitch(Arr)) -} \ No newline at end of file diff --git a/Arrays/ceaser_cipher.cpp b/Arrays/ceaser_cipher.cpp new file mode 100644 index 00000000..f1c57495 --- /dev/null +++ b/Arrays/ceaser_cipher.cpp @@ -0,0 +1,92 @@ +/* + Given a non-empty string of lowercase letters and a non-negative integer + representing a key, write a function that returns a new string obtained by + shifting every letter in the input string by k positions in the alphabet, + where k is the key. + + Note that letters should "wrap" around the alphabet; in other words, the + letter z shifted by one returns the letter a + + Sample Input : abz key: 3 + Output: dec + + Explanation: + + The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string + obtained by shifting each character of the input string by the key number of positions to the right in + the alphabet, wrapping around if necessary. + + The function first calculates the shift amount and offset value. The shift amount is calculated by taking + the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset + value is calculated by taking 26, which is the number of letters in the alphabet. + + The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single + character in a programming language. The rune type is used to represent characters in Go. + + The function then iterates over each character in the rune slice. For each character, the function checks + + if the character is a lowercase letter and if shifting it will still be within the lowercase range. + + If the character is a lowercase letter and shifting it will still be within the lowercase range, the + function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. + + The function then updates the character in the rune slice. + + The function then converts the resulting rune slice back to a string and returns it. + + The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function iterates over each character in the input string, performing a constant amount + of work for each character. + + The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function creates a new rune slice to store the encrypted string. The rune slice is the + same size as the input string, so the space complexity is O(n). +*/ +// C++ + +#include +#include + +using namespace std; + +string CaesarCipherEncryptor(string str, int key) { + // Calculate the shift amount and offset value + int shift = key % 26; + int offset = 26; + + // Convert the input string to a vector of characters + vector chars(str.begin(), str.end()); + + // Iterate over each character in the vector + for (int i = 0; i < chars.size(); i++) { + // If the character is a lowercase letter and shifting it will still be within the lowercase range + if (chars[i] >= 'a' && chars[i] + shift <= 'z') { + chars[i] += shift; + } else { + // If the character is outside of the lowercase range after shifting, wrap it around + chars[i] += shift - offset; + } + } + + // Convert the resulting vector of characters back to a string and return it + return string(chars.begin(), chars.end()); +} + +int main() { + // Get the input string and key from the user + string str; + cout << "Enter a string: "; + cin >> str; + + int key; + cout << "Enter a key: "; + cin >> key; + + // Encrypt the string using the Caesar cipher + string encrypted_str = CaesarCipherEncryptor(str, key); + + // Print the encrypted string + cout << "Encrypted string: " << encrypted_str << endl; + + return 0; +} diff --git a/Arrays/ceaser_cipher.go b/Arrays/ceaser_cipher.go index f730fe9b..00890d17 100644 --- a/Arrays/ceaser_cipher.go +++ b/Arrays/ceaser_cipher.go @@ -1,34 +1,72 @@ /* - Given a non-empty string of lowercase letters and a non-negative integer - representing a key, write a function that returns a new string obtained by - shifting every letter in the input string by k positions in the alphabet, - where k is the key. + Given a non-empty string of lowercase letters and a non-negative integer + representing a key, write a function that returns a new string obtained by + shifting every letter in the input string by k positions in the alphabet, + where k is the key. - Note that letters should "wrap" around the alphabet; in other words, the - letter z shifted by one returns the letter a + Note that letters should "wrap" around the alphabet; in other words, the + letter z shifted by one returns the letter a - Sample Input : abz key: 3 - Output: dec + Sample Input : abz key: 3 + Output: dec + + Explanation: + + The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string + obtained by shifting each character of the input string by the key number of positions to the right in + the alphabet, wrapping around if necessary. + + The function first calculates the shift amount and offset value. The shift amount is calculated by taking + the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset + value is calculated by taking 26, which is the number of letters in the alphabet. + + The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single + character in a programming language. The rune type is used to represent characters in Go. + + The function then iterates over each character in the rune slice. For each character, the function checks + + if the character is a lowercase letter and if shifting it will still be within the lowercase range. + + If the character is a lowercase letter and shifting it will still be within the lowercase range, the + function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. + + The function then updates the character in the rune slice. + + The function then converts the resulting rune slice back to a string and returns it. + + The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function iterates over each character in the input string, performing a constant amount + of work for each character. + + The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function creates a new rune slice to store the encrypted string. The rune slice is the + same size as the input string, so the space complexity is O(n). */ package main -import "fmt" - +// CaesarCipherEncryptor takes a string and a key (integer) as input and returns +// a new string obtained by shifting each character of the input string by the +// key number of positions to the right in the alphabet, wrapping around if necessary. func CaesarCipherEncryptor(str string, key int) string { - key = key % 26 // mod with 26 just in case the key is not bigger - result := "" - // find out ascii value if its greater than ascii value of 'z' then reduce 26 from value - for i := 0; i < len(str); i++ { - asciiValue := int(str[i]) + key - if asciiValue > 122 { - asciiValue -= 26 - } - result += string(asciiValue) - } - return result + // Calculate the shift amount and offset value + shift, offset := rune(key % 26), rune(26) + + // Convert the input string to a rune slice (for ease of manipulation) + runes := []rune(str) + + // Iterate over each character in the rune slice + for i, char := range runes { + // If the character is a lowercase letter and shifting it will still be within the lowercase range + if char >= 'a' && char + shift <= 'z' { + char += shift + } else { + // If the character is outside of the lowercase range after shifting, wrap it around + char += shift - offset + } + // Update the character in the rune slice + runes[i] = char + } + + // Convert the resulting rune slice back to a string and return it + return string(runes) } - -func main() { - msg := CaesarCipherEncryptor("abcadzxc", 10) - fmt.Println(msg) -} \ No newline at end of file diff --git a/Arrays/ceaser_cipher.java b/Arrays/ceaser_cipher.java new file mode 100644 index 00000000..ede0790f --- /dev/null +++ b/Arrays/ceaser_cipher.java @@ -0,0 +1,89 @@ +/* + Given a non-empty string of lowercase letters and a non-negative integer + representing a key, write a function that returns a new string obtained by + shifting every letter in the input string by k positions in the alphabet, + where k is the key. + + Note that letters should "wrap" around the alphabet; in other words, the + letter z shifted by one returns the letter a + + Sample Input : abz key: 3 + Output: dec + + Explanation: + + The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string + obtained by shifting each character of the input string by the key number of positions to the right in + the alphabet, wrapping around if necessary. + + The function first calculates the shift amount and offset value. The shift amount is calculated by taking + the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset + value is calculated by taking 26, which is the number of letters in the alphabet. + + The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single + character in a programming language. The rune type is used to represent characters in Go. + + The function then iterates over each character in the rune slice. For each character, the function checks + + if the character is a lowercase letter and if shifting it will still be within the lowercase range. + + If the character is a lowercase letter and shifting it will still be within the lowercase range, the + function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. + + The function then updates the character in the rune slice. + + The function then converts the resulting rune slice back to a string and returns it. + + The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function iterates over each character in the input string, performing a constant amount + of work for each character. + + The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function creates a new rune slice to store the encrypted string. The rune slice is the + same size as the input string, so the space complexity is O(n). +*/ +// Java + +import java.util.Scanner; + +public class CaesarCipherEncryptor { + + public static String encrypt(String str, int key) { + // Calculate the shift amount and offset value + int shift = key % 26; + int offset = 26; + + // Convert the input string to a char array + char[] chars = str.toCharArray(); + + // Iterate over each character in the char array + for (int i = 0; i < chars.length; i++) { + // If the character is a lowercase letter and shifting it will still be within the lowercase range + if (chars[i] >= 'a' && chars[i] + shift <= 'z') { + chars[i] += shift; + } else { + // If the character is outside of the lowercase range after shifting, wrap it around + chars[i] += shift - offset; + } + } + + // Convert the resulting char array back to a string and return it + return new String(chars); + } + + public static void main(String[] args) { + // Get the input string and key from the user + Scanner scanner = new Scanner(System.in); + System.out.println("Enter a string: "); + String str = scanner.nextLine(); + + System.out.println("Enter a key: "); + int key = scanner.nextInt(); + + // Encrypt the string using the Caesar cipher + String encrypted_str = encrypt(str, key); + + // Print the encrypted string + System.out.println("Encrypted string: " + encrypted_str); + } +} diff --git a/Arrays/ceaser_cipher.js b/Arrays/ceaser_cipher.js new file mode 100644 index 00000000..922c4075 --- /dev/null +++ b/Arrays/ceaser_cipher.js @@ -0,0 +1,73 @@ +/* + Given a non-empty string of lowercase letters and a non-negative integer + representing a key, write a function that returns a new string obtained by + shifting every letter in the input string by k positions in the alphabet, + where k is the key. + + Note that letters should "wrap" around the alphabet; in other words, the + letter z shifted by one returns the letter a + + Sample Input : abz key: 3 + Output: dec + + Explanation: + + The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string + obtained by shifting each character of the input string by the key number of positions to the right in + the alphabet, wrapping around if necessary. + + The function first calculates the shift amount and offset value. The shift amount is calculated by taking + the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset + value is calculated by taking 26, which is the number of letters in the alphabet. + + The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single + character in a programming language. The rune type is used to represent characters in Go. + + The function then iterates over each character in the rune slice. For each character, the function checks + + if the character is a lowercase letter and if shifting it will still be within the lowercase range. + + If the character is a lowercase letter and shifting it will still be within the lowercase range, the + function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. + + The function then updates the character in the rune slice. + + The function then converts the resulting rune slice back to a string and returns it. + + The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function iterates over each character in the input string, performing a constant amount + of work for each character. + + The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function creates a new rune slice to store the encrypted string. The rune slice is the + same size as the input string, so the space complexity is O(n). +*/ +function caesarCipherEncryptor(str, key) { + // Calculate the shift amount and offset value + let shift = key % 26; + let offset = 26; + + // Convert the input string to a lowercase string + str = str.toLowerCase(); + + // Iterate over each character in the string + for (let i = 0; i < str.length; i++) { + // If the character is a letter and shifting it will still be within the lowercase range + if (str[i] >= "a" && str[i] + shift <= "z") { + str[i] += shift; + } else { + // If the character is outside of the lowercase range after shifting, wrap it around + str[i] += shift - offset; + } + } + + // Return the encrypted string + return str; +} + +// Example usage +let str = "Hello, world!"; +let key = 3; + +let encryptedStr = caesarCipherEncryptor(str, key); +console.log(encryptedStr); // Output: "Khoor, zloor!" diff --git a/Arrays/ceaser_cipher.py b/Arrays/ceaser_cipher.py new file mode 100644 index 00000000..9dd07531 --- /dev/null +++ b/Arrays/ceaser_cipher.py @@ -0,0 +1,66 @@ +''' + Given a non-empty string of lowercase letters and a non-negative integer + representing a key, write a function that returns a new string obtained by + shifting every letter in the input string by k positions in the alphabet, + where k is the key. + + Note that letters should "wrap" around the alphabet; in other words, the + letter z shifted by one returns the letter a + + Sample Input : abz key: 3 + Output: dec + + Explanation: + + The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string + obtained by shifting each character of the input string by the key number of positions to the right in + the alphabet, wrapping around if necessary. + + The function first calculates the shift amount and offset value. The shift amount is calculated by taking + the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset + value is calculated by taking 26, which is the number of letters in the alphabet. + + The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single + character in a programming language. The rune type is used to represent characters in Go. + + The function then iterates over each character in the rune slice. For each character, the function checks + + if the character is a lowercase letter and if shifting it will still be within the lowercase range. + + If the character is a lowercase letter and shifting it will still be within the lowercase range, the + function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. + + The function then updates the character in the rune slice. + + The function then converts the resulting rune slice back to a string and returns it. + + The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function iterates over each character in the input string, performing a constant amount + of work for each character. + + The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. + This is because the function creates a new rune slice to store the encrypted string. The rune slice is the + same size as the input string, so the space complexity is O(n). +''' +# Python + +def caesar_cipher_encryptor(str, key): + # Calculate the shift amount and offset value + shift = key % 26 + offset = 26 + + # Convert the input string to a list of characters + chars = list(str) + + # Iterate over each character in the list + for i in range(len(chars)): + # If the character is a lowercase letter and shifting it will still be within the lowercase range + if chars[i] >= 'a' and chars[i] + shift <= 'z': + chars[i] += shift + else: + # If the character is outside of the lowercase range after shifting, wrap it around + chars[i] += shift - offset + + # Convert the resulting list of characters back to a string and return it + return ''.join(chars) + diff --git a/Arrays/christmas_trees.java b/Arrays/christmas_trees.java deleted file mode 100644 index e1bb8991..00000000 --- a/Arrays/christmas_trees.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * You are given an array A consisting of heights of Christmas trees and an array B of the same size consisting of the cost of each of the trees (Bi is the cost of tree Ai, where 1 ≤ i ≤ size(A)), and you are supposed to choose 3 trees (let's say, indices p, q, and r), such that Ap < Aq < Ar, where p < q < r. - * The cost of these trees is Bp + Bq + Br. - * - * You are to choose 3 trees such that their total cost is minimum. Return that cost. - * - * If it is not possible to choose 3 such trees return -1. - * - * - * - * Problem Constraints - * 1 <= A[i], B[i] <= 109 - * 3 <= size(A) = size(B) <= 3000 - * - * - * - * Input Format - * First argument is an integer array A. - * Second argument is an integer array B. - * - * - * - * Output Format - * Return an integer denoting the minimum cost of choosing 3 trees whose heights are strictly in increasing order, if not possible, -1. - * - * - * - * Example Input - * Input 1: - * - * A = [1, 3, 5] - * B = [1, 2, 3] - * Input 2: - * - * A = [1, 6, 4, 2, 6, 9] - * B = [2, 5, 7, 3, 2, 7] - * - * - * Example Output - * Output 1: - * - * 6 - * Output 2: - * - * 7 - * - * - * Example Explanation - * Explanation 1: - * - * We can choose the trees with indices 1, 2 and 3, and the cost is 1 + 2 + 3 = 6. - * Explanation 2: - * - * We can choose the trees with indices 1, 4 and 5, and the cost is 2 + 3 + 2 = 7. - * This is the minimum cost that we can get. - */ - -package InterviewProblems; - -public class ChristmasTrees { - public static void main(String[] args) { - int[] a = {1, 6, 4, 2, 6, 9}; - int[] b = {2, 5, 7, 3, 2, 7}; - - int ans = solve(a, b); - System.out.println(ans); - } - - public static int solve(int[] a, int[] b) { - // O(N^2) time | O(1) space - int ans = Integer.MAX_VALUE; - int len = a.length; - for (int j = 1; j < len; j++) { - int tempSum = b[j], iVal = Integer.MAX_VALUE, kVal = Integer.MAX_VALUE; - for (int i = j - 1; i > -1; i--) { - if (a[i] < a[j] && b[i] < iVal) iVal = b[i]; - } - for (int k = j + 1; k < len; k++) { - if (a[k] > a[j] && b[k] < kVal) kVal = b[k]; - } - if (iVal != Integer.MAX_VALUE && kVal != Integer.MAX_VALUE) { - tempSum = tempSum + iVal + kVal; - ans = Math.min(tempSum, ans); - } - } - return (ans == Integer.MAX_VALUE) ? -1 : ans; - } -} diff --git a/Arrays/container_with_most_water.java b/Arrays/container_with_most_water.java deleted file mode 100644 index fa6e57b5..00000000 --- a/Arrays/container_with_most_water.java +++ /dev/null @@ -1,53 +0,0 @@ -/*You are given an integer array height of length n. - There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]). - -Find two lines that together with the x-axis form a container, such that the container contains the most water. - -Return the maximum amount of water a container can store. -Example 1: - - -Input: height = [1,8,6,2,5,4,8,3,7] -Output: 49 -Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. - In this case, the max area of water the container can contain is 49. - - -Example 2: - -Input: height = [1,1] -Output: 1 - - -Constraints: - -n == height.length -2 <= n <= 105 -0 <= height[i] <= 104 -*/ - -class Solution { - public int maxArea(int[] height) { - int left=0,right=height.length-1; - int maxArea=Integer.MIN_VALUE; - while(left<=right) - { - int minLine=Math.min(height[left],height[right]); - maxArea=Math.max(maxArea,minLine*(right-left)); - if(height[left] < height[right]) - left++; - else - right--; - - } - return maxArea; - - } -} -class ContainerWithMostWater { - public static void main(String[] args) { - Solution s=new Solution(); - int height[] = {1,8,6,2,5,4,8,3,7}; - System.out.println(s.maxArea(height)); - } -} \ No newline at end of file diff --git a/Arrays/count_increasing_pairs.java b/Arrays/count_increasing_pairs.java deleted file mode 100644 index 367e2880..00000000 --- a/Arrays/count_increasing_pairs.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * You are given an array A of N elements. Find the number of triplets i,j and k such that i -1; leftIdx--) { - int leftNum = array[leftIdx]; - if (leftNum < midNum) leftCount++; - } - int rightCount = 0; - for (int rightIdx = midIdx + 1; rightIdx < len; rightIdx++) { - int rightNum = array[rightIdx]; - if (rightNum > midNum) rightCount++; - } - int currentPairs = leftCount * rightCount; - totalPairs += currentPairs; - } - return totalPairs; - } - public static int solveUsingBruteForce(int[] array) { - // O(N^3) time | O(1) space - int ans = 0; - int len = array.length; - for (int leftIdx = 0; leftIdx < len; leftIdx++) { - int lefNum = array[leftIdx]; - for (int midIdx = leftIdx + 1; midIdx < len; midIdx++) { - int middleNum = array[midIdx]; - if (lefNum >= middleNum) continue; - for (int rightIdx = midIdx + 1; rightIdx < len; rightIdx++) { - int rightNum = array[rightIdx]; - if (middleNum < rightNum) { - ans++; - } - } - } - } - return ans; - } -} diff --git a/Arrays/count_matches.cpp b/Arrays/count_matches.cpp deleted file mode 100644 index 1ede90b7..00000000 --- a/Arrays/count_matches.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* -You are given an integer n, the number of teams in a tournament that has strange rules: - -If the current number of teams is even, each team gets paired with another team. A total of n / 2 matches are played, and n / 2 teams advance to the next round. -If the current number of teams is odd, one team randomly advances in the tournament, and the rest gets paired. A total of (n - 1) / 2 matches are played, and (n - 1) / 2 + 1 teams advance to the next round. -Return the number of matches played in the tournament until a winner is decided. - - - -Example 1: - -Input: n = 7 -Output: 6 -Explanation: Details of the tournament: -- 1st Round: Teams = 7, Matches = 3, and 4 teams advance. -- 2nd Round: Teams = 4, Matches = 2, and 2 teams advance. -- 3rd Round: Teams = 2, Matches = 1, and 1 team is declared the winner. -Total number of matches = 3 + 2 + 1 = 6. -Example 2: - -Input: n = 14 -Output: 13 -Explanation: Details of the tournament: -- 1st Round: Teams = 14, Matches = 7, and 7 teams advance. -- 2nd Round: Teams = 7, Matches = 3, and 4 teams advance. -- 3rd Round: Teams = 4, Matches = 2, and 2 teams advance. -- 4th Round: Teams = 2, Matches = 1, and 1 team is declared the winner. -Total number of matches = 7 + 3 + 2 + 1 = 13. - - -Constraints: - -1 <= n <= 200 -*/ -class Solution { -public: - int numberOfMatches(int n) { - int res = 0; - while(n > 1){ - if(n % 2 == 0){ - n = n / 2; - res += n; - } - if(n % 2 == 1){ - n = n / 2; - res += n; - n++; - } - } - return res; - } -}; \ No newline at end of file diff --git a/Arrays/counting_subarrays.java b/Arrays/counting_subarrays.java deleted file mode 100644 index fbd442c3..00000000 --- a/Arrays/counting_subarrays.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Given an array A of N non-negative numbers and a non-negative number B, - * you need to find the number of subarrays in A with a sum less than B. - * We may assume that there is no overflow. - * - * - * - * Problem Constraints - * 1 <= N <= 103 - * - * 1 <= A[i] <= 1000 - * - * 1 <= B <= 107 - * - * - * - * Input Format - * First argument is an integer array A. - * - * Second argument is an integer B. - * - * - * - * Output Format - * Return an integer denoting the number of subarrays in A having sum less than B. - * - * - * - * Example Input - * Input 1: - * - * A = [2, 5, 6] - * B = 10 - * Input 2: - * - * A = [1, 11, 2, 3, 15] - * B = 10 - * - * - * Example Output - * Output 1: - * - * 4 - * Output 2: - * - * 4 - * - * - * Example Explanation - * Explanation 1: - * - * The subarrays with sum less than B are {2}, {5}, {6} and {2, 5}, - * Explanation 2: - * - * The subarrays with sum less than B are {1}, {2}, {3} and {2, 3} - */ -public class CountingSubArrays { - public static void main(String[] args) { - final long startTime = System.currentTimeMillis(); - final long beforeUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - final long endTime = System.currentTimeMillis(); - final long afterUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - final long actualMemUsed = afterUsedMem-beforeUsedMem; - - int[] array = {1, 11, 2, 3, 15}; - int b = 10; - int ans = solve(array, b); - System.out.println(ans); - - - System.out.println("Runtime " + (endTime - startTime) + " ms"); - System.out.println("Space " + actualMemUsed + " B"); - } - public static int solve(int[] array, int b) { - // O(n^2) time | O(n) space - int ans = 0; - int len = array.length; - int[] prefixSum = new int[len]; - prefixSum[0] = array[0]; - - for (int i = 1; i < len; i++) prefixSum[i] = prefixSum[i - 1] + array[i]; - - for(int i = 0; i < len; i++) { - for (int j = i; j < len; j++) { - int currentSubArraySum = prefixSum[j]; - if (i > 0) currentSubArraySum -= prefixSum[i - 1]; - - if (currentSubArraySum < b) ans++; - } - } - return ans; - } - -} diff --git a/Arrays/diagonal_sum.java b/Arrays/diagonal_sum.java deleted file mode 100644 index 8826cd91..00000000 --- a/Arrays/diagonal_sum.java +++ /dev/null @@ -1,71 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * You are given a N X N integer matrix. You have to find the sum of all the main diagonal elements of A. - * - * Main diagonal of a matrix A is a collection of elements A[i, j] such that i = j. - * - * - * Problem Constraints - * 1 <= N <= 103 - * - * -1000 <= A[i][j] <= 1000 - * - * - * - * Input Format - * There are 1 lines in the input. First 2 integers R, C are the number of rows and columns. Then R * C integers follow corresponding to the rowwise numbers in the 2D array A. - * - * - * - * Output Format - * Return an integer denoting the sum of main diagonal elements. - * - * - * - * Example Input - * Input 1: - * - * 3 3 1 -2 -3 -4 5 -6 -7 -8 9 - * Input 2: - * - * 2 2 3 2 2 3 - * - * - * Example Output - * Output 1: - * - * 15 - * Output 2: - * - * 6 - * - * - * Example Explanation - * Explanation 1: - * - * A[1][1] + A[2][2] + A[3][3] = 1 + 5 + 9 = 15 - * Explanation 2: - * - * A[1][1] + A[2][2] = 3 + 3 = 6 - */ -public class DiagonalSum { - public static void main(String[] args) { - List> array = new ArrayList<>(); - array.add(Arrays.asList(1, -2, -3)); - array.add(Arrays.asList(-4, 5, -6)); - array.add(Arrays.asList(-7 ,-8, 9)); - - System.out.println(solve(array)); - } - public static int solve(final List> A) { - // O(row) time | O(1) space - int ans = 0; - for (int i = 0; i < A.size(); i++) { - ans += A.get(i).get(i); - } - return ans; - } -} diff --git a/Arrays/dutch_national_flag.cpp b/Arrays/dutch_national_flag.cpp new file mode 100644 index 00000000..47aaa46f --- /dev/null +++ b/Arrays/dutch_national_flag.cpp @@ -0,0 +1,78 @@ +/* + Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. + + We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. + + Input: nums = [2,0,2,1,1,0] + Output: [0,0,1,1,2,2] + + Explanation: + + + The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements + with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. + + The algorithm iterates through the array using the `low` pointer. Here's how it works: + + 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element + with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the + next element to process. + + 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments + the `low` pointer and moves to the next element to process. + + 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element + with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. + + The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have + been processed. + + The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. + It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. + + Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place + to achieve the sorted order. + + Time complexity: O(n) + Space complexity: O(1) +*/ + + + +#include +using namespace std; + +void sortColors(vector &nums) +{ + // created 3 variables start , low and end which are pointing start and low which are pointing to first index , end is pointing to last index . + + int start = 0, low = 0, end = nums.size() - 1; + while (low <= end) + { + if (nums[low] == 0) // checking if element of low is 0 . If yes then swap to start and low . + { + swap(nums[low], nums[start]); + start++, low++; + } + else if (nums[low] == 1) // checking if element at low index is 1 , If yes then increase the index by 1 . + { + low++; + } + else // else swap the element of low index to end . + { + swap(nums[low], nums[end]); + end--; + } + } +} +int main() +{ + vector nums{2, 0, 2, 1, 1, 0}; + sortColors(nums); + + // Printing array's elements .. + for (auto i : nums) + { + cout << i << " "; + } +} diff --git a/Arrays/dutch_national_flag.go b/Arrays/dutch_national_flag.go new file mode 100644 index 00000000..bd10ccd7 --- /dev/null +++ b/Arrays/dutch_national_flag.go @@ -0,0 +1,68 @@ +/* + Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. + + We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. + + Input: nums = [2,0,2,1,1,0] + Output: [0,0,1,1,2,2] + + Explanation: + + The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements + with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. + + The algorithm iterates through the array using the `low` pointer. Here's how it works: + + 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element + with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the + next element to process. + + 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments + the `low` pointer and moves to the next element to process. + + 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element + with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. + + The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have + been processed. + + The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. + It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. + + Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place + to achieve the sorted order. + + Time complexity: O(n) + Space complexity: O(1) +*/ +package main + +import "fmt" + +func sortColors(nums []int) { + start := 0 + low := 0 + end := len(nums) - 1 + + for low <= end { + if nums[low] == 0 { + // Swap the element at low with the element at start + nums[low], nums[start] = nums[start], nums[low] + start++ + low++ + } else if nums[low] == 1 { + // Move to the next element + low++ + } else { + // Swap the element at low with the element at end + nums[low], nums[end] = nums[end], nums[low] + end-- + } + } +} + +func main() { + nums := []int{2, 0, 2, 1, 1, 0} + sortColors(nums) + fmt.Println(nums) +} diff --git a/Arrays/dutch_national_flag.java b/Arrays/dutch_national_flag.java new file mode 100644 index 00000000..04a5a4b3 --- /dev/null +++ b/Arrays/dutch_national_flag.java @@ -0,0 +1,72 @@ +/* + Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. + + We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. + + Input: nums = [2,0,2,1,1,0] + Output: [0,0,1,1,2,2] + + Explanation: + + The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements + with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. + + The algorithm iterates through the array using the `low` pointer. Here's how it works: + + 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element + with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the + next element to process. + + 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments + the `low` pointer and moves to the next element to process. + + 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element + with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. + + The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have + been processed. + + The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. + It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. + + Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place + to achieve the sorted order. + + Time complexity: O(n) + Space complexity: O(1) +*/ +public class Main { + public static void sortColors(int[] nums) { + int start = 0; + int low = 0; + int end = nums.length - 1; + + while (low <= end) { + if (nums[low] == 0) { + // Swap the element at low with the element at start + int temp = nums[low]; + nums[low] = nums[start]; + nums[start] = temp; + start++; + low++; + } else if (nums[low] == 1) { + // Move to the next element + low++; + } else { + // Swap the element at low with the element at end + int temp = nums[low]; + nums[low] = nums[end]; + nums[end] = temp; + end--; + } + } + } + + public static void main(String[] args) { + int[] nums = {2, 0, 2, 1, 1, 0}; + sortColors(nums); + for (int num : nums) { + System.out.print(num + " "); + } + } +} diff --git a/Arrays/dutch_national_flag.js b/Arrays/dutch_national_flag.js new file mode 100644 index 00000000..b153a626 --- /dev/null +++ b/Arrays/dutch_national_flag.js @@ -0,0 +1,63 @@ +/* + Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. + + We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. + + Input: nums = [2,0,2,1,1,0] + Output: [0,0,1,1,2,2] + + Explanation: + + The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements + with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. + + The algorithm iterates through the array using the `low` pointer. Here's how it works: + + 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element + with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the + next element to process. + + 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments + the `low` pointer and moves to the next element to process. + + 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element + with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. + + The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have + been processed. + + The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. + It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. + + Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place + to achieve the sorted order. + + Time complexity: O(n) + Space complexity: O(1) +*/ +function sortColors(nums) { + let start = 0; + let low = 0; + let end = nums.length - 1; + + while (low <= end) { + if (nums[low] === 0) { + // Swap the element at low with the element at start + [nums[low], nums[start]] = [nums[start], nums[low]]; + start++; + low++; + } else if (nums[low] === 1) { + // Move to the next element + low++; + } else { + // Swap the element at low with the element at end + [nums[low], nums[end]] = [nums[end], nums[low]]; + end--; + } + } +} + +// Example usage: +const nums = [2, 0, 2, 1, 1, 0]; +sortColors(nums); +console.log(nums); diff --git a/Arrays/dutch_national_flag.py b/Arrays/dutch_national_flag.py new file mode 100644 index 00000000..405e264b --- /dev/null +++ b/Arrays/dutch_national_flag.py @@ -0,0 +1,60 @@ +''' + Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. + + We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. + + Input: nums = [2,0,2,1,1,0] + Output: [0,0,1,1,2,2] + + Explanation: + + The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements + with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. + + The algorithm iterates through the array using the `low` pointer. Here's how it works: + + 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element + with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the + next element to process. + + 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments + the `low` pointer and moves to the next element to process. + + 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element + with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. + + The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have + been processed. + + The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. + It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. + + Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place + to achieve the sorted order. + + Time complexity: O(n) + Space complexity: O(1) +''' +def sortColors(nums): + start = 0 + low = 0 + end = len(nums) - 1 + + while low <= end: + if nums[low] == 0: + # Swap the element at low with the element at start + nums[low], nums[start] = nums[start], nums[low] + start += 1 + low += 1 + elif nums[low] == 1: + # Move to the next element + low += 1 + else: + # Swap the element at low with the element at end + nums[low], nums[end] = nums[end], nums[low] + end -= 1 + +# Example usage: +nums = [2, 0, 2, 1, 1, 0] +sortColors(nums) +print(nums) diff --git a/Arrays/equilibrium_index.java b/Arrays/equilibrium_index.java deleted file mode 100644 index 226dccad..00000000 --- a/Arrays/equilibrium_index.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * You are given an array A of integers of size N. - * - * Your task is to find the equilibrium index of the given array - * - * The equilibrium index of an array is an index such that the sum of elements at lower indexes is equal to the sum of elements at higher indexes. - * - * NOTE: - * - * Array indexing starts from 0. - * If there is no equilibrium index then return -1. - * If there are more than one equilibrium indexes then return the minimum index. - * - * - * - * Problem Constraints - * 1 <= N <= 105 - * -105 <= A[i] <= 105 - * - * - * Input Format - * First arugment is an array A . - * - * - * Output Format - * Return the equilibrium index of the given array. If no such index is found then return -1. - * - * - * Example Input - * Input 1: - * A=[-7, 1, 5, 2, -4, 3, 0] - * Input 2: - * - * A=[1,2,3] - * - * - * Example Output - * Output 1: - * 3 - * Output 2: - * - * -1 - * - * - * Example Explanation - * Explanation 1: - * 3 is an equilibrium index, because: - * A[0] + A[1] + A[2] = A[4] + A[5] + A[6] - * Explanation 1: - * - * There is no such index. - */ - -package InterviewProblems; - -import java.util.stream.IntStream; - -public class EquilibriumIndex { - public static void main(String[] args) { - int[] arr = {-7, 1, 5, 2, -4, 3, 0}; - int ans = solve(arr); - System.out.println(ans); - } - public static int solve(int[] arr) { - // O(N) | O(1) space - int len = arr.length; - int rightSum = IntStream.of(arr).sum(); - int leftSum = 0; - for (int i = 0; i < len; i++) { - int currentNum = arr[i]; - rightSum -= currentNum; - if (leftSum == rightSum) return i; - leftSum += currentNum; - - } - // Without prefix sum - // Using Prefix sum | O(N) time | O(N) space - -// int len = arr.length; -// int[] prefixSum = new int[len]; -// prefixSum[0] = arr[0]; -// for (int i = 1; i < len; i++) -// prefixSum[i] = prefixSum[i-1] + arr[i]; - - // find equilibrium index -// if (prefixSum[len - 1] - prefixSum[0] == 0) return 0; -// for (int i = 1; i < len; i++) { -// if (prefixSum[i - 1] == prefixSum[ len - 1] - prefixSum[i]) return i; -// } - return -1; - } -} diff --git a/Arrays/even_indices_sum.java b/Arrays/even_indices_sum.java deleted file mode 100644 index 40c11ecd..00000000 --- a/Arrays/even_indices_sum.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * You are given an array A of length N and Q queries given by the 2D array B of size Q*2. Each query consists of two integers B[i][0] and B[i][1]. - * For every query, the task is to calculate the sum of all even indices in the range A[B[i][0]…B[i][1]]. - * - * Note : Use 0-based indexing - * - * - * Problem Constraints - * 1 <= N <= 105 - * 1 <= Q <= 105 - * 1 <= A[i] <= 100 - * 0 <= B[i][0] <= B[i][1] < N - * - * - * Input Format - * First argument A is an array of integers. - * Second argument B is a 2D array of integers. - * - * - * Output Format - * Return an array of integers. - * - * - * Example Input - * Input 1: - * A = [1, 2, 3, 4, 5] - * B = [ [0,2] - * [1,4] ] - * Input 2: - * A = [2, 1, 8, 3, 9] - * B = [ [0,3] - * [2,4] ] - * - * - * Example Output - * Output 1: - * [4, 8] - * Output 2: - * [10, 17] - * - * - * Example Explanation - * For Input 1: - * The subarray for the first query is [1, 2, 3] whose sum of even indices is 4. - * The subarray for the second query is [2, 3, 4, 5] whose sum of even indices is 8. - * For Input 2: - * The subarray for the first query is [2, 1, 8, 3] whose sum of even indices is 10. - * The subarray for the second query is [8, 3, 9] whose sum of even indices is 17. - */ -package InterviewProblems; - -import java.util.Arrays; - -public class evenIndicesSum { - public static void main(String[] args) { - int[] arr = {2, 1, 8, 3, 9}; - int[][] b = { {0, 3}, {2, 4}}; - int[] ans = solve(arr, b); - System.out.println(Arrays.toString(ans)); - } - public static int[] solve(int[] arr, int[][] b) { - // (Q*N) time | (N) space where Q is no.of queries and N is length of arr. - int len = arr.length; - int[] evenPrefixSum = new int[len]; - int[] ans = new int[b.length]; - - //even prefix - evenPrefixSum[0] = arr[0]; - for (int i = 1; i < len; i++) { - int currentNum = arr[i]; - evenPrefixSum[i] = i % 2 == 0 ? - evenPrefixSum[i - 1] + - currentNum : evenPrefixSum[i - 1]; - } - for (int i = 0; i < b.length; i++) { - int[] currentQuery = b[i]; - int startIdx = currentQuery[0]; - int endIdx = currentQuery[1]; - ans[i] = startIdx == 0 ? evenPrefixSum[endIdx] : - evenPrefixSum[endIdx] - evenPrefixSum[startIdx - 1]; - } - return ans; - } -} diff --git a/Arrays/find_three_largest_integers.js b/Arrays/find_three_largest_integers.js new file mode 100644 index 00000000..54cedbbe --- /dev/null +++ b/Arrays/find_three_largest_integers.js @@ -0,0 +1,83 @@ +/* + Write a function that takes in an array of at least three integers and, + without sorting the input array, returns a sorted array of the three largest + integers in the input array. + + Explanation: + + This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and + returns an array of the three largest integers in the input array. + + The `triplets` array is initialized with three smallest possible values. Then, the function iterates through + the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with + the current number if it is larger than one of the values in the array. + + The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater + than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the + `triplets` array with the current number at the third index. + + If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` + array, it checks if `num` is greater than the first value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the first index. + + The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the + `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches + the `idx` index. Then it updates the value at the `idx` index with the current number `num`. + + Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array +*/ +function findThreeLargestNumbers(array) { + // Initialize an array to hold the three largest numbers, starting with negative infinity + let triplets = [-Infinity, -Infinity, -Infinity]; + + // Iterate through each number in the input array + for (let num of array) { + // Call the updateLargest function to determine if the number should be included in the triplet + updateLargest(triplets, num); + } + + // Return the array containing the three largest numbers + return triplets; +} + +function updateLargest(triplets, num) { + // If the number is larger than the third-largest element in the triplet + if (num > triplets[2]) { + // Shift the other elements to make room and add the number as the new third-largest element + shiftAndUpdate(triplets, num, 2); + } + // Otherwise, if the number is larger than the second-largest element + else if (num > triplets[1]) { + // Shift and update the triplet accordingly + shiftAndUpdate(triplets, num, 1); + } + // Otherwise, if the number is larger than the first-largest element + else if (num > triplets[0]) { + // Shift and update the triplet accordingly + shiftAndUpdate(triplets, num, 0); + } +} + +function shiftAndUpdate(triplets, num, idx) { + // Iterate through the elements of the triplet + for (let i = 0; i <= idx; i++) { + // If the loop reaches the specified index, add the new number to the triplet + if (i === idx) { + triplets[i] = num; + } + // Otherwise, shift the elements to the right + else { + triplets[i] = triplets[i + 1]; + } + } +} + +// Test the findThreeLargestNumbers function +const array = [1, 5, 2, 9, 10, 3]; +const result = findThreeLargestNumbers(array); +console.log(result); diff --git a/Arrays/find_three_largest_number.cpp b/Arrays/find_three_largest_number.cpp new file mode 100644 index 00000000..8fe6dc75 --- /dev/null +++ b/Arrays/find_three_largest_number.cpp @@ -0,0 +1,101 @@ +/* + Write a function that takes in an array of at least three integers and, + without sorting the input array, returns a sorted array of the three largest + integers in the input array. +*/ +/* Explanation + This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and + returns an array of the three largest integers in the input array. + + The `triplets` array is initialized with three smallest possible values. Then, the function iterates through + the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with + the current number if it is larger than one of the values in the array. + + The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater + than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the + `triplets` array with the current number at the third index. + + If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` + array, it checks if `num` is greater than the first value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the first index. + + The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the + `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches + the `idx` index. Then it updates the value at the `idx` index with the current number `num`. + + Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array +*/ +#include +#include +#include +#include + +using namespace std; + +// Function to update the triplet if the input number is larger than any of its elements. +void updateLargest(vector& triplets, int num); + +// Function to shift the elements of the triplet to make room for a new number and add the number to the specified index. +void shiftAndUpdate(vector& triplets, int num, int idx); + +// Function to find the three largest integers in the input array in descending order. +vector findThreeLargestNumbers(vector& array); + +int main() { + // Create an input array. + vector array = {141, 1, 17, -7, -17, -27, 18, 541, 8, 7, 7}; + + // Find the three largest integers in the input array. + vector result = findThreeLargestNumbers(array); + + // Output the three largest integers in descending order. + for (int num : result) { + cout << num << " "; + } + cout << endl; + return 0; +} + +vector findThreeLargestNumbers(vector& array) { + // Initialize a vector to hold the three largest integers, starting with negative infinity. + vector triplets = {INT_MIN, INT_MIN, INT_MIN}; + + // Iterate over each number in the input array and call the updateLargest function to determine if it should be included in the triplet. + for (int num : array) { + updateLargest(triplets, num); + } + + // Return the vector containing the three largest integers in descending order. + return triplets; +} + +void updateLargest(vector& triplets, int num) { + // If the number is larger than the third-largest element in the triplet, shift the other elements to make room and add the number. + if (num > triplets[2]) { + shiftAndUpdate(triplets, num, 2); + // Otherwise, if the number is larger than the second-largest element, shift and update the triplet accordingly. + } else if (num > triplets[1]) { + shiftAndUpdate(triplets, num, 1); + // Otherwise, if the number is larger than the first-largest element, shift and update the triplet accordingly. + } else if (num > triplets[0]) { + shiftAndUpdate(triplets, num, 0); + } +} + +void shiftAndUpdate(vector& triplets, int num, int idx) { + // Shift the elements of the triplet to the right starting at the specified index, and add the new number to the specified index. + for (int i = 0; i < idx + 1; i++) { + // If the loop reaches the specified index, add the new number to the triplet. + if (i == idx) { + triplets[i] = num; + // Otherwise, shift the elements to the right. + } else { + triplets[i] = triplets[i + 1]; + } + } +} diff --git a/Arrays/find_three_largest_numbers.go b/Arrays/find_three_largest_numbers.go new file mode 100644 index 00000000..9491bb36 --- /dev/null +++ b/Arrays/find_three_largest_numbers.go @@ -0,0 +1,74 @@ +/* + Write a function that takes in an array of at least three integers and, + without sorting the input array, returns a sorted array of the three largest + integers in the input array. + + Explanation: + + This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and + returns an array of the three largest integers in the input array. + + The `triplets` array is initialized with three smallest possible values. Then, the function iterates through + the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with + the current number if it is larger than one of the values in the array. + + The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater + than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the + `triplets` array with the current number at the third index. + + If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` + array, it checks if `num` is greater than the first value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the first index. + + The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the + `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches + the `idx` index. Then it updates the value at the `idx` index with the current number `num`. + + Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array +*/ +package main + +import "math" + +// FindThreeLargestNumbers returns the three largest integers in the input array in descending order. +func FindThreeLargestNumbers(array []int) []int { + // Initialize a slice to hold the three largest integers, starting with negative infinity. + triplets := []int{math.MinInt32, math.MinInt32, math.MinInt32} + for _, num := range array { + // For each number in the array, call the updateLargest function to determine if it should be included in the triplet. + updateLargest(triplets, num) + } + return triplets +} + +// updateLargest updates the triplet if the input number is larger than any of its elements. +func updateLargest(triplets []int, num int) { + // If the number is larger than the third-largest element in the triplet, shift the other elements to make room and add the number. + if num > triplets[2] { + shiftAndUpdate(triplets, num, 2) + // Otherwise, if the number is larger than the second-largest element, shift and update the triplet accordingly. + } else if num > triplets[1] { + shiftAndUpdate(triplets, num, 1) + // Otherwise, if the number is larger than the first-largest element, shift and update the triplet accordingly. + } else if num > triplets[0] { + shiftAndUpdate(triplets, num, 0) + } +} + +// shiftAndUpdate shifts the elements of the triplet to make room for a new number and adds the number to the specified index. +func shiftAndUpdate(triplets []int, num int, idx int) { + for i := 0; i < idx+1; i++ { + // If the loop reaches the specified index, add the new number to the triplet. + if i == idx { + triplets[i] = num + // Otherwise, shift the elements to the right. + } else { + triplets[i] = triplets[i+1] + } + } +} diff --git a/Arrays/find_three_largest_numbers.java b/Arrays/find_three_largest_numbers.java new file mode 100644 index 00000000..edf485ef --- /dev/null +++ b/Arrays/find_three_largest_numbers.java @@ -0,0 +1,93 @@ +/* + Write a function that takes in an array of at least three integers and, + without sorting the input array, returns a sorted array of the three largest + integers in the input array. + + Explanation: + + This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and + returns an array of the three largest integers in the input array. + + The `triplets` array is initialized with three smallest possible values. Then, the function iterates through + the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with + the current number if it is larger than one of the values in the array. + + The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater + than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the + `triplets` array with the current number at the third index. + + If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` + array, it checks if `num` is greater than the first value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the first index. + + The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the + `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches + the `idx` index. Then it updates the value at the `idx` index with the current number `num`. + + Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array +*/ + +import java.util.Arrays; + +public class Main { + // Function to find the three largest numbers in the input array + public static int[] findThreeLargestNumbers(int[] array) { + // Initialize an array to hold the three largest numbers, starting with negative infinity + int[] triplets = new int[]{Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE}; + + // Iterate through each number in the input array + for (int num : array) { + // Call the updateLargest function to determine if the number should be included in the triplet + updateLargest(triplets, num); + } + + // Return the array containing the three largest numbers + return triplets; + } + + // Function to update the triplet if the input number is larger than any of its elements + private static void updateLargest(int[] triplets, int num) { + // If the number is larger than the third-largest element in the triplet + if (num > triplets[2]) { + // Shift the other elements to make room and add the number as the new third-largest element + shiftAndUpdate(triplets, num, 2); + } + // Otherwise, if the number is larger than the second-largest element + else if (num > triplets[1]) { + // Shift and update the triplet accordingly + shiftAndUpdate(triplets, num, 1); + } + // Otherwise, if the number is larger than the first-largest element + else if (num > triplets[0]) { + // Shift and update the triplet accordingly + shiftAndUpdate(triplets, num, 0); + } + } + + // Function to shift the elements of the triplet and add the new number to the specified index + private static void shiftAndUpdate(int[] triplets, int num, int idx) { + // Iterate through the elements of the triplet + for (int i = 0; i < idx + 1; i++) { + // If the loop reaches the specified index, add the new number to the triplet + if (i == idx) { + triplets[i] = num; + } + // Otherwise, shift the elements to the right + else { + triplets[i] = triplets[i + 1]; + } + } + } + + // Main function to test the findThreeLargestNumbers function + public static void main(String[] args) { + int[] array = {1, 5, 2, 9, 10, 3}; + int[] result = findThreeLargestNumbers(array); + System.out.println(Arrays.toString(result)); + } +} diff --git a/Arrays/find_three_largest_numbers.py b/Arrays/find_three_largest_numbers.py new file mode 100644 index 00000000..dfc2d9f4 --- /dev/null +++ b/Arrays/find_three_largest_numbers.py @@ -0,0 +1,77 @@ +''' + Write a function that takes in an array of at least three integers and, + without sorting the input array, returns a sorted array of the three largest + integers in the input array. + + Explanation: + + This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and + returns an array of the three largest integers in the input array. + + The `triplets` array is initialized with three smallest possible values. Then, the function iterates through + the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with + the current number if it is larger than one of the values in the array. + + The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater + than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the + `triplets` array with the current number at the third index. + + If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` + array, it checks if `num` is greater than the first value in the `triplets` array. + + If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at + the first index. + + The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the + `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches + the `idx` index. Then it updates the value at the `idx` index with the current number `num`. + + Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array + +''' +def find_three_largest_numbers(array): + # Initialize a list to hold the three largest numbers, starting with negative infinity + triplets = [float('-inf'), float('-inf'), float('-inf')] + + # Iterate through each number in the input array + for num in array: + # Call the update_largest function to determine if the number should be included in the triplet + update_largest(triplets, num) + + # Return the list containing the three largest numbers + return triplets + + +def update_largest(triplets, num): + # If the number is larger than the third-largest element in the triplet + if num > triplets[2]: + # Shift the other elements to make room and add the number as the new third-largest element + shift_and_update(triplets, num, 2) + # Otherwise, if the number is larger than the second-largest element + elif num > triplets[1]: + # Shift and update the triplet accordingly + shift_and_update(triplets, num, 1) + # Otherwise, if the number is larger than the first-largest element + elif num > triplets[0]: + # Shift and update the triplet accordingly + shift_and_update(triplets, num, 0) + + +def shift_and_update(triplets, num, idx): + # Iterate through the elements of the triplet + for i in range(idx + 1): + # If the loop reaches the specified index, add the new number to the triplet + if i == idx: + triplets[i] = num + # Otherwise, shift the elements to the right + else: + triplets[i] = triplets[i + 1] + + +# Test the find_three_largest_numbers function +array = [1, 5, 2, 9, 10, 3] +result = find_three_largest_numbers(array) +print(result) diff --git a/Arrays/first_duplicate_value.cpp b/Arrays/first_duplicate_value.cpp new file mode 100644 index 00000000..3d1f894e --- /dev/null +++ b/Arrays/first_duplicate_value.cpp @@ -0,0 +1,43 @@ +/* + Given an array of integers between 1 and n, inclusive, where n is the length of the array, write a function + that returns the first integer that appears more than once (when the array is read from left to right). + + Sample Input = [2, 1, 5, 2, 3, 3, 4] + Output : 2 + + Please provide O(n) time and O(1) space solution along with O(n) time and O(n) space solution +*/ + +#include +using namespace std; + +class Solution{ +public: + // O(N) time complexity and O(N) Space Complexity Solution + int findDuplicate1(vector& nums) + { + int N=nums.size(); + + // Use Vector Instead of Unordered Set or Map for O(1) extraction time complexity + vector trk(N,false); + + for(int i=0;i& nums) + { + int N=nums.size(); + for(int i=0;i visited = new HashSet<>(); + // iterate through the array + for (int num : nums) { + // check if the current element has already been visited + if (visited.contains(num)) { + // if it has, return the current element + return num; + } + // otherwise, add it to the set of visited elements + visited.add(num); + } + // if no duplicate is found, return -1 + return -1; +} + diff --git a/Arrays/first_duplicate_value.js b/Arrays/first_duplicate_value.js new file mode 100644 index 00000000..b0aa4df7 --- /dev/null +++ b/Arrays/first_duplicate_value.js @@ -0,0 +1,51 @@ +/* + Given an array of integers between 1 and n, inclusive, where n is the length of the array, write a function + that returns the first integer that appears more than once (when the array is read from left to right). + + Sample Input = [2, 1, 5, 2, 3, 3, 4] + Output : 2 + + +*/ + +// O(n) time and O(1) space solution. +// Approach: The approach utilizes the fact that the array contains integers between 1 and n, +// where n is the length of the array. By negating the values at specific indices, +// we can track which numbers have appeared before. If a number has appeared before, +// its corresponding index will have a negative value. +// This allows us to identify the first duplicate encountered during the iteration. +function findFirstDuplicate(nums){ + let n=nums.length; + for(let i=0;i>The fourSum function takes in a list of integers (nums) and a target value (target). +>>The code sorts the nums list in ascending order. +>>It initializes an empty list res to store the resulting quadruplets. +>>Code uses two nested loops to iterate over combinations of four numbers from the nums list. +>>It avoids duplicates by skipping iterations when the current element is the same as the previous element + in both the outer and inner loops. +>>Inside nested loops, code uses two pointers (lo and hi) to find pairs of elements that sum up to the remaining target value. +>>It compares the sum of the four elements with the target value and takes appropriate actions: + +. If sum equals the target, it adds the quadruplet to the result list res and skips any duplicate elements by moving the pointers accordingly. + +. If sum is less than the target, it increments the lo pointer to try larger values. + +. If sum is greater than the target, it decrements the hi pointer to try smaller values. +>>After the nested loops, the function returns the resulting list of quadruplets res. + +''' + from typing import List +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + n = len(nums) + nums.sort() + res = [] + + for i in range(n-3): + # avoid the duplicates while moving i + if i > 0 and nums[i] == nums[i - 1]: + continue + for j in range(i+1, n-2): + # avoid the duplicates while moving j + if j > i + 1 and nums[j] == nums[j - 1]: + continue + lo = j + 1 + hi = n - 1 + while lo < hi: + temp = nums[i] + nums[j] + nums[lo] + nums[hi] + if temp == target: + res += [nums[i], nums[j], nums[lo], nums[hi]], + + # skip duplicates + while lo < hi and nums[lo] == nums[lo + 1]: + lo += 1 + lo += 1 + while lo < hi and nums[hi] == nums[hi - 1]: + hi -= 1 + hi -= 1 + elif temp < target: + lo += 1 + else: + hi -= 1 + return res +# Test case +nums = [1, 0, -1, 0, -2, 2] +target = 0 +solution = Solution() +result = solution.fourSum(nums, target) +print(result) diff --git a/Arrays/good_subarrays.java b/Arrays/good_subarrays.java deleted file mode 100644 index 44baf980..00000000 --- a/Arrays/good_subarrays.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Given an array of integers A, a subarray of an array is said to be good if it fulfills any one of the criteria: - * 1. Length of the subarray is be even, and the sum of all the elements of the subarray must be less than B. - * 2. Length of the subarray is be odd, and the sum of all the elements of the subarray must be greater than B. - * Your task is to find the count of good subarrays in A. - * - * - * Problem Constraints - * 1 <= len(A) <= 103 - * 1 <= A[i] <= 103 - * 1 <= B <= 107 - * - * - * Input Format - * The first argument given is the integer array A. - * The second argument given is an integer B. - * - * - * Output Format - * Return the count of good subarrays in A. - * - * - * Example Input - * Input 1: - * A = [1, 2, 3, 4, 5] - * B = 4 - * Input 2: - * - * A = [13, 16, 16, 15, 9, 16, 2, 7, 6, 17, 3, 9] - * B = 65 - * - * - * Example Output - * Output 1: - * 6 - * Output 2: - * - * 36 - * - * - * Example Explanation - * Explanation 1: - * Even length good subarrays = {1, 2} - * Odd length good subarrays = {1, 2, 3}, {1, 2, 3, 4, 5}, {2, 3, 4}, {3, 4, 5}, {5} - */ - -public class GoodSubArrays { - public static void main(String[] args) { - final long startTime = System.currentTimeMillis(); - final long beforeUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - int[] array = {13, 16, 16, 15, 9, 16, 2, 7, 6, 17, 3, 9}; - int b = 65; - int ans = solve(array, b); - System.out.println(ans); - final long endTime = System.currentTimeMillis(); - final long afterUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - final long actualMemUsed = afterUsedMem-beforeUsedMem; - System.out.println("Runtime " + (endTime - startTime) + " ms"); - System.out.println("Space " + actualMemUsed + " B"); - } - public static int solve(int[] array, int b) { - // O(n^2) time | O(n) space - int len = array.length; - int[] prefixArray = new int[len]; - int ans = 0; - - prefixArray[0] = array[0]; - for (int i = 1; i < len; i++) { // O(n) time | O(n) space for prefix array - int currentNum = array[i]; - prefixArray[i] = prefixArray[i - 1] + currentNum; - } - - for (int i = 0; i < len; i++) { - for (int j = i; j < len; j++) { - int currentSubArraySize = j - i + 1; - int currentSubArraySum; - - if (i == 0) currentSubArraySum = prefixArray[j]; - else currentSubArraySum = prefixArray[j] - prefixArray[i - 1]; - - if (currentSubArraySize % 2 == 0 && currentSubArraySum < b) ans += 1; //for even - if (currentSubArraySize % 2 > 0 && currentSubArraySum > b) ans += 1; //for odd - - } - } - return ans; - } -} diff --git a/Arrays/insert_interval.cpp b/Arrays/insert_interval.cpp new file mode 100644 index 00000000..41fd59f1 --- /dev/null +++ b/Arrays/insert_interval.cpp @@ -0,0 +1,105 @@ +/* + Insert Interval + + In this implementation, the `Interval` struct represents an interval with a start and end value. + The `insert` function takes a sorted list of intervals and a new interval as input and returns a new + list of intervals after merging the new interval with the existing intervals. + + Here's how the `insert` function works: + + 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. + 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the + `result` slice. + 3. It merges intervals that overlap with the new interval by updating the start and end values of the new + interval accordingly. + 4. It adds the merged new interval to the `result` slice. + 5. It adds any remaining intervals from the original list to the `result` slice. + 6. Finally, it returns the `result` slice containing the merged intervals. + + The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. + + In the `main` function, an example input is provided with a list of intervals and a new interval. + The `insert` function is called with these inputs, and the result is printed to the console. + + Time Complexity: + The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to + iterate through each interval in the list to merge and insert the new interval. In the worst case, we may + need to traverse all intervals in the list. + + Space Complexity: + The space complexity is O(n), where n is the number of intervals in the input list. This is because we + create a new result slice to store the merged intervals, which can potentially contain all the intervals + from the input list plus the merged new interval. Therefore, the space required is proportional to the + number of intervals in the input list. + + Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number + of intervals in the input list. +*/ +#include +#include + +using namespace std; + +// Interval represents a closed interval [start, end]. +struct Interval { + int start; + int end; + + Interval(int s, int e) : start(s), end(e) {} +}; + +vector insertInterval(vector& intervals, Interval newInterval) { + vector result; + + // Traverse through each interval in the input list + // and perform the necessary merging and inserting. + for (const auto& interval : intervals) { + // If the current interval ends before the new interval starts, + // add it to the result as it does not overlap. + if (interval.end < newInterval.start) { + result.push_back(interval); + } + // If the current interval starts after the new interval ends, + // add the new interval and update it to the current interval + // as there won't be any more overlap with subsequent intervals. + else if (interval.start > newInterval.end) { + result.push_back(newInterval); + newInterval = interval; + } + // If there is an overlap between the current interval and the new interval, + // merge them by updating the new interval's start and end. + else { + newInterval.start = min(interval.start, newInterval.start); + newInterval.end = max(interval.end, newInterval.end); + } + } + + // Add the final merged or inserted interval to the result. + result.push_back(newInterval); + + return result; +} + +// Utility function to print the intervals. +void printIntervals(const vector& intervals) { + for (const auto& interval : intervals) { + cout << "[" << interval.start << ", " << interval.end << "] "; + } + cout << endl; +} + +int main() { + // Example usage + vector intervals = {Interval(1, 3), Interval(6, 9)}; + Interval newInterval(2, 5); + + cout << "Original intervals: "; + printIntervals(intervals); + + vector mergedIntervals = insertInterval(intervals, newInterval); + + cout << "Merged intervals: "; + printIntervals(mergedIntervals); + + return 0; +} diff --git a/Arrays/insert_interval.go b/Arrays/insert_interval.go new file mode 100644 index 00000000..8fb5282e --- /dev/null +++ b/Arrays/insert_interval.go @@ -0,0 +1,105 @@ +/* + Insert Interval + + In this implementation, the `Interval` struct represents an interval with a start and end value. + The `insert` function takes a sorted list of intervals and a new interval as input and returns a new + list of intervals after merging the new interval with the existing intervals. + + Here's how the `insert` function works: + + 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. + 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the + `result` slice. + 3. It merges intervals that overlap with the new interval by updating the start and end values of the new + interval accordingly. + 4. It adds the merged new interval to the `result` slice. + 5. It adds any remaining intervals from the original list to the `result` slice. + 6. Finally, it returns the `result` slice containing the merged intervals. + + The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. + + In the `main` function, an example input is provided with a list of intervals and a new interval. + The `insert` function is called with these inputs, and the result is printed to the console. + + Time Complexity: + The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to + iterate through each interval in the list to merge and insert the new interval. In the worst case, we may + need to traverse all intervals in the list. + + Space Complexity: + The space complexity is O(n), where n is the number of intervals in the input list. This is because we + create a new result slice to store the merged intervals, which can potentially contain all the intervals + from the input list plus the merged new interval. Therefore, the space required is proportional to the + number of intervals in the input list. + + Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number + of intervals in the input list. +*/ +package main + +import ( + "fmt" +) + +// Interval represents an interval with a start and end value. +type Interval struct { + Start int + End int +} + +// insert merges a new interval into a sorted list of intervals. +func insert(intervals []Interval, newInterval Interval) []Interval { + result := make([]Interval, 0) + i := 0 + + // Add intervals that end before the new interval starts + for i < len(intervals) && intervals[i].End < newInterval.Start { + result = append(result, intervals[i]) + i++ + } + + // Merge intervals that overlap with the new interval + for i < len(intervals) && intervals[i].Start <= newInterval.End { + newInterval.Start = min(newInterval.Start, intervals[i].Start) + newInterval.End = max(newInterval.End, intervals[i].End) + i++ + } + + // Add the merged new interval + result = append(result, newInterval) + + // Add remaining intervals + for i < len(intervals) { + result = append(result, intervals[i]) + i++ + } + + return result +} + +// min returns the minimum of two integers. +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// max returns the maximum of two integers. +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func main() { + intervals := []Interval{ + {1, 3}, + {6, 9}, + } + newInterval := Interval{2, 5} + + result := insert(intervals, newInterval) + fmt.Println("Merged Intervals:", result) +} diff --git a/Arrays/insert_interval.java b/Arrays/insert_interval.java new file mode 100644 index 00000000..3620a5b9 --- /dev/null +++ b/Arrays/insert_interval.java @@ -0,0 +1,107 @@ +/* + Insert Interval + + In this implementation, the `Interval` struct represents an interval with a start and end value. + The `insert` function takes a sorted list of intervals and a new interval as input and returns a new + list of intervals after merging the new interval with the existing intervals. + + Here's how the `insert` function works: + + 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. + 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the + `result` slice. + 3. It merges intervals that overlap with the new interval by updating the start and end values of the new + interval accordingly. + 4. It adds the merged new interval to the `result` slice. + 5. It adds any remaining intervals from the original list to the `result` slice. + 6. Finally, it returns the `result` slice containing the merged intervals. + + The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. + + In the `main` function, an example input is provided with a list of intervals and a new interval. + The `insert` function is called with these inputs, and the result is printed to the console. + + Time Complexity: + The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to + iterate through each interval in the list to merge and insert the new interval. In the worst case, we may + need to traverse all intervals in the list. + + Space Complexity: + The space complexity is O(n), where n is the number of intervals in the input list. This is because we + create a new result slice to store the merged intervals, which can potentially contain all the intervals + from the input list plus the merged new interval. Therefore, the space required is proportional to the + number of intervals in the input list. + + Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number + of intervals in the input list. +*/ +import java.util.ArrayList; +import java.util.List; + +class Interval { + int start; + int end; + + Interval(int start, int end) { + this.start = start; + this.end = end; + } +} + +public class InsertInterval { + public static List insertInterval(List intervals, Interval newInterval) { + List result = new ArrayList<>(); + + // Traverse through each interval in the input list + // and perform the necessary merging and inserting. + for (Interval interval : intervals) { + // If the current interval ends before the new interval starts, + // add it to the result as it does not overlap. + if (interval.end < newInterval.start) { + result.add(interval); + } + // If the current interval starts after the new interval ends, + // add the new interval and update it to the current interval + // as there won't be any more overlap with subsequent intervals. + else if (interval.start > newInterval.end) { + result.add(newInterval); + newInterval = interval; + } + // If there is an overlap between the current interval and the new interval, + // merge them by updating the new interval's start and end. + else { + newInterval.start = Math.min(interval.start, newInterval.start); + newInterval.end = Math.max(interval.end, newInterval.end); + } + } + + // Add the final merged or inserted interval to the result. + result.add(newInterval); + + return result; + } + + // Utility function to print the intervals. + public static void printIntervals(List intervals) { + for (Interval interval : intervals) { + System.out.print("[" + interval.start + ", " + interval.end + "] "); + } + System.out.println(); + } + + public static void main(String[] args) { + // Example usage + List intervals = new ArrayList<>(); + intervals.add(new Interval(1, 3)); + intervals.add(new Interval(6, 9)); + Interval newInterval = new Interval(2, 5); + + System.out.print("Original intervals: "); + printIntervals(intervals); + + List mergedIntervals = insertInterval(intervals, newInterval); + + System.out.print("Merged intervals: "); + printIntervals(mergedIntervals); + } +} diff --git a/Arrays/insert_interval.js b/Arrays/insert_interval.js new file mode 100644 index 00000000..562875c4 --- /dev/null +++ b/Arrays/insert_interval.js @@ -0,0 +1,79 @@ +/* + Insert Interval + + In this implementation, the `Interval` struct represents an interval with a start and end value. + The `insert` function takes a sorted list of intervals and a new interval as input and returns a new + list of intervals after merging the new interval with the existing intervals. + + Here's how the `insert` function works: + + 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. + 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the + `result` slice. + 3. It merges intervals that overlap with the new interval by updating the start and end values of the new + interval accordingly. + 4. It adds the merged new interval to the `result` slice. + 5. It adds any remaining intervals from the original list to the `result` slice. + 6. Finally, it returns the `result` slice containing the merged intervals. + + The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. + + In the `main` function, an example input is provided with a list of intervals and a new interval. + The `insert` function is called with these inputs, and the result is printed to the console. + + Time Complexity: + The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to + iterate through each interval in the list to merge and insert the new interval. In the worst case, we may + need to traverse all intervals in the list. + + Space Complexity: + The space complexity is O(n), where n is the number of intervals in the input list. This is because we + create a new result slice to store the merged intervals, which can potentially contain all the intervals + from the input list plus the merged new interval. Therefore, the space required is proportional to the + number of intervals in the input list. + + Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number + of intervals in the input list. +*/ +class Interval { + constructor(start, end) { + this.start = start; + this.end = end; + } +} + +function insertInterval(intervals, newInterval) { + const mergedIntervals = []; + let i = 0; + + // Skip all intervals that end before the new interval starts + while (i < intervals.length && intervals[i].end < newInterval.start) { + mergedIntervals.push(intervals[i]); + i++; + } + + // Merge intervals that overlap with the new interval + while (i < intervals.length && intervals[i].start <= newInterval.end) { + newInterval.start = Math.min(intervals[i].start, newInterval.start); + newInterval.end = Math.max(intervals[i].end, newInterval.end); + i++; + } + + mergedIntervals.push(newInterval); + + // Add the remaining intervals to the merged intervals list + while (i < intervals.length) { + mergedIntervals.push(intervals[i]); + i++; + } + + return mergedIntervals; +} + +// Example usage +const intervals = [new Interval(1, 3), new Interval(6, 9)]; +const newInterval = new Interval(2, 5); + +console.log("Original intervals:", intervals); +const mergedIntervals = insertInterval(intervals, newInterval); +console.log("Merged intervals:", mergedIntervals); diff --git a/Arrays/insert_interval.py b/Arrays/insert_interval.py new file mode 100644 index 00000000..5efd0a1a --- /dev/null +++ b/Arrays/insert_interval.py @@ -0,0 +1,76 @@ +''' + Insert Interval + + In this implementation, the `Interval` struct represents an interval with a start and end value. + The `insert` function takes a sorted list of intervals and a new interval as input and returns a new + list of intervals after merging the new interval with the existing intervals. + + Here's how the `insert` function works: + + 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. + 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the + `result` slice. + 3. It merges intervals that overlap with the new interval by updating the start and end values of the new + interval accordingly. + 4. It adds the merged new interval to the `result` slice. + 5. It adds any remaining intervals from the original list to the `result` slice. + 6. Finally, it returns the `result` slice containing the merged intervals. + + The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. + + In the `main` function, an example input is provided with a list of intervals and a new interval. + The `insert` function is called with these inputs, and the result is printed to the console. + + Time Complexity: + The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to + iterate through each interval in the list to merge and insert the new interval. In the worst case, we may + need to traverse all intervals in the list. + + Space Complexity: + The space complexity is O(n), where n is the number of intervals in the input list. This is because we + create a new result slice to store the merged intervals, which can potentially contain all the intervals + from the input list plus the merged new interval. Therefore, the space required is proportional to the + number of intervals in the input list. + + Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number + of intervals in the input list. +''' +class Interval: + def __init__(self, start, end): + self.start = start + self.end = end + +def insertInterval(intervals, newInterval): + mergedIntervals = [] + i = 0 + + # Skip all intervals that end before the new interval starts + while i < len(intervals) and intervals[i].end < newInterval.start: + mergedIntervals.append(intervals[i]) + i += 1 + + # Merge intervals that overlap with the new interval + while i < len(intervals) and intervals[i].start <= newInterval.end: + newInterval.start = min(intervals[i].start, newInterval.start) + newInterval.end = max(intervals[i].end, newInterval.end) + i += 1 + + mergedIntervals.append(newInterval) + + # Add the remaining intervals to the merged intervals list + while i < len(intervals): + mergedIntervals.append(intervals[i]) + i += 1 + + return mergedIntervals + +# Example usage +intervals = [ + Interval(1, 3), + Interval(6, 9) +] +newInterval = Interval(2, 5) + +print('Original intervals:', [(i.start, i.end) for i in intervals]) +mergedIntervals = insertInterval(intervals, newInterval) +print('Merged intervals:', [(i.start, i.end) for i in mergedIntervals]) diff --git a/Arrays/insert_intervals.cpp b/Arrays/insert_intervals.cpp new file mode 100644 index 00000000..0c199085 --- /dev/null +++ b/Arrays/insert_intervals.cpp @@ -0,0 +1,47 @@ +/* You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval. + +Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if necessary). + +Return intervals after the insertion. + +Example 1: + +Input: intervals = [[1,3],[6,9]], newInterval = [2,5] +Output: [[1,5],[6,9]] +*/ + + +class Solution { +public: + vector> insert(vector>& intervals, vector& newInterval) { + // get the number of intervals and initialize an index variable + int n = intervals.size(), i = 0; + // initialize a vector to store the result + vector> res; + + // loop through the intervals until the end or until the end of the first interval that comes after the new interval + while(i < n && intervals[i][1] < newInterval[0]){ + // add the current interval to the result + res.push_back(intervals[i]); + i++; + } + // loop through the intervals that overlap with the new interval + while(i < n && newInterval[1] >= intervals[i][0]){ + // update the start and end of the new interval to include the current interval + newInterval[0] = min(newInterval[0], intervals[i][0]); + newInterval[1] = max(newInterval[1], intervals[i][1]); + i++; + } + // add the new interval to the result + res.push_back(newInterval); + + // add the remaining intervals to the result + while(i < n){ + res.push_back(intervals[i]); + i++; + } + // return the result + return res; + } +}; + diff --git a/Arrays/insert_intervals.go b/Arrays/insert_intervals.go new file mode 100644 index 00000000..bf29328d --- /dev/null +++ b/Arrays/insert_intervals.go @@ -0,0 +1,90 @@ +// 57. Insert Interval +// You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval. +// Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if necessary). +// Return intervals after the insertion. +// Example 1: +// Input: intervals = [[1,3],[6,9]], newInterval = [2,5] +// Output: [[1,5],[6,9]] +// Example 2: +// Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8] +// Output: [[1,2],[3,10],[12,16]] +// Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10]. + +// CODE EXPLAINATION WITH DRY RUN: +// The insert function takes in two inputs: an array of non-overlapping intervals represented as an array of arrays of integers (intervals), and a single interval represented as an array of integers (newInterval). The function returns the updated intervals after inserting the new interval such that the intervals remain sorted by the start value and do not overlap. + +// The function first initializes some variables: + +// n: the new interval +// j: the array of existing intervals +// i: a counter variable initialized to 0 +// m: the starting point of the new interval; initially set to the starting point of n +// m1: the ending point of the new interval; initially set to the ending point of n +// The function then enters a loop that iterates through each interval in the array of existing intervals j. For each iteration, it checks if the new interval overlaps with the current interval. An overlap occurs if any of the following conditions are met: + +// The starting point of the new interval is between the starting and ending points of the current interval +// The ending point of the new interval is between the starting and ending points of the current interval +// The new interval completely engulfs the current interval +// If an overlap is detected, the function updates the starting point and ending point of the new interval (m and m1) based on the starting and ending points of the current interval (j[i][0] and j[i][1], respectively). It then removes the current interval from the array of existing intervals j using the append function with a slice operation that excludes the current element (append(j[:i], j[i+1:]...)) and decrements the counter variable i so that the loop will consider the next interval after removing the current one. + +// After iterating through all intervals in j, the function appends the new interval represented by [m, m1] to the end of the j array using the append function. Finally, the intervals in the array are sorted based on their starting points using two nested loops that swap elements if they are not in order. + +// Now, let's do a dry run of the example input and output: + +// Input: + +// intervals = [[1,3],[6,9]] +// newInterval = [2,5] +// The initial values of variables are: + +// n = [2,5] +// j = [[1,3],[6,9]] +// i = 0 +// m = 2 +// m1 = 5 +// In the first iteration of the loop, the current interval is [1,3]. Since n overlaps with this interval, m is updated to 1 and m1 is updated to 5. The current interval is removed from j, which becomes [[6,9]]. The counter variable i is decremented to account for the removal, making its value -1. In the next iteration of the loop, i is incremented to 0, so the current interval is [6,9]. Since there is no overlap between n and this interval, nothing happens. The loop exits, and the updated j array is [[1,5],[6,9]], which matches the expected output. + +// Thus, the function correctly inserts the new interval and sorts the resulting array. + +package main + +import "fmt" + +func insert(intervals [][]int, newInterval []int) [][]int { + n := newInterval + j := intervals + i := 0 + m := n[0] // Initialize starting point of the interval to be inserted + m1 := n[1] // Initialize ending point of the interval to be inserted + + for i < len(j) { + if n[0] >= j[i][0] && n[0] <= j[i][1] || n[1] >= j[i][0] && n[1] <= j[i][1] || n[0] <= j[i][0] && n[1] >= j[i][1] { + if j[i][0] < m { + m = j[i][0] + } + if j[i][1] > m1 { + m1 = j[i][1] + } + j = append(j[:i], j[i+1:]...) + i-- + } + i++ + } + s1 := []int{m, m1} + j = append(j, s1) + for i := 0; i < len(j); i++ { + for k := i + 1; k < len(j); k++ { + if j[i][0] > j[k][0] { + j[i], j[k] = j[k], j[i] + } + } + } + return j +} + +func main() { + intervals := [][]int{{1, 3}, {6, 9}} + newInterval := []int{2, 5} + result := insert(intervals, newInterval) + fmt.Println(result) +} diff --git a/Arrays/is_monotonic.cpp b/Arrays/is_monotonic.cpp new file mode 100644 index 00000000..fb6dbbb9 --- /dev/null +++ b/Arrays/is_monotonic.cpp @@ -0,0 +1,52 @@ +/* + An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. + Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. + + The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array + is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. + + The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. + It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. + + If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the + array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, + indicating that the array is not non-increasing. + + At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, + indicating that the array is monotonic. Otherwise, it returns false. + + O(n) time | O(1) space - where n is the length of the array +*/ +#include +#include + +using namespace std; + +bool IsMonotonic(vector& array) { + bool isNonDecreasing = true; // flag to track if array is non-decreasing + bool isNonIncreasing = true; // flag to track if array is non-increasing + + // iterate through the array starting at index 1 + for (int i = 1; i < array.size(); i++) { + if (array[i] < array[i - 1]) { + // if the current element is less than the previous element, array is not non-decreasing + isNonDecreasing = false; + } + if (array[i] > array[i - 1]) { + // if the current element is greater than the previous element, array is not non-increasing + isNonIncreasing = false; + } + } + + // if either flag is true, return true indicating that array is monotonic + return isNonDecreasing || isNonIncreasing; +} + +int main() { + // example usage + vector arr = {1, 2, 3, 3, 4, 5}; + bool isMonotonic = IsMonotonic(arr); + cout << "Array is monotonic: " << isMonotonic << endl; + + return 0; +} \ No newline at end of file diff --git a/Arrays/is_monotonic.go b/Arrays/is_monotonic.go index 0703f433..003b1a35 100644 --- a/Arrays/is_monotonic.go +++ b/Arrays/is_monotonic.go @@ -1,34 +1,44 @@ /* An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. + + The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array + is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. + + The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. + It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. + + If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the + array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, + indicating that the array is not non-increasing. + + At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, + indicating that the array is monotonic. Otherwise, it returns false. + + O(n) time | O(1) space - where n is the length of the array */ package main import "fmt" -func IsMonotonic(Arr []int) bool { - if len(Arr) < 2 { - return true - } - - isIncreasing := 0 - for i := 1; i < len(Arr); i++ { - if isIncreasing == 0 { - if Arr[i - 1] > Arr[i] { - isIncreasing = -1 // means we will check for decreasing - } else if Arr[i - 1] < Arr[i] { - isIncreasing = 1 // means we will check for increasing - } - } - if isIncreasing == 1 && Arr[i - 1] > Arr[i] { - return false // in increasing array element before other element cannot be less so return false +func IsMonotonic(array []int) bool { + // assume the array is non-decreasing until we find a decreasing element + isNonDecreasing := true + // assume the array is non-increasing until we find an increasing element + isNonIncreasing := true + for i := 1; i < len(array); i++ { + if array[i] < array[i - 1] { + // if the current element is less than the previous element, the array is not non-decreasing + isNonDecreasing = false } - if isIncreasing == -1 && Arr[i - 1] < Arr[i] { - return false // in decreasing array element after other element cannot be greater so return false + if array[i] > array[i - 1] { + // if the current element is greater than the previous element, the array is not non-increasing + isNonIncreasing = false } } - return true + // return true if the array is either non-decreasing or non-increasing + return isNonDecreasing || isNonIncreasing } func main() { diff --git a/Arrays/is_monotonic.java b/Arrays/is_monotonic.java new file mode 100644 index 00000000..5df0b3f3 --- /dev/null +++ b/Arrays/is_monotonic.java @@ -0,0 +1,44 @@ +/* + An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. + Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. + + The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array + is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. + + The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. + It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. + + If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the + array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, + indicating that the array is not non-increasing. + + At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, + indicating that the array is monotonic. Otherwise, it returns false. + + O(n) time | O(1) space - where n is the length of the array +*/ +public class Main { + public static boolean isMonotonic(int[] array) { + boolean isNonDecreasing = true; // Assume the array is non-decreasing until we find a decreasing element + boolean isNonIncreasing = true; // Assume the array is non-increasing until we find an increasing element + for (int i = 1; i < array.length; i++) { + if (array[i] < array[i - 1]) { + // If the current element is less than the previous element, the array is not non-decreasing + isNonDecreasing = false; + } + if (array[i] > array[i - 1]) { + // If the current element is greater than the previous element, the array is not non-increasing + isNonIncreasing = false; + } + } + // Return true if the array is either non-decreasing or non-increasing + return isNonDecreasing || isNonIncreasing; + } + + public static void main(String[] args) { + int[] arr = { 1, 2, 3, 4, 5 }; + int[] arr2 = { 5, 4, 3, 2, 1 }; + System.out.println(isMonotonic(arr)); + System.out.println(isMonotonic(arr2)); + } +} diff --git a/Arrays/is_monotonic.js b/Arrays/is_monotonic.js new file mode 100644 index 00000000..fccdd243 --- /dev/null +++ b/Arrays/is_monotonic.js @@ -0,0 +1,38 @@ +/* + An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. + Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. + + The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array + is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. + + The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. + It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. + + If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the + array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, + indicating that the array is not non-increasing. + + At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, + indicating that the array is monotonic. Otherwise, it returns false. + + O(n) time | O(1) space - where n is the length of the array +*/ +function isMonotonic(array) { + let isNonDecreasing = true; // Assume the array is non-decreasing until we find a decreasing element + let isNonIncreasing = true; // Assume the array is non-increasing until we find an increasing element + for (let i = 1; i < array.length; i++) { + if (array[i] < array[i - 1]) { + // If the current element is less than the previous element, the array is not non-decreasing + isNonDecreasing = false; + } + if (array[i] > array[i - 1]) { + // If the current element is greater than the previous element, the array is not non-increasing + isNonIncreasing = false; + } + } + // Return true if the array is either non-decreasing or non-increasing + return isNonDecreasing || isNonIncreasing; +} + +const arr = [1, 2, 3, 4, 5]; +const arr2 = [ diff --git a/Arrays/is_monotonic.py b/Arrays/is_monotonic.py new file mode 100644 index 00000000..d464bb1b --- /dev/null +++ b/Arrays/is_monotonic.py @@ -0,0 +1,36 @@ +''' + An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. + Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. + + The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array + is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. + + The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. + It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. + + If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the + array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, + indicating that the array is not non-increasing. + + At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, + indicating that the array is monotonic. Otherwise, it returns false. + + O(n) time | O(1) space - where n is the length of the array +''' +def is_monotonic(array): + is_non_decreasing = True # Assume the array is non-decreasing until we find a decreasing element + is_non_increasing = True # Assume the array is non-increasing until we find an increasing element + for i in range(1, len(array)): + if array[i] < array[i - 1]: + # If the current element is less than the previous element, the array is not non-decreasing + is_non_decreasing = False + if array[i] > array[i - 1]: + # If the current element is greater than the previous element, the array is not non-increasing + is_non_increasing = False + # Return true if the array is either non-decreasing or non-increasing + return is_non_decreasing or is_non_increasing + +arr = [1, 2, 3, 4, 5] +arr2 = [5, 4, 3, 2, 1] +print(is_monotonic(arr)) +print(is_monotonic(arr2)) diff --git a/Arrays/josephus_problem.java b/Arrays/josephus_problem.java deleted file mode 100644 index cbd808c6..00000000 --- a/Arrays/josephus_problem.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * There are A people standing in a circle. Person 1 kills their immediate clockwise neighbour and pass the knife to the next person standing in circle. This process continues till there is only 1 person remaining. Find the last person standing in the circle. - * - * - * Problem Constraints - * 1 <= A <= 105 - * - * - * Input Format - * First argument A is an integer. - * - * - * Output Format - * Return an integer. - * - * - * Example Input - * Input 1: - * A = 4 - * Input 2: - * A = 5 - * - * - * Example Output - * Output 1: - * 1 - * Output 2: - * 3 - * - * - * Example Explanation - * For Input 1: - * Firstly, the person at position 2 is killed, then the person at position 4 is killed, - * then the person at position 3 is killed. So the person at position 1 survives. - * For Input 2: - * - * Firstly, the person at position 2 is killed, then the person at position 4 is killed, - * then the person at position 1 is killed. Finally, the person at position 5 is killed. - * So the person at position 3 survives. - */ - -package InterviewProblems; - -public class JosephusProblem { - public static void main(String[] args) { - int numOfPeople = 5; - int ans = solve(numOfPeople); - System.out.println(ans); - } - public static int solve(int numOfPeople) { - // O(Log(N)) time | O(1) space - int num = (int) (Math.log (numOfPeople) / Math.log(2)); - - int kills = numOfPeople - (int) Math.pow(2, num); - - return (2 * kills) + 1; - } -} diff --git a/Arrays/longest_consecutive_nums_subarray.cpp b/Arrays/longest_consecutive_nums_subarray.cpp deleted file mode 100644 index d7ee0e9a..00000000 --- a/Arrays/longest_consecutive_nums_subarray.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Subarray with maximum sum using cumulative sum O(n^2) -#include -using namespace std; -const int Nmax = 100001; -int a[Nmax], n; -bool fr[Nmax]; - -int longest_consecutive_nums_subarray(){ - int ans = 0; - for(int left = 1; left <= n; left++){ - for(int i = 1; i <= n; i++){ - fr[i] = false; - } - int mini = a[left], maxi = a[left]; - for(int right = left; right <= n; right++){ - if(fr[a[right]]) - break; - fr[a[right]] = true; - mini = min(mini, a[right]); - maxi = max(maxi, a[right]); - if(right - left == maxi - mini){ - ans = max(ans, right - left + 1); - } - } - } - return ans; -} -int main(){ - cin >> n; - for(int i = 1; i <= n; i++){ - cin >> a[i]; - } - cout << longest_consecutive_nums_subarray(); - return 0; -} \ No newline at end of file diff --git a/Arrays/longest_consecutive_ones.java b/Arrays/longest_consecutive_ones.java deleted file mode 100644 index e914f568..00000000 --- a/Arrays/longest_consecutive_ones.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Given a binary string A. It is allowed to do at most one swap between any 0 and 1. Find and return the length of the longest consecutive 1’s that can be achieved. - * - * - * Input Format - * - * The only argument given is string A. - * Output Format - * - * Return the length of the longest consecutive 1’s that can be achieved. - * Constraints - * - * 1 <= length of string <= 1000000 - * A contains only characters 0 and 1. - * For Example - * - * Input 1: - * A = "111000" - * Output 1: - * 3 - * - * Input 2: - * A = "111011101" - * Output 2: - * 7 - */ - -package InterviewProblems; - -public class longestConsecutiveOnes { - public static void main(String[] args) { - String str = "11010110000000000"; - int ans = solve(str); - System.out.println(ans); - } - public static int solve(String str) { - // O(N) time | O(1) space - int ans = 0; - int len = str.length(); - int totalOnes = 0; - - // first count total one's - for (int i = 0; i < len; i++) { // 1110111 - char currentChar = str.charAt(i); - if (currentChar == '1') totalOnes++; - } - - if (totalOnes == 0) return 0; - for (int i = 0; i < len; i++) { - char currentChar = str.charAt(i); - if (currentChar == '0') { - int leftIdx = i - 1, leftOnes = 0; - while (leftIdx > -1 && str.charAt(leftIdx) == '1') { - leftIdx--; - leftOnes++; - } - - int rightIdx = i + 1, rightOnes = 0; - while (rightIdx < len && str.charAt(rightIdx) == '1') { - rightIdx++; - rightOnes++; - } - - if (leftOnes + rightOnes == totalOnes) { - ans = Math.max(ans, leftOnes + rightOnes); - } else { - ans = Math.max(ans, leftOnes + rightOnes + 1); - } - } - } - return ans; - } -} diff --git a/Arrays/longest_peak.cpp b/Arrays/longest_peak.cpp new file mode 100644 index 00000000..fa4e0104 --- /dev/null +++ b/Arrays/longest_peak.cpp @@ -0,0 +1,76 @@ +/* + Write a function that takes in an array of integers and returns the length of the longest peak in the array. + A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), + at which point they become strictly decreasing. At least three integers are required to form a peak. + + The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer + representing the length of the longest "peak" in the array. + + A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, + reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. + + The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest + peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. + + The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks + whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. + + If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning + and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of + integers, using the variables leftIndex and rightIndex. + + Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula + rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates + longestPeak to the length of the current peak. + + Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over + the entire peak and continue iterating from the end of the peak. + + The function returns the value of longestPeak once it has finished iterating over the array. + + The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. + + The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. +*/ +#include + +int LongestPeak(std::vector& array) { + int longestPeak = 0; + int i = 1; + + while (i < array.size() - 1) { + // Check if i is a peak (i.e., it's greater than its neighbors) + bool isPeak = array[i - 1] < array[i] && array[i] > array[i + 1]; + + if (!isPeak) { + // If i is not a peak, move to the next element + i += 1; + continue; + } + + // Search left of i to find the beginning of the peak + int leftIndex = i - 2; + while (leftIndex >= 0 && array[leftIndex] < array[leftIndex + 1]) { + leftIndex--; + } + + // Search right of i to find the end of the peak + int rightIndex = i + 2; + while (rightIndex < array.size() && array[rightIndex] < array[rightIndex - 1]) { + rightIndex++; + } + + // Calculate the length of the current peak + int currentPeak = rightIndex - leftIndex - 1; + + // Update longestPeak if currentPeak is longer + if (currentPeak > longestPeak) { + longestPeak = currentPeak; + } + + // Move i to the end of the current peak + i = rightIndex; + } + + return longestPeak; +} diff --git a/Arrays/longest_peak.go b/Arrays/longest_peak.go new file mode 100644 index 00000000..42d1fa8f --- /dev/null +++ b/Arrays/longest_peak.go @@ -0,0 +1,82 @@ +/* + Write a function that takes in an array of integers and returns the length of the longest peak in the array. + A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), + at which point they become strictly decreasing. At least three integers are required to form a peak. + + The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer + representing the length of the longest "peak" in the array. + + A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, + reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. + + The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest + peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. + + The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks + whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. + + If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning + and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of + integers, using the variables leftIndex and rightIndex. + + Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula + rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates + longestPeak to the length of the current peak. + + Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over + the entire peak and continue iterating from the end of the peak. + + The function returns the value of longestPeak once it has finished iterating over the array. + + The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. + + The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. +*/ +package main + +import "fmt" + +// LongestPeak function takes an integer array and returns the length of the longest peak in the array. +func LongestPeak(array []int) int { + longestPeak := 0 + i := 1 + for i < len(array)-1 { + // check if i is a peak (i.e., it's greater than its neighbors) + isPeak := array[i-1] < array[i] && array[i] > array[i+1] + if !isPeak { + // if i is not a peak, move to the next element + i += 1 + continue + } + + // search left of i to find the beginning of the peak + leftIndex := i - 2 + for leftIndex >= 0 && array[leftIndex] < array[leftIndex+1] { + leftIndex-- + } + + // search right of i to find the end of the peak + rightIndex := i + 2 + for rightIndex < len(array) && array[rightIndex] < array[rightIndex-1] { + rightIndex++ + } + + // calculate the length of the current peak + currentPeak := rightIndex - leftIndex - 1 + + // update longestPeak if currentPeak is longer + if currentPeak > longestPeak { + longestPeak = currentPeak + } + + // move i to the end of the current peak + i = rightIndex + } + return longestPeak +} + +func main() { + array := []int{1, 2, 3, 4, 5, 4, 3, 2, 1} + longestPeak := LongestPeak(array) + fmt.Println(longestPeak) // Output: 9 +} \ No newline at end of file diff --git a/Arrays/longest_peak.java b/Arrays/longest_peak.java new file mode 100644 index 00000000..7f156bc2 --- /dev/null +++ b/Arrays/longest_peak.java @@ -0,0 +1,34 @@ +/* + Write a function that takes in an array of integers and returns the length of the longest peak in the array. + A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), + at which point they become strictly decreasing. At least three integers are required to form a peak. + + The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer + representing the length of the longest "peak" in the array. + + A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, + reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. + + The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest + peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. + + The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks + whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. + + If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning + and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of + integers, using the variables leftIndex and rightIndex. + + Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula + rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates + longestPeak to the length of the current peak. + + Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over + the entire peak and continue iterating from the end of the peak. + + The function returns the value of longestPeak once it has finished iterating over the array. + + The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. + + The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. +*/ \ No newline at end of file diff --git a/Arrays/longest_peak.js b/Arrays/longest_peak.js new file mode 100644 index 00000000..c26bc2f3 --- /dev/null +++ b/Arrays/longest_peak.js @@ -0,0 +1,82 @@ +/* + Write a function that takes in an array of integers and returns the length of the longest peak in the array. + A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), + at which point they become strictly decreasing. At least three integers are required to form a peak. + + The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer + representing the length of the longest "peak" in the array. + + A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, + reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. + + The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest + peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. + + The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks + whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. + + If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning + and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of + integers, using the variables leftIndex and rightIndex. + + Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula + rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates + longestPeak to the length of the current peak. + + Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over + the entire peak and continue iterating from the end of the peak. + + The function returns the value of longestPeak once it has finished iterating over the array. + + The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. + + The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. +*/ +function longestPeak(array) { + let longestPeak = 0; + let i = 1; + + while (i < array.length - 1) { + // Check if i is a peak (i.e., it's greater than its neighbors) + const isPeak = array[i - 1] < array[i] && array[i] > array[i + 1]; + + if (!isPeak) { + // If i is not a peak, move to the next element + i += 1; + continue; + } + + // Search left of i to find the beginning of the peak + let leftIndex = i - 2; + while (leftIndex >= 0 && array[leftIndex] < array[leftIndex + 1]) { + leftIndex--; + } + + // Search right of i to find the end of the peak + let rightIndex = i + 2; + while ( + rightIndex < array.length && + array[rightIndex] < array[rightIndex - 1] + ) { + rightIndex++; + } + + // Calculate the length of the current peak + const currentPeak = rightIndex - leftIndex - 1; + + // Update longestPeak if currentPeak is longer + if (currentPeak > longestPeak) { + longestPeak = currentPeak; + } + + // Move i to the end of the current peak + i = rightIndex; + } + + return longestPeak; +} + +// Test the function +const array = [1, 3, 2, 1, 4, 7, 3, 2, 1]; +const result = longestPeak(array); +console.log(result); // Output: 6 diff --git a/Arrays/longest_peak.py b/Arrays/longest_peak.py new file mode 100644 index 00000000..0debdf75 --- /dev/null +++ b/Arrays/longest_peak.py @@ -0,0 +1,102 @@ +''' + Write a function that takes in an array of integers and returns the length of the longest peak in the array. + A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), + at which point they become strictly decreasing. At least three integers are required to form a peak. + + The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer + representing the length of the longest "peak" in the array. + + A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, + reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. + + The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest + peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. + + The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks + whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. + + If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning + and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of + integers, using the variables leftIndex and rightIndex. + + Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula + rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates + longestPeak to the length of the current peak. + + Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over + the entire peak and continue iterating from the end of the peak. + + The function returns the value of longestPeak once it has finished iterating over the array. + + The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. + + The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. + +''' + +def longest_peak(array): + longest_peak = 0 + i = 1 + + while i < len(array) - 1: + # Check if i is a peak (i.e., it's greater than its neighbors) + is_peak = array[i - 1] < array[i] > array[i + 1] + + if not is_peak: + # If i is not a peak, move to the next element + i += 1 + continue + + # Search left of i to find the beginning of the peak + left_index = i - 2 + while left_index >= 0 and array[left_index] < array[left_index + 1]: + left_index -= 1 + + # Search right of i to find the end of the peak + right_index = i + 2 + while right_index < len(array) and array[right_index] < array[right_index - 1]: + right_index += 1 + + # Calculate the length of the current peak + current_peak = right_index - left_index - 1 + + # Update longest_peak if current_peak is longer + if current_peak > longest_peak: + longest_peak = current_peak + + # Move i to the end of the current peak + i = right_index + + return longest_peak + +# Test the function +array = [1, 3, 2, 1, 4, 7, 3, 2, 1] +result = longest_peak(array) +print(result) # Output: 6 + + + +arr=[1, 2, 3, 3, 4, 0, 10, 6, 5, -1, -3, 2, 3] + + +def longestPeak(arr: list) -> int: + ans = 0 + # iterate through the array from index 1 to len(arr) - 1 + for indx in range(1, len(arr) - 1): + # check if the current element is a peak + if arr[indx - 1] < arr[indx] > arr[indx + 1]: + # if it is a peak, then find the length of the peak + uphill_start = downhill_ends = indx + # go to the uphill start + while uphill_start > 0 and arr[uphill_start] > arr[uphill_start - 1]: + uphill_start -= 1 + # go to the downhill end + while downhill_ends + 1 < len(arr) and arr[downhill_ends] > arr[downhill_ends + 1]: + downhill_ends += 1 + # update the ans + ans = max(ans, (downhill_ends - uphill_start + 1)) + return ans + + +print(longestPeak(arr)) +# output: 6 \ No newline at end of file diff --git a/Arrays/majority_el.cpp b/Arrays/majority_el.cpp deleted file mode 100644 index 18a1c699..00000000 --- a/Arrays/majority_el.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/*Given an array nums of size n, return the majority element. - -The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array. - -Example 1: - -Input: nums = [3,2,3] -Output: 3 - -Example 2: - -Input: nums = [2,2,1,1,1,2,2] -Output: 2 */ - - - - -class Solution { -public: - int majorityElement(vector& arr) { - int ele = arr[0]; - int count = 0; - for(int i = 0;i < arr.size();i++){ - if(count == 0) ele = arr[i]; - count += (ele == arr[i]) ? 1 : -1; - } - return ele; - } -}; \ No newline at end of file diff --git a/Arrays/majority_el.java b/Arrays/majority_el.java deleted file mode 100644 index f3923a7e..00000000 --- a/Arrays/majority_el.java +++ /dev/null @@ -1,47 +0,0 @@ -/* -Majority Element - - -Given an array nums of size n, return the majority element. - -The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array. - - - -Example 1: - -Input: nums = [3,2,3] -Output: 3 -Example 2: - -Input: nums = [2,2,1,1,1,2,2] -Output: 2 - --*/ - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -class Solution { - public int majorityElement(int[] nums) { - Set set = new HashSet<>(); - for (int i : nums) { - set.add(i); - } - ArrayList arr = new ArrayList<>(set); - int count = 0; - for (Integer var : arr) { - count = 0; - for (int j = 0; j < nums.length; j++) { - if (var.intValue() == nums[j]) { - count++; - } - } - if (count > nums.length / 2) { - return var.intValue(); - } - } - return 0; - } -} \ No newline at end of file diff --git a/Arrays/majority_element.java b/Arrays/majority_element.java new file mode 100644 index 00000000..23c00bc2 --- /dev/null +++ b/Arrays/majority_element.java @@ -0,0 +1,33 @@ +/* + Majority Element + Given an array nums of size n, return the majority element. + The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array. + + Example 1: + Input: nums = [3,2,3] + Output: 3 + + Example 2: + Input: nums = [2,2,1,1,1,2,2] + Output: 2 +*/ + +class Solution { + public int majorityElement(int[] nums) { + int count = 0; + int target = nums[0]; + for(int i=0; i ans = solve(nums); - System.out.println(ans); - } - public static List solve(int[] nums) { - // O(N) time | O(1) space - int num1 = -1, num2 = -1, count1 = 0, count2 = 0, len = nums.length; - - for (int num : nums) { - if (num == num1) count1++; - else if (num == num2) count2++; - else if (count1 == 0) { - num1 = num; - count1++; - } else if (count2 == 0) { - num2 = num; - count2++; - } else { - count1--; - count2--; - } - } - - List ans = new ArrayList<>(); - count1 = 0; count2 = 0; - for (int num : nums) { - if (num == num1) count1++; - else if (num == num2) count2++; - } - if (count1 > len / 3) ans.add(num1); - if (count2 > len / 3) ans.add(num2); - - return ans; - } -} diff --git a/Arrays/max_contiguous_sum.go b/Arrays/max_contiguous_sum.go deleted file mode 100644 index 9dad2f6a..00000000 --- a/Arrays/max_contiguous_sum.go +++ /dev/null @@ -1,50 +0,0 @@ -// Given an array of 􀝊 numbers, give an algorithm for finding -// a contiguous subsequence A(i). . . A(j) for which the sum of elements is maximum. -// Example: {-2, 11, -4, 13, -5, 2} → 20 and {1, -3, 4, -2, -1, 6} → 7 -package main - -import "fmt" - -// MaxContiguousSum: give max contiguous sum in supplied array -// Approach: Bruteforce tru all possibilities and select maximum sum -// Time complexity O(n3) -func MaxContiguousSum(Arr []int) int { - maxSum, n := 0, len(Arr) - for i := 0; i < n; i++ { - for j := i; j < n; j++ { - currentSum := 0 - // range over i to j and calculate sum - for k := i; k <= j; k++ { - currentSum += Arr[k] - } - // if sum exceeds maxsum so far then set max sum as currsum - if currentSum > maxSum { - maxSum = currentSum - } - } - } - return maxSum -} - -// Time complexity O(n2) -func MaxContiguousSum2(Arr []int) int { - maxSum, n := 0, len(Arr) - for i := 0; i < n ; i++ { - currentSum := 0 - for j := i; j < n; j++ { - currentSum += Arr[j] - if currentSum > maxSum { - maxSum = currentSum - } - } - } - return maxSum -} - -func main() { - Arr := []int{-2, 11, -4, 13, -5, 2} - fmt.Println(MaxContiguousSum(Arr)) - Arr = []int{1, -3, 4, -2, -1, 6} - fmt.Println(MaxContiguousSum(Arr)) - fmt.Println(MaxContiguousSum2(Arr)) -} \ No newline at end of file diff --git a/Arrays/max_len_of_concatenated_string_with_unique_char.cpp b/Arrays/max_len_of_concatenated_string_with_unique_char.cpp deleted file mode 100644 index 4a129590..00000000 --- a/Arrays/max_len_of_concatenated_string_with_unique_char.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* -You are given an array of strings arr. A string s is formed by the concatenation of a subsequence of arr that has unique characters. - -Return the maximum possible length of s. - -A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements. - - - -Example 1: - -Input: arr = ["un","iq","ue"] -Output: 4 -Explanation: All the valid concatenations are: -- "" -- "un" -- "iq" -- "ue" -- "uniq" ("un" + "iq") -- "ique" ("iq" + "ue") -Maximum length is 4. -Example 2: - -Input: arr = ["cha","r","act","ers"] -Output: 6 -Explanation: Possible longest valid concatenations are "chaers" ("cha" + "ers") and "acters" ("act" + "ers"). -Example 3: - -Input: arr = ["abcdefghijklmnopqrstuvwxyz"] -Output: 26 -Explanation: The only string in arr has all 26 characters. - - -Constraints: - -1 <= arr.length <= 16 -1 <= arr[i].length <= 26 -arr[i] contains only lowercase English letters. - -*/ - -class Solution { -public: - int check(vector& arr, int i, string s){ - if(i == arr.size()){ - int freq[26] = {0}; - for(int k = 0; k < s.length(); k++){ - if(freq[s[k]-'a'] == 1) - return 0; - freq[s[k]-'a']++; - } - return s.length(); - } - int op1, op2; - op1 = op2 = INT_MIN; - // include the string - if(s.length() + arr[i].length() <= 26){ - op1 = check(arr, i+1, s + arr[i]); - } - // exclude it - op2 = check(arr, i+1, s); - return max(op1, op2); - } - int maxLength(vector& arr) { - return check(arr, 0, ""); - } -}; \ No newline at end of file diff --git a/Arrays/max_subarray_sum_with_length_k.java b/Arrays/max_subarray_sum_with_length_k.java deleted file mode 100644 index 1f55299d..00000000 --- a/Arrays/max_subarray_sum_with_length_k.java +++ /dev/null @@ -1,37 +0,0 @@ -package SlidingWindow; - -public class MaxSubarraySumWithLengthK { - public static void main(String[] args) { - final long startTime = System.currentTimeMillis(); - final long beforeUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - - int[] array = {-3, 4, -2, 5, 3, -2, 8, 2, -1, 4}; - int targetLength = 5; - int ans = solve(array, targetLength); - System.out.println(ans); - - final long endTime = System.currentTimeMillis(); - final long afterUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - final long actualMemUsed = afterUsedMem-beforeUsedMem; - System.out.println("Runtime " + (endTime - startTime) + " ms"); - System.out.println("Space " + actualMemUsed + " B"); - } - public static int solve(int[] array, int targetLength) { - // O(n) time | O(1) space - int maxSubarraySum = 0; - int currentSum = 0; - int length = array.length; - - for (int i = 0; i < targetLength; i++) - currentSum += array[i]; - - int startIdx = 1; - int endIdx = targetLength; - - while (endIdx < length) { - currentSum = currentSum + array[endIdx++] - array[startIdx++ - 1]; - maxSubarraySum = Math.max (maxSubarraySum, currentSum); - } - return maxSubarraySum; - } -} diff --git a/Arrays/maximum_positivity.java b/Arrays/maximum_positivity.java deleted file mode 100644 index 2328a8c4..00000000 --- a/Arrays/maximum_positivity.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Given an array of integers A, of size N. - * - * Return the maximum size subarray of A having only non-negative elements. If there are more than one such subarray, return the one having the smallest starting index in A. - * - * NOTE: It is guaranteed that an answer always exists. - * - * - * - * Problem Constraints - * 1 <= N <= 105 - * - * -109 <= A[i] <= 109 - * - * - * - * Input Format - * The first and only argument given is the integer array A. - * - * - * - * Output Format - * Return maximum size subarray of A having only non-negative elements. If there are more than one such subarrays, return the one having earliest starting index in A. - * - * - * - * Example Input - * Input 1: - * - * A = [5, 6, -1, 7, 8] - * Input 2: - * - * A = [1, 2, 3, 4, 5, 6] - * - * - * Example Output - * Output 1: - * - * [5, 6] - * Output 2: - * - * [1, 2, 3, 4, 5, 6] - * - * - * Example Explanation - * Explanation 1: - * - * There are two subarrays of size 2 having only non-negative elements. - * 1. [5, 6] starting point = 0 - * 2. [7, 8] starting point = 3 - * As starting point of 1 is smaller, return [5, 6] - * Explanation 2: - * - * There is only one subarray of size 6 having only non-negative elements: - * [1, 2, 3, 4, 5, 6] - */ -package InterviewProblems; - -import java.util.Arrays; - -public class MaximumPositivity { - - public static void main(String[] args) { -// int[] array = {3341620, -7399236, 5207903, -1729033, 7603748, -3283659, 4646901, 9983066, -1239862, -2196498}; - int[] array = {9, 3, -2, 11, 1, 2}; -// int[] array = {1, 2, 3, 4, 5}; - int[] ans = solve(array); - System.out.println(Arrays.toString(ans)); - } - - public static int[] solve(int[] array) { - // O(N) time | O(1) space - int size = 0, leftIdx = 0, rightIdx = 0, len = array.length; - for (int currentLeftIdx = -1, currentRightIdx = 0; currentRightIdx < len; currentRightIdx++) { - int currentRightNum = array[currentRightIdx]; - if (currentRightNum > -1) { - if (size < currentRightIdx - currentLeftIdx) { - size = currentRightIdx - currentLeftIdx; - leftIdx = currentLeftIdx; - rightIdx = currentRightIdx; - } - } else currentLeftIdx = currentRightIdx; - } - return Arrays.copyOfRange(array, leftIdx + 1, rightIdx + 1); - } - - - public static int[] solveUsingBruteForce(int[] A) { - // O(N^2) time | O(1) space - int maxLen = 0; - int startIdx = 0; - int currentLen = 0; -// int endIdx = 0; - for (int i = 0; i < A.length; i++) { - currentLen = 0; - int currentNum = A[i]; - int j = i; - if (currentNum > -1) { - while (j < A.length && A[j] > -1) { - j++; - currentLen++; - } - if (currentLen > maxLen) { - maxLen = currentLen; - startIdx = i; -// endIdx = j; - } - } - i = j; - } - return Arrays.copyOfRange(A, startIdx, startIdx + maxLen); - } -} diff --git a/Arrays/maximum_subarray_sum.cpp b/Arrays/maximum_subarray_sum.cpp new file mode 100644 index 00000000..8534e6a6 --- /dev/null +++ b/Arrays/maximum_subarray_sum.cpp @@ -0,0 +1,89 @@ +// Maximum Subarray +/* + The maxSubarraySum function takes an integer slice arr as input and returns the maximum subarray + sum as an integer. + + The maxSoFar variable is initialized to the smallest possible integer value, since any valid subarray + sum must be greater than or equal to this value. The maxEndingHere variable is initialized to 0, + since an empty subarray has a sum of 0. + + The function then iterates through the elements of arr, updating maxEndingHere and maxSoFar as necessary. + At each iteration, the maximum ending here is updated by adding the current element to it. + If the maximum ending here becomes negative, it is reset to 0, since any subarray that includes a negative + sum will not be the maximum subarray. If the maximum ending here is greater than the maximum subarray + sum so far, maxSoFar is updated to the new maximum. + + Finally, the function returns maxSoFar. + + In the main function, an example input array is defined and passed to maxSubarraySum. + The resulting maximum subarray sum is printed to the console. + + The time complexity of the above implementation of Kadane's algorithm for finding the maximum subarray sum is O(n), + where n is the length of the input array. This is because we are iterating over each element of the array only once. + + The space complexity of the implementation is O(1), as we are only using a constant amount of extra space for + storing the maximum subarray sum and the current subarray sum. + + Example Input: arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] + Example Output: 6 + Explanation: The maximum subarray sum is [4, -1, 2, 1] which adds up to 6. + +*/ +#include +#include + +using namespace std; + +// Function to find the maximum subarray sum using Kadane's algorithm +int maxSubArraySum(vector& nums) { + int maxSum = INT_MIN; + int currSum = 0; + + // Iterate over each element in the array + for (int i = 0; i < nums.size(); i++) { + // Add the current element to the current sum + currSum += nums[i]; + + // Update the maximum sum seen so far + if (currSum > maxSum) { + maxSum = currSum; + } + + // If the current sum is negative, we reset it to zero + if (currSum < 0) { + currSum = 0; + } + } + + // Return the maximum sum + return maxSum; +} + +// Function to calculate maximum subarray sum using brute force approach +int maxSubarraySumBruteForce(vector& nums) { + int maxSum = INT_MIN; // Initialize maximum sum to smallest integer value + // Consider all subarrays starting from i to j and calculate their sum + for (int i = 0; i < nums.size(); i++) { + int sum = 0; // Initialize sum for each subarray + for (int j = i; j < nums.size(); j++) { + sum += nums[j]; // Add element to sum + // Update maxSum if sum is greater + if (sum > maxSum) { + maxSum = sum; + } + } + } + return maxSum; // Return maximum sum +} + +// Driver code to test the function +int main() { + // Example usage + vector nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; + int maxSum = maxSubArraySum(nums); + cout << "Maximum subarray sum: " << maxSum << endl; + maxSum = maxSubarraySumBruteForce(nums); + cout << "Maximum subarray sum using brute force: " << maxSum << endl; + + return 0; +} diff --git a/Arrays/maximum_subarray_sum.go b/Arrays/maximum_subarray_sum.go new file mode 100644 index 00000000..f2ee3507 --- /dev/null +++ b/Arrays/maximum_subarray_sum.go @@ -0,0 +1,95 @@ +// Maximum Subarray +/* + The maxSubarraySum function takes an integer slice arr as input and returns the maximum subarray + sum as an integer. + + The maxSoFar variable is initialized to the smallest possible integer value, since any valid subarray + sum must be greater than or equal to this value. The maxEndingHere variable is initialized to 0, + since an empty subarray has a sum of 0. + + The function then iterates through the elements of arr, updating maxEndingHere and maxSoFar as necessary. + At each iteration, the maximum ending here is updated by adding the current element to it. + If the maximum ending here becomes negative, it is reset to 0, since any subarray that includes a negative + sum will not be the maximum subarray. If the maximum ending here is greater than the maximum subarray + sum so far, maxSoFar is updated to the new maximum. + + Finally, the function returns maxSoFar. + + In the main function, an example input array is defined and passed to maxSubarraySum. + The resulting maximum subarray sum is printed to the console. + + The time complexity of the above implementation of Kadane's algorithm for finding the maximum subarray sum is O(n), + where n is the length of the input array. This is because we are iterating over each element of the array only once. + + The space complexity of the implementation is O(1), as we are only using a constant amount of extra space for + storing the maximum subarray sum and the current subarray sum. + + Example Input: arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] + Example Output: 6 + Explanation: The maximum subarray sum is [4, -1, 2, 1] which adds up to 6. + +*/ +package main + +import ( + "fmt" + "math" +) + +// Kadanes algorithm +// This function returns the maximum subarray sum in a given slice of integers. +// It takes an integer slice as input and returns the maximum subarray sum as an integer. +func maxSubarraySum(arr []int) int { + maxSoFar := math.MinInt32 // Initialize the maximum subarray sum to the smallest possible integer value + maxEndingHere := 0 // Initialize the maximum ending here to 0 + + for _, num := range arr { + // Update the maximum ending here + maxEndingHere += num + + // If the maximum ending here is negative, we reset it to 0 + if maxEndingHere < 0 { + maxEndingHere = 0 + } + + // If the maximum ending here is greater than the maximum subarray sum so far, + // we update the maximum subarray sum so far + if maxEndingHere > maxSoFar { + maxSoFar = maxEndingHere + } + } + + return maxSoFar +} + +// Brute Force Solution +func maxSubarraySumBruteForce(nums []int) int { + maxSum := math.MinInt32 // Initialize the maximum sum to the smallest possible integer + n := len(nums) + + // Consider all possible subarrays and keep track of the maximum sum + for i := 0; i < n; i++ { + currSum := 0 // Initialize the current sum to 0 + + // Consider all subarrays starting from i and ending at j + for j := i; j < n; j++ { + currSum += nums[j] // Add the jth element to the current sum + + // Update the maximum sum if the current sum is greater + if currSum > maxSum { + maxSum = currSum + } + } + } + + return maxSum +} + + +func main() { + arr := []int{-2, 1, -3, 4, -1, 2, 1, -5, 4} + maxSum := maxSubarraySum(arr) + fmt.Println("Maximum subarray sum:", maxSum) + maxSum = maxSubarraySumBruteForce(arr) + fmt.Println("Maximum subarray sum using brute force:", maxSum) +} diff --git a/Arrays/maximum_subarray_sum.py b/Arrays/maximum_subarray_sum.py new file mode 100644 index 00000000..2d8075fd --- /dev/null +++ b/Arrays/maximum_subarray_sum.py @@ -0,0 +1,58 @@ +''' + The max_subarray_sum function takes an array arr as input and returns the maximum sum of a + contiguous subarray in the given array. + + The function initializes two variables curr_sum and max_sum to the first element of the array. + It then loops through the rest of the array starting from the second element. + + Inside the loop, the function updates the curr_sum variable by adding the current element to it. + If the current sum is less than the current element, it means that starting a new subarray at this + point will result in a greater sum, so the function starts a new subarray with the current element. + The max_sum variable is then updated with the maximum value of max_sum and curr_sum. + + Once the loop is finished, the function returns the max_sum variable as the maximum sum of a + contiguous subarray in the given array. + + The time complexity of this algorithm is O(n), where n is the length of the input array, + since we only loop through the array once. + + The space complexity is O(1), since we only use + a constant amount of extra space to store the current sum and maximum sum variables. + + Example Input: arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] + Example Output: 6 + Explanation: The maximum subarray sum is [4, -1, 2, 1] which adds up to 6. + +''' +def max_subarray_sum(arr): + """ + Returns the maximum sum of a contiguous subarray in the given array. + + Parameters: + arr (list): A list of integers. + + Returns: + int: The maximum sum of a contiguous subarray. + + """ + + # Initialize variables to keep track of the current sum and maximum sum + curr_sum = max_sum = arr[0] + + # Loop through the array starting from the second element + for i in range(1, len(arr)): + + # Update the current sum by adding the current element + curr_sum += arr[i] + + # If the current sum is less than the current element, start a new subarray + curr_sum = max(curr_sum, arr[i]) + + # Update the maximum sum if the current sum is greater + max_sum = max(max_sum, curr_sum) + + # Return the maximum sum + return max_sum + +arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] +print(max_subarray_sum(arr)) \ No newline at end of file diff --git a/Arrays/merge_intervals.cpp b/Arrays/merge_intervals.cpp new file mode 100644 index 00000000..125ec096 --- /dev/null +++ b/Arrays/merge_intervals.cpp @@ -0,0 +1,34 @@ +/* Array: Merge intervals in C++ #1113 +Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. + +Example 1: + +Input: intervals = [[1,3],[2,6],[8,10],[15,18]] +Output: [[1,6],[8,10],[15,18]] +Explanation: Since intervals [1,3] and [2,6] overlap, merge them into [1,6]. +*/ + + +class Solution { +public: + vector> merge(vector>& intervals) { + vector>v; // Create an empty vector of vectors to store the merged intervals + sort(intervals.begin(), intervals.end()); // Sort the intervals based on their start times + vectorv1=intervals[0]; // Initialize the first merged interval to be the first interval in the sorted list + for(int i=1; i") + + // get the end time of the current interval and the start and end time of the next interval + currentIntervalEnd := currentInterval[1] + nextIntervalStart, nextIntervalEnd := nextInterval[0], nextInterval[1] + + // if the end time of the current interval is greater than or equal to the start time of the next interval, + // then the two intervals overlap and should be merged + if currentIntervalEnd >= nextIntervalStart { + + // set the end time of the current interval to the maximum of its current end time and the end time of the next interval + currentInterval[1] = max(currentIntervalEnd, nextIntervalEnd) + + } else { + // if the two intervals do not overlap, then the next interval becomes the new current interval and is added to the merged intervals list + currentInterval = nextInterval + mergedIntervals = append(mergedIntervals, currentInterval) + } + } + + // return the list of merged intervals + return mergedIntervals +} + +// function to return the maximum of two integers +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/Arrays/merge_intervals.java b/Arrays/merge_intervals.java new file mode 100644 index 00000000..54042dba --- /dev/null +++ b/Arrays/merge_intervals.java @@ -0,0 +1,34 @@ +/* Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. + +Example 1: + +Input: intervals = [[1,3],[2,6],[8,10],[15,18]] +Output: [[1,6],[8,10],[15,18]] +Explanation: Since intervals [1,3] and [2,6] overlap, merge them into [1,6]. */ + + + + + + + +int len = intervals.length; // Get the length of the input intervals array +int i = 0; // Initialize a variable to keep track of the current interval being merged +List result = new ArrayList<>(); // Create an empty ArrayList to store the merged intervals +Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); // Sort the intervals based on their start times using a lambda expression +int first = intervals[i][0]; // Initialize the start time of the first merged interval to be the start time of the first interval in the sorted list +int second = intervals[i][1]; // Initialize the end time of the first merged interval to be the end time of the first interval in the sorted list + +while (i < len) { // Iterate through the intervals until all have been merged + if (intervals[i][0] <= second) { // If the start time of the current interval is less than or equal to the end time of the current merged interval, they overlap + second = Math.max(second, intervals[i][1]); // Update the end time of the current merged interval to be the maximum of the two end times + } else { // If the current interval does not overlap with the current merged interval + result.add(new int[]{first, second}); // Add the current merged interval to the output list + first = intervals[i][0]; // Update the start time of the current merged interval to be the start time of the current interval + second = intervals[i][1]; // Update the end time of the current merged interval to be the end time of the current interval + } + i++; // Move on to the next interval +} +result.add(new int[]{first, second}); // Add the last merged interval to the output list +return result.toArray(new int[0][]); // Convert the output ArrayList to an array and return it + diff --git a/Arrays/merge_intervals.js b/Arrays/merge_intervals.js new file mode 100644 index 00000000..7e76b5cc --- /dev/null +++ b/Arrays/merge_intervals.js @@ -0,0 +1,63 @@ +/* + Problem definition: + Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, + and return an array of the non-overlapping intervals that cover all the intervals in the input. + + Approach: + The approach is to first sort the intervals by their start times. + Then we initialize a new merged intervals array and loop through all the intervals. + For each interval, if the merged array is empty or + the current interval does not overlap with the previous interval, + then we add the current interval to the merged array. + Otherwise, we merge the current interval with the previous interval + by updating the end time of the previous interval to the maximum of + its original end time and the end time of the current interval. + Finally, we return the merged array. + + Complexity: + The time complexity of this solution is O(n log n) due to the initial sorting step. + The space complexity is O(n) to store the merged intervals array. + + Sample input/outputs: + Example 1: + Input: [[1,4],[4,5]] + Output: [[1,5]] + + Example 2: + Input: [[1,3],[2,6],[8,10],[9,12]] + Output: [[1,6],[8,12]] + + Example 3: + Input: [[1,4],[0,4]] + Output: [[0,4]] +*/ + +function merge(intervals) { + // Sort the intervals by their start times + intervals.sort((a, b) => a[0] - b[0]); + + // Initialize a new merged intervals array + const merged = []; + + // Loop through all the intervals + for (let i = 0; i < intervals.length; i++) { + let interval = intervals[i]; + + // If the merged array is empty or the current interval does not overlap with the previous interval + // then add the current interval to the merged array + if (merged.length === 0 || interval[0] > merged[merged.length - 1][1]) { + merged.push(interval); + } else { + // Otherwise, merge the current interval with the previous interval + merged[merged.length - 1][1] = Math.max(merged[merged.length - 1][1], interval[1]); + } + } + + // Return the merged array + return merged; + } + + // Example usage: + const intervals = [[1,3],[2,6],[8,10],[15,18]]; + console.log(merge(intervals)); // [[1,6],[8,10],[15,18]] + \ No newline at end of file diff --git a/Arrays/merge_intervals.py b/Arrays/merge_intervals.py new file mode 100644 index 00000000..b55cf533 --- /dev/null +++ b/Arrays/merge_intervals.py @@ -0,0 +1,55 @@ +#Program Author : TheCodeVenturer [Niraj Modi] +''' + Problem definition: + Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, + and return an array of the non-overlapping intervals that cover all the intervals in the input. + + Approach: + The Approach to this problem is that it will be more easier if we start merging the time linear increasing fashion + To Make It in Linear Increasing Fashion We will Sort the Array + Then will initialise a new mergeIntervals Array. Here, sol + then will pick the first starting and ending point and will iterate through the array .Here, start,end + and insert it if the new start time is Greater then previous one(end) or will merge it with previous Start Time,end Time(start,end). + and at finally when the loop is over again will insert the start and end for the final Interval + then Will the mergedIntervals Array + Complexity: + Time Complexity: O(n logn) for sorting the array and additional O(n) for the complete iteration + All Over It will be O(n logn) + Space Complexity: O(1) as we are not using any extra Spaces + Space to Store Solution is not counted in Space complexity + Sample input/outputs: + Example 1: + Input: [[1,9],[4,5]] + Output: [[1,9]] + + Example 2: + Input: [[1,3],[2,6],[8,10],[9,12]] + Output: [[1,6],[8,12]] + + Example 3: + Input: [[1,4],[0,2]] + Output: [[0,4]] +''' + +class Solution(object): + def mergeIntervals(self, Intervals): + Intervals.sort() # Sorting the Intervals to make the Array in Linear Increasing Fashion + sol = [] # MergedIntervals Array Initialisation + start = Intervals[0][0] + end = Intervals[0][1] + #setting up the first start and end Point + for [l, u] in Intervals: + # if l (current start) is Greater then previous end then will add it to mergedIntervals Array + # else will update the end with the greater end that is max(end,u) + if (l > end): + sol.append([start, end]) + start, end = l, u + else: + end = max(end, u) + #finally adding the final start and end point to sol + sol.append([start, end]) + return sol +if __name__ == "__main__": + Intervals = [[1,3],[2,6],[8,10],[9,12]] + sol = Solution() + print(sol.mergeIntervals(Intervals)) diff --git a/Arrays/merge_overlapping_intervals.java b/Arrays/merge_overlapping_intervals.java deleted file mode 100644 index 4be10132..00000000 --- a/Arrays/merge_overlapping_intervals.java +++ /dev/null @@ -1,68 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * - - Write a function that takes in a non-empty array of arbitrary intervals, merges any overlapping intervals, and returns the new intervals in no particular order. - - Each interval interval is an array of two integers, with interval[0] as the start of the interval and interval[1] as the end of the interval. - - Note that back-to-back intervals aren't considered to be overlapping. For example, [1, 5] and [6, 7] aren't overlapping; however, [1, 6] and [6, 7] are indeed overlapping. - - Also note that the start of any particular interval will always be less than or equal to the end of that interval. - Sample Input - - intervals = [[1, 2], [3, 5], [4, 7], [6, 8], [9, 10]] - - Sample Output - - [[1, 2], [3, 8], [9, 10]] - // Merge the intervals [3, 5], [4, 7], and [6, 8]. - // The intervals could be ordered differently. - - - */ -public class MergeOverlappingIntervals { - public static void main(String[] args) { - int[][] intervals = {{1, 2}, - {3, 7}, - {4, 7}, - {6, 8}, - {9, 10}}; - int[][] result = solve(intervals); - System.out.println(Arrays.deepToString(result)); - } - - public static int[][] solve(int[][] intervals) { - // O(nlog(n)) time | O(n) space - //Sort the intervals by starting value. - int[][] sortedIntervals = intervals.clone(); - Arrays.sort(sortedIntervals, (a, b) -> Integer.compare(a[0], b[0])); - - List mergedIntervals = new ArrayList<>(); - int[] currentInterval = sortedIntervals[0]; - mergedIntervals.add(currentInterval); - - for (int[] nextInterval : sortedIntervals) { - int currentIntervalEnd = currentInterval[0]; - int nextIntervalStart = nextInterval[0]; - int nextIntervalEnd = nextInterval[1]; - - if (currentIntervalEnd >= nextIntervalStart) { - currentInterval[1] = Math.max(currentIntervalEnd, nextIntervalEnd); - } else { - currentInterval = nextInterval; - mergedIntervals.add(currentInterval); - } - } - return mergedIntervals.toArray(new int[mergedIntervals.size()][]); - } - -} - - - - - diff --git a/Arrays/merge_sorted_array.cpp b/Arrays/merge_sorted_array.cpp new file mode 100644 index 00000000..3a32f49f --- /dev/null +++ b/Arrays/merge_sorted_array.cpp @@ -0,0 +1,67 @@ +/* +Problem Statement :- You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, +and two integers m and n, representing the number of elements in nums1 and nums2 respectively. +Merge nums1 and nums2 into a single array sorted in non-decreasing order. +The final sorted array should not be returned by the function, but instead be stored inside the array nums1. +To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, +and the last n elements are set to 0 and should be ignored. nums2 has a length of n. +Example 1 :- +Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 +Output: [1,2,2,3,5,6] +Explanation: The arrays we are merging are [1,2,3] and [2,5,6]. +The result of the merge is [1,2,2,3,5,6] with the underlined elements coming from nums1. +Example 2 :- +Input: nums1 = [1], m = 1, nums2 = [], n = 0 +Output: [1] +Explanation: The arrays we are merging are [1] and []. +The result of the merge is [1]. +Example 3 :- +Input: nums1 = [0], m = 0, nums2 = [1], n = 1 +Output: [1] +Explanation: The arrays we are merging are [] and [1]. +The result of the merge is [1]. +Note that because m = 0, there are no elements in nums1. The 0 is only there to ensure the merge result can fit in nums1. +*/ +// Two Pointer Approch From index 0; + +void merge( vector < int > & nums1 , int m , vector< int > & nums2 , int n ) { + int i , j = 0 , u = 0 ; + vector< int > temp ; + + for( i = 0 ; i < m ; i++ ) + temp.push_back( nums1[i] ) ; // Copying the element form num1 that is needed to merged + + i = 0 ; + while( i < m && j < n ){ + + if( temp[i] > nums2[j] ) + nums1[ u++ ] = nums2[ j++ ] ; + else + nums1[ u++ ] = temp[ i++ ] ; + } + + while( i < m ) + nums1[ u++ ] = temp[ i++ ] ; + + while( j < n ) + nums1[ u++ ] = nums2[ j++ ] ; + +} + +// Two Pointer Approch From index last + + void merge( vector< int > & nums1 , int m , vector< int > & nums2 , int n ) { + + int j = n-1 , i = m-1 , k = m+n-1 ; + + while( j >= 0 ){ + if( i >= 0 && nums1[ i ] > nums2[ j ] ) + nums1[ k-- ] = nums1[ i-- ] ; + else + nums1[ k-- ] = nums2[ j-- ] ; + + } + } +/* +Tip :- Dry Run this code on Copy to Understand the key factors. +*/ diff --git a/Arrays/merge_sorted_array.java b/Arrays/merge_sorted_array.java new file mode 100644 index 00000000..02e75427 --- /dev/null +++ b/Arrays/merge_sorted_array.java @@ -0,0 +1,38 @@ +//You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers +// m and n, representing the number of elements in nums1 and nums2 respectively. + +//Merge nums1 and nums2 into a single array sorted in non-decreasing order. + +//The final sorted array should not be returned by the function, but instead be stored inside the array nums1. +// To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that +// should be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n. + + +import java.util.Arrays; + +class merge_sorted_array{ + + public void merge(int[] nums1, int m, int[] nums2, int n){ + int[] merged=new int[m+n]; //new merged array + //copying the first m elements from nums1 to the merged array + if (m >= 0) System.arraycopy(nums1, 0, merged, 0, m); + //copying the first n elements from nums2 to the merged array + if (n >= 0) System.arraycopy(nums2, 0, merged, m + 0, n); + //sorting the merged array + Arrays.sort(merged); + //copying the merged array to the nums1 array + System.arraycopy(merged, 0, nums1, 0, nums1.length); + } + + public static void main(String[] args){ + int[] nums1 = {1, 2, 3, 0, 0, 0}; + int m = 3, n = 3; + int[] nums2 ={2, 5, 6}; + + merge_sorted_array is = new merge_sorted_array(); + is.merge(nums1, m, nums2, n); + for (int j : nums1) { + System.out.print(j + " "); + } + } +} diff --git a/Arrays/merge_sorted_arrays.py b/Arrays/merge_sorted_arrays.py new file mode 100644 index 00000000..e7d0813d --- /dev/null +++ b/Arrays/merge_sorted_arrays.py @@ -0,0 +1,33 @@ +class Solution: + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + #Time Complexity - O(M+N) + #Space Complexity - O(1) + + #Using similar to merge sort algorithm implementation. + #Traversing in reverse order and comparing m,n values. + #Placing the highest element last and continuing with the algorithm. + i, j, k = m - 1, n - 1, m + n - 1 + while(i >= 0 and j >= 0): + if(nums1[i] > nums2[j]): + nums1[k] = nums1[i] + i -= 1 + k -= 1 + else: + nums1[k] = nums2[j] + j -= 1 + k -= 1 + + #placing left over elements from num1 to nums1 + while(i >= 0): + nums1[k] = nums1[i] + i -= 1 + k -= 1 + + #placing left over elements from num2 to num1 + while(j >= 0): + nums1[k] = nums2[j] + j -= 1 + k -= 1 diff --git a/Arrays/min_rewards.java b/Arrays/min_rewards.java deleted file mode 100644 index d676040a..00000000 --- a/Arrays/min_rewards.java +++ /dev/null @@ -1,47 +0,0 @@ -import java.util.Arrays; -import java.util.stream.IntStream; - -/** - * - - Imagine that you're a teacher who's just graded the final exam in a class. You have a list of student scores on the final exam in a particular order (not necessarily sorted), and you want to reward your students. You decide to do so fairly by giving them arbitrary rewards following two rules: - - All students must receive at least one reward. - Any given student must receive strictly more rewards than an adjacent student (a student immediately to the left or to the right) with a lower score and must receive strictly fewer rewards than an adjacent student with a higher score. - - Write a function that takes in a list of scores and returns the minimum number of rewards that you must give out to students to satisfy the two rules. - - You can assume that all students have different scores; in other words, the scores are all unique. - Sample Input - - scores = [8, 4, 2, 1, 3, 6, 7, 9, 5] - - Sample Output - - 25 // you would give out the following rewards: [4, 3, 2, 1, 2, 3, 4, 5, 1] - - - */ -public class MinRewards { - - public static void main(String[] args) { - int[] scores = {8, 4, 2, 1, 3, 6, 7, 9, 5}; - System.out.println(solve(scores)); - } - - public static int solve(int[] scores) { - // O(n) time | O(n) space - int[] rewards = new int[scores.length]; - Arrays.fill(rewards, 1); - - for (int i = 1; i < scores.length; i++) { - if (scores[i] > scores[i - 1]) rewards[i] = rewards[i - 1] + 1; - } - for (int i = scores.length - 2; i > -1; i--) { - if (scores[i] > scores[i + 1]) rewards[i] = Math.max(rewards[i], rewards[i + 1] + 1); - } - - return IntStream.of(rewards).sum(); - } - -} diff --git a/Arrays/minimum_size_subarray_sum.java b/Arrays/minimum_size_subarray_sum.java new file mode 100644 index 00000000..e685bd1f --- /dev/null +++ b/Arrays/minimum_size_subarray_sum.java @@ -0,0 +1,82 @@ +/** + * Given an array of positive integers nums and a positive integer target, return the minimal length of a subarray whose sum is greater than or equal to target. If there is no such subarray, return 0 instead. + * + * + * + * Example 1: + * + * Input: target = 7, nums = [2,3,1,2,4,3] + * Output: 2 + * Explanation: The subarray [4,3] has the minimal length under the problem constraint. + * Example 2: + * + * Input: target = 4, nums = [1,4,4] + * Output: 1 + * Example 3: + * + * Input: target = 11, nums = [1,1,1,1,1,1,1,1] + * Output: 0 + * + * + * Constraints: + * + * 1 <= target <= 109 + * 1 <= nums.length <= 105 + * 1 <= nums[i] <= 104 + * + * https://leetcode.com/problems/minimum-size-subarray-sum/ + */ +public class MinimumSizeSubarraySum { + public static void main(String[] args) { + int[] nums = {2, 3, 1, 2, 4, 3}; + + int target = 7; + int ans = solve(nums, target); + System.out.println(ans); + } + public static int solve(int[] nums, int target) { + // O(N) time | O(1) space + + // Sliding Window + + int leftIdx = 0, + rightIdx = 0, + currentSum = 0, + minIdx = Integer.MAX_VALUE, + len = nums.length; + + // Variable size sliding window: 2 - pointer + while (rightIdx < len) { + int currentNum = nums[rightIdx]; + currentSum += currentNum; + + if (currentSum >= target) { // Check id currentSum >= target + //Skip all left elements until currentSum < target (To find the smallest window) + while (currentSum >= target) { + currentSum -= nums[leftIdx++]; + } + int currentWindowSize = rightIdx - leftIdx + 1 + 1; // including leftIdx - 1 idx and update smallest window size. + minIdx = Math.min(minIdx, currentWindowSize); + } + rightIdx++; + } + return minIdx == Integer.MAX_VALUE ? 0 : minIdx; + + // O(N^2) time | O(1) space +// int ans = Integer.MAX_VALUE, len = nums.length; +// for (int i = 0; i < len; i++) {; +// int sum = 0; +// for (int j = i; j < len; j++) { +// int currentNum = nums[j]; +// sum += currentNum; +// if (sum >= target) { +// ans = Math.min(ans, j - i + 1); +// } +// } +// } +// return ans == Integer.MAX_VALUE ? 0 : ans; + + + + } +} diff --git a/Arrays/minimum_swaps.java b/Arrays/minimum_swaps.java deleted file mode 100644 index 091c2445..00000000 --- a/Arrays/minimum_swaps.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Given an array of integers A and an integer B, find and return the minimum number of swaps required to bring all the numbers less than or equal to B together. - * - * Note: It is possible to swap any two elements, not necessarily consecutive. - * - * - * - * Problem Constraints - * - * 1 <= length of the array <= 100000 - * -109 <= A[i], B <= 109 - * - * - * - * Input Format - * - * The first argument given is the integer array A. - * The second argument given is the integer B. - * - * - * - * Output Format - * - * Return the minimum number of swaps. - * - * - * - * Example Input - * - * Input 1: - * - * A = [1, 12, 10, 3, 14, 10, 5] - * B = 8 - * Input 2: - * - * A = [5, 17, 100, 11] - * B = 20 - * - * - * Example Output - * - * Output 1: - * - * 2 - * Output 2: - * - * 1 - * - * - * Example Explanation - * - * Explanation 1: - * - * A = [1, 12, 10, 3, 14, 10, 5] - * After swapping 12 and 3, A => [1, 3, 10, 12, 14, 10, 5]. - * After swapping the first occurence of 10 and 5, A => [1, 3, 5, 12, 14, 10, 10]. - * Now, all elements less than or equal to 8 are together. - * Explanation 2: - * - * A = [5, 17, 100, 11] - * After swapping 100 and 11, A => [5, 17, 11, 100]. - * Now, all elements less than or equal to 20 are together. - * - */ - -package SlidingWindow; - -public class MinimumSwaps { - public static void main(String[] args) { - final long startTime = System.currentTimeMillis(); - final long beforeUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - final long endTime = System.currentTimeMillis(); - - - int[] array = {1, 12, 10, 3, 14, 10, 5}; - int b = 8; - int ans = solve(array, b); - System.out.println(ans); - - final long afterUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - final long actualMemUsed = afterUsedMem-beforeUsedMem; - System.out.println("Runtime " + (endTime - startTime) + " ms"); - System.out.println("Space " + actualMemUsed + " B"); - } - public static int solve(int[] array, int b) { - //O(n) time | O(1) space - - int minSwaps; - int length = array.length; - - // Find count of all the elements <= b - int k = 0; - for (int i = 0; i < length; i++) - if (array[i] <= b) k++; - - // Calculate the count of elements > b for the first window (i.e k length) - int count = 0; - for (int i = 0; i < k; i++) { - if (array[i] > b) count++; - } - minSwaps = count; - - // Consider remaining windows - int startIdx = 1; - int endIdx = k; - while (endIdx < length) { - if (array[endIdx] > b) count++; - if (array[startIdx - 1] > b) count--; - minSwaps = Math.min(count, minSwaps); - startIdx++; - endIdx++; - } - - return minSwaps; - } -} diff --git a/Arrays/move_element_to_end.cpp b/Arrays/move_element_to_end.cpp new file mode 100644 index 00000000..dffc815a --- /dev/null +++ b/Arrays/move_element_to_end.cpp @@ -0,0 +1,37 @@ +/* + Move Element to end + Sample Input : [1, 0, 3, 0, 0, 5] To move: 0 + Output : [1, 3, 5, 0, 0, 0] + + This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, + and returns a modified array with all instances of toMove moved to the end of the array. + + The function first initializes an integer variable index to 0, which will keep track of the index of the first + element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the + current element is not equal to toMove, it replaces the element at the index position with the current element + and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the + beginning of the array. + + Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in + the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the + end of the array. + + Finally, the modified array is returned. + + O(n) time | O(1) space - where n is the length of the array +*/ +#include + +std::vector MoveElementToEnd(std::vector& array, int toMove) { + int index = 0; // initialize a variable to keep track of the index where elements should be moved to + for (int i = 0; i < array.size(); i++) { // loop through the entire array + if (array[i] != toMove) { // check if the current element is not equal to the element to be moved + array[index] = array[i]; // move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array[i]) + index++; // increment the index variable by 1 to keep track of the index where the next non-target element should be moved + } + } + for (int i = index; i < array.size(); i++) { // loop through the remaining elements in the array from index to the end + array[i] = toMove; // set each element to be the target element + } + return array; // return the modified array +} diff --git a/Arrays/move_element_to_end.go b/Arrays/move_element_to_end.go new file mode 100644 index 00000000..15c210f9 --- /dev/null +++ b/Arrays/move_element_to_end.go @@ -0,0 +1,37 @@ +/* + Move Element to end + Sample Input : [1, 0, 3, 0, 0, 5] To move: 0 + Output : [1, 3, 5, 0, 0, 0] + + This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, + and returns a modified array with all instances of toMove moved to the end of the array. + + The function first initializes an integer variable index to 0, which will keep track of the index of the first + element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the + current element is not equal to toMove, it replaces the element at the index position with the current element + and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the + beginning of the array. + + Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in + the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the + end of the array. + + Finally, the modified array is returned. + + O(n) time | O(1) space - where n is the length of the array +*/ +package main + +func MoveElementToEnd(array []int, toMove int) []int { + index := 0 // initialize a variable to keep track of the index where elements should be moved to + for i := 0; i < len(array); i++ { // loop through the entire array + if array[i] != toMove { // check if the current element is not equal to the element to be moved + array[index] = array[i] // move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array[i]) + index++ // increment the index variable by 1 to keep track of the index where the next non-target element should be moved + } + } + for i := index; i < len(array); i++ { // loop through the remaining elements in the array from index to the end + array[i] = toMove // set each element to be the target element + } + return array // return the modified array +} diff --git a/Arrays/move_element_to_end.java b/Arrays/move_element_to_end.java new file mode 100644 index 00000000..25959ad1 --- /dev/null +++ b/Arrays/move_element_to_end.java @@ -0,0 +1,40 @@ +/* + Move Element to end + Sample Input : [1, 0, 3, 0, 0, 5] To move: 0 + Output : [1, 3, 5, 0, 0, 0] + + This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, + and returns a modified array with all instances of toMove moved to the end of the array. + + The function first initializes an integer variable index to 0, which will keep track of the index of the first + element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the + current element is not equal to toMove, it replaces the element at the index position with the current element + and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the + beginning of the array. + + Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in + the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the + end of the array. + + Finally, the modified array is returned. + + O(n) time | O(1) space - where n is the length of the array +*/ + +import java.util.List; + +public class MoveElementToEnd { + public static List moveElementToEnd(List array, int toMove) { + int index = 0; // initialize a variable to keep track of the index where elements should be moved to + for (int i = 0; i < array.size(); i++) { // loop through the entire array + if (array.get(i) != toMove) { // check if the current element is not equal to the element to be moved + array.set(index, array.get(i)); // move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array.get(i)) + index++; // increment the index variable by 1 to keep track of the index where the next non-target element should be moved + } + } + for (int i = index; i < array.size(); i++) { // loop through the remaining elements in the array from index to the end + array.set(i, toMove); // set each element to be the target element + } + return array; // return the modified array + } +} diff --git a/Arrays/move_element_to_end.js b/Arrays/move_element_to_end.js new file mode 100644 index 00000000..f6aed32c --- /dev/null +++ b/Arrays/move_element_to_end.js @@ -0,0 +1,38 @@ +/* + Move Element to end + Sample Input : [1, 0, 3, 0, 0, 5] To move: 0 + Output : [1, 3, 5, 0, 0, 0] + + This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, + and returns a modified array with all instances of toMove moved to the end of the array. + + The function first initializes an integer variable index to 0, which will keep track of the index of the first + element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the + current element is not equal to toMove, it replaces the element at the index position with the current element + and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the + beginning of the array. + + Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in + the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the + end of the array. + + Finally, the modified array is returned. + + O(n) time | O(1) space - where n is the length of the array +*/ +function moveElementToEnd(array, toMove) { + let index = 0; // initialize a variable to keep track of the index where elements should be moved to + for (let i = 0; i < array.length; i++) { + // loop through the entire array + if (array[i] !== toMove) { + // check if the current element is not equal to the element to be moved + array[index] = array[i]; // move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array[i]) + index++; // increment the index variable by 1 to keep track of the index where the next non-target element should be moved + } + } + for (let i = index; i < array.length; i++) { + // loop through the remaining elements in the array from index to the end + array[i] = toMove; // set each element to be the target element + } + return array; // return the modified array +} diff --git a/Arrays/move_element_to_end.py b/Arrays/move_element_to_end.py new file mode 100644 index 00000000..85b18c3f --- /dev/null +++ b/Arrays/move_element_to_end.py @@ -0,0 +1,31 @@ +''' + Move 0's to end + Sample Input : [1, 0, 3, 0, 0, 5] + Output : [1, 3, 5, 0, 0, 0] + + This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, + and returns a modified array with all instances of toMove moved to the end of the array. + + The function first initializes an integer variable index to 0, which will keep track of the index of the first + element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the + current element is not equal to toMove, it replaces the element at the index position with the current element + and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the + beginning of the array. + + Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in + the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the + end of the array. + + Finally, the modified array is returned. + + O(n) time | O(1) space - where n is the length of the array +''' +def move_element_to_end(array, to_move): + index = 0 # initialize a variable to keep track of the index where elements should be moved to + for i in range(len(array)): # loop through the entire array + if array[i] != to_move: # check if the current element is not equal to the element to be moved + array[index] = array[i] # move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array[i]) + index += 1 # increment the index variable by 1 to keep track of the index where the next non-target element should be moved + for i in range(index, len(array)): # loop through the remaining elements in the array from index to the end + array[i] = to_move # set each element to be the target element + return array # return the modified array diff --git a/Arrays/multiple_left_rotations.java b/Arrays/multiple_left_rotations.java deleted file mode 100644 index 066b3507..00000000 --- a/Arrays/multiple_left_rotations.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Given an array of integers A and multiple values in B, which represents the number of times array A needs to be left rotated. - * - * Find the rotated array for each value and return the result in the from of a matrix where ith row represents the rotated array for the ith value in B. - * - * - * - * Problem Constraints - * 1 <= length of both arrays <= 2000 -10^9 <= A[i] <= 10^9 0 <= B[i] <= 2000 - * - * - * Input Format - * The first argument given is the integer array A. - * The second argument given is the integer array B. - * - * - * Output Format - * Return the resultant matrix. - * - * - * Example Input - * Input 1: - * - * A = [1, 2, 3, 4, 5] - * B = [2, 3] - * - * Input 2: - * - * - * A = [5, 17, 100, 11] - * B = [1] - * - * - * - * - * Example Output - * Output 1: - * - * [ [3, 4, 5, 1, 2] - * [4, 5, 1, 2, 3] ] - * - * - * Output 2: - * - * - * [ [17, 100, 11, 5] ] - * - * - * - * Example Explanation - * for input 1 -> B[0] = 2 which requires 2 times left rotations - * - * 1: [2, 3, 4, 5, 1] - * - * 2: [3, 4, 5, 1, 2] - * - * B[1] = 3 which requires 3 times left rotation - * - * 1: [2, 3, 4, 5, 1] - * - * 2: [3, 4, 5, 1, 2] - * - * 2: [4, 5, 1, 2, 4] - */ - -package InterviewProblems; - -import java.util.Arrays; - -public class MultipleLeftRotationsOfArray { - public static void main(String[] args) { - int[] A = {1, 2, 3, 4, 5}; - int[] B = {2, 3}; - int[][] ans = solve(A, B); - System.out.println(Arrays.deepToString(ans)); - } - public static int[][] solve(int[] arr, int[] b) { - // O(N * K) time | O(N * K) space where K is size of b && N is length of arr. - int n = arr.length; - int [][] ans = new int[b.length][n]; - - for (int i = 0; i < b.length; i++) { - int num =b[i]; - int[] temp = new int[n]; - for (int j = 0; j < n; j++) { - temp[j] = arr[(num + j) % n]; - } - ans[i] = temp; - } - return ans; - } -} diff --git a/Arrays/next_greater_element.cpp b/Arrays/next_greater_element.cpp deleted file mode 100644 index 953fe916..00000000 --- a/Arrays/next_greater_element.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* -The next greater element of some element x in an array is the first greater element that is to the right of x in the same array. - -You are given two distinct 0-indexed integer arrays nums1 and nums2, where nums1 is a subset of nums2. - -For each 0 <= i < nums1.length, find the index j such that nums1[i] == nums2[j] and determine the next greater element of nums2[j] in nums2. If there is no next greater element, then the answer for this query is -1. - -Return an array ans of length nums1.length such that ans[i] is the next greater element as described above. - - - -Example 1: - -Input: nums1 = [4,1,2], nums2 = [1,3,4,2] -Output: [-1,3,-1] -Explanation: The next greater element for each value of nums1 is as follows: -- 4 is underlined in nums2 = [1,3,4,2]. There is no next greater element, so the answer is -1. -- 1 is underlined in nums2 = [1,3,4,2]. The next greater element is 3. -- 2 is underlined in nums2 = [1,3,4,2]. There is no next greater element, so the answer is -1. -Example 2: - -Input: nums1 = [2,4], nums2 = [1,2,3,4] -Output: [3,-1] -Explanation: The next greater element for each value of nums1 is as follows: -- 2 is underlined in nums2 = [1,2,3,4]. The next greater element is 3. -- 4 is underlined in nums2 = [1,2,3,4]. There is no next greater element, so the answer is -1. - - -Constraints: - -1 <= nums1.length <= nums2.length <= 1000 -0 <= nums1[i], nums2[i] <= 104 -All integers in nums1 and nums2 are unique. -All the integers of nums1 also appear in nums2. -*/ - -class Solution { -public: - vector nextGreaterElement(vector& nums1, vector& nums2) { - vector result(nums1.size(), -1); - stack st; - unordered_map umap; - for(auto element : nums2){ - while(!st.empty() && element > st.top()){ - umap[st.top()] = element; - st.pop(); - } - st.push(element); - } - for(int i = 0; i < nums1.size(); i++){ - if(umap.find(nums1[i]) != umap.end()){ - result[i] = umap[nums1[i]]; - } - } - return result; - } -}; \ No newline at end of file diff --git a/Arrays/non-overlapping intervals.cpp b/Arrays/non-overlapping intervals.cpp new file mode 100644 index 00000000..b17b8d88 --- /dev/null +++ b/Arrays/non-overlapping intervals.cpp @@ -0,0 +1,50 @@ +// Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping. +// Example 1: + +// Input: intervals = [[1,2],[2,3],[3,4],[1,3]] +// Output: 1 +// Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping. +// Example 2: + +// Input: intervals = [[1,2],[1,2],[1,2]] +// Output: 2 +// Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping. +// Example 3: + +// Input: intervals = [[1,2],[2,3]] +// Output: 0 +// Explanation: You don't need to remove any of the intervals since they're already non-overlapping. + + +// Constraints: +// 1 <= intervals.length <= 105 +// intervals[i].length == 2 +// -5 * 104 <= starti < endi <= 5 * 104 + +// CODE: + + class Solution { +public: + int eraseOverlapIntervals(vector>& intervals) { + sort(intervals.begin(),intervals.end()); + int previous = 0; + int n = intervals.size(); + int ans = 0; + for(int current = 1;current currentChangeCreated + 1) return currentChangeCreated + 1; - currentChangeCreated += coin; - } - return currentChangeCreated + 1; - } - -} diff --git a/Arrays/non_constructible_change.go b/Arrays/non_constructible_change.go deleted file mode 100644 index caf40ab9..00000000 --- a/Arrays/non_constructible_change.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - Given an array of positive integers representing the values of coins in your - possession, write a function that returns the minimum amount of change (the - minimum sum of money) that you - - For example, if you're given coins = [1, 2, 5] , the minimum - amount of change that you can't create is 4, . If you're given no - coins, the minimum amount of change that you can't create is 1 - - Sample Input: [1, 5, 1, 1, 1, 10, 15, 20, 100] - Output: 55 -*/ -package main - -import ( - "fmt" - "sort" -) - - -func NonConstructibleChange(coins []int) int { - // Write your code here. - result := 0 - sort.Ints(coins) - - for i := 0; i < len(coins); i++ { - nextGreaterCoin := result + 1 - if coins[i] > nextGreaterCoin { - return nextGreaterCoin - } else if coins[i] <= nextGreaterCoin { - result += coins[i] - } - } - return result + 1 -} - - -func main() { - coins := []int{1, 5, 1, 1, 1, 10, 15, 20, 100} - - msg := NonConstructibleChange(coins) - fmt.Println(msg) -} \ No newline at end of file diff --git a/Arrays/odd_indices_sum.java b/Arrays/odd_indices_sum.java deleted file mode 100644 index 9101ea18..00000000 --- a/Arrays/odd_indices_sum.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * You are given an array A of length N and Q queries given by the 2D array B of size Q*2. Each query consists of two integers B[i][0] and B[i][1]. - * For every query, the task is to calculate the sum of all odd indices in the range A[B[i][0]…B[i][1]]. - * - * Note : Use 0-based indexing - * - * - * Problem Constraints - * 1 <= N <= 105 - * 1 <= Q <= 105 - * 1 <= A[i] <= 100 - * 0 <= B[i][0] <= B[i][1] < N - * - * - * Input Format - * First argument A is an array of integers. - * Second argument B is a 2D array of integers. - * - * - * Output Format - * Return an array of integers. - * - * - * Example Input - * Input 1: - * A = [1, 2, 3, 4, 5] - * B = [ [0,2] - * [1,4] ] - * Input 2: - * A = [2, 1, 8, 3, 9] - * B = [ [0,3] - * [2,4] ] - * - * - * Example Output - * Output 1: - * [2, 6] - * Output 2: - * [4, 3] - * - * - * Example Explanation - * For Input 1: - * The subarray for the first query is [1, 2, 3] whose sum of odd indices is 2. - * The subarray for the second query is [2, 3, 4, 5] whose sum of odd indices is 6. - * For Input 2: - * The subarray for the first query is [2, 1, 8, 3] whose sum of odd indices is 4. - * The subarray for the second query is [8, 3, 9] whose sum of odd indices is 3. - */ -package InterviewProblems; - -import java.util.Arrays; - -public class oddIndicesSum { - public static void main(String[] args) { - int[] a = {2, 1, 8, 3, 9}; - int[][] b = { {0, 3}, {2, 4} }; - int[] ans = solve(a, b); - System.out.println(Arrays.toString(ans)); - } - public static int[] solve(int[] a, int[][] b) { - // O(Q*N) time | O(N) space - int len = a.length; - int[] ans = new int[b.length]; - int[] oddIndicesSum = new int[len]; - - oddIndicesSum[0] = 0; - for (int i = 1; i < len; i++) { - int currentNum = a[i]; - oddIndicesSum[i] = i % 2 != 0 ? oddIndicesSum[i - 1] + currentNum : oddIndicesSum[i - 1]; - } - - for (int i = 0; i < b.length; i++) { - int[] currentQuery = b[i]; - int startIdx = currentQuery[0]; - int endIdx = currentQuery[1]; - - ans[i] = startIdx == 0 ? oddIndicesSum[endIdx] : oddIndicesSum[endIdx] - oddIndicesSum[startIdx - 1]; - } - return ans; - } -} diff --git a/CCTI/arrays_strings_pallindromic_permutations.cpp b/Arrays/pallindromic_permutations.cpp similarity index 65% rename from CCTI/arrays_strings_pallindromic_permutations.cpp rename to Arrays/pallindromic_permutations.cpp index cfa3048a..d3c9bb0f 100644 --- a/CCTI/arrays_strings_pallindromic_permutations.cpp +++ b/Arrays/pallindromic_permutations.cpp @@ -1,62 +1,62 @@ -// Write a function to check if it is a permutation of a pallindrome -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -// TC O(n) n is length of string -bool is_permutation_pallindrome(string s){ - // we built a hash table to count how many times each character appears - // we then iterate through hashtable to find out no more than one character has - // odd count - vector table(26, 0); - bool found_odd = false; - for(int i = 0; i < s.length(); i++){ - table[s[i]-'a']++; - } - for(int x : table){ - if(x&1){ - if(found_odd){ - return false; - } - found_odd = true; - } - } - return true; -} -bool is_permutation_pallindrome_improved(string s){ - // instead of checking oddcounts at end we can check - // as we traverse the chars in string, as soon as we reach end - // we have out answer. - vector table(26, 0); - int count_odd = 0; - for(int i = 0; i < s.length(); i++){ - int x = s[i]-'a'; - table[x]++; - if(table[x] & 1){ - count_odd++; - } - else{ - count_odd--; - } - } - return count_odd <= 1; -} -void print_ans(bool ans){ - if(ans){ - cout << "YES\n"; - } - else{ - cout << "NO\n"; - } -} -int main(){ - is_permutation_pallindrome("aaabdddcba") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome("abab") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome("ABA") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome("abcddeab") == true ? print_ans(true) : print_ans(false); - cout << "**********IMPROVED*********\n"; - is_permutation_pallindrome_improved("a") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome_improved("ab") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome_improved("ABA") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome_improved("abcddeab") == true ? print_ans(true) : print_ans(false); - return 0; +// Write a function to check if it is a permutation of a pallindrome +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +// TC O(n) n is length of string +bool is_permutation_pallindrome(string s){ + // we built a hash table to count how many times each character appears + // we then iterate through hashtable to find out no more than one character has + // odd count + vector table(26, 0); + bool found_odd = false; + for(int i = 0; i < s.length(); i++){ + table[s[i]-'a']++; + } + for(int x : table){ + if(x&1){ + if(found_odd){ + return false; + } + found_odd = true; + } + } + return true; +} +bool is_permutation_pallindrome_improved(string s){ + // instead of checking oddcounts at end we can check + // as we traverse the chars in string, as soon as we reach end + // we have out answer. + vector table(26, 0); + int count_odd = 0; + for(int i = 0; i < s.length(); i++){ + int x = s[i]-'a'; + table[x]++; + if(table[x] & 1){ + count_odd++; + } + else{ + count_odd--; + } + } + return count_odd <= 1; +} +void print_ans(bool ans){ + if(ans){ + cout << "YES\n"; + } + else{ + cout << "NO\n"; + } +} +int main(){ + is_permutation_pallindrome("aaabdddcba") ? print_ans(true) : print_ans(false); + is_permutation_pallindrome("abab") ? print_ans(true) : print_ans(false); + is_permutation_pallindrome("ABA") ? print_ans(true) : print_ans(false); + is_permutation_pallindrome("abcddeab") ? print_ans(true) : print_ans(false); + cout << "**********IMPROVED*********\n"; + is_permutation_pallindrome_improved("a") ? print_ans(true) : print_ans(false); + is_permutation_pallindrome_improved("ab") ? print_ans(true) : print_ans(false); + is_permutation_pallindrome_improved("ABA") ? print_ans(true) : print_ans(false); + is_permutation_pallindrome_improved("abcddeab") ? print_ans(true) : print_ans(false); + return 0; } \ No newline at end of file diff --git a/Arrays/quilibruim_index.java b/Arrays/quilibruim_index.java deleted file mode 100644 index 1b07b30e..00000000 --- a/Arrays/quilibruim_index.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * You are given an array A of integers of size N. - * - * Your task is to find the equilibrium index of the given array - * - * The equilibrium index of an array is an index such that the sum of elements at lower indexes is equal to the sum of elements at higher indexes. - * - * NOTE: - * - * Array indexing starts from 0. - * If there is no equilibrium index then return -1. - * If there are more than one equilibrium indexes then return the minimum index. - * - * - * - * Problem Constraints - * 1 <= N <= 105 - * -105 <= A[i] <= 105 - * - * - * Input Format - * First arugment is an array A . - * - * - * Output Format - * Return the equilibrium index of the given array. If no such index is found then return -1. - * - * - * Example Input - * Input 1: - * A=[-7, 1, 5, 2, -4, 3, 0] - * Input 2: - * - * A=[1,2,3] - * - * - * Example Output - * Output 1: - * 3 - * Output 2: - * - * -1 - * - * - * Example Explanation - * Explanation 1: - * 3 is an equilibrium index, because: - * A[0] + A[1] + A[2] = A[4] + A[5] + A[6] - * Explanation 1: - * - * There is no such index. - */ - -package PrefixSum; - -import java.util.ArrayList; -import java.util.Arrays; - -public class EquilibriumIndex { - public static void main(String[] args) { - ArrayList arr = new ArrayList<>( - Arrays.asList(-7, 1, 5, 2, -4, 3, 0)); - int ans = solve(arr); - System.out.println(ans); - } - public static int solve(ArrayList array) { - // O(N) time | O(N) space - ArrayList prefixSum = new ArrayList<>(); - - prefixSum.add(array.get(0)); - for (int i = 1; i < array.size(); i++) { - int currentPrefixSum = prefixSum.get(i - 1) + array.get(i); - prefixSum.add(currentPrefixSum); - } - - for (int i = 0; i < array.size(); i++) { - if (i == 0 ) { - if (prefixSum.get(prefixSum.size() - 1) - prefixSum.get(i) == 0) return i; - continue; - } - if (prefixSum.get(i - 1) == prefixSum.get(prefixSum.size() - 1) - prefixSum.get(i)) - return i; - } - return -1; - } -} diff --git a/Arrays/removeDuplicates.js b/Arrays/removeDuplicates.js deleted file mode 100644 index 54d21f5f..00000000 --- a/Arrays/removeDuplicates.js +++ /dev/null @@ -1,41 +0,0 @@ - -/* -Given an integer array nums sorted in non-decreasing order, -remove the duplicates in-place such that each unique element appears only once. - The relative order of the elements should be kept the same. -Since it is impossible to change the length of the array in some languages, -you must instead have the result be placed in the first part of the array nums. -More formally, if there are k elements after removing the duplicates, -then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements. -Return k after placing the final result in the first k slots of nums. -Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory. - -Example 1: - -Input: nums = [1,1,2] -Output: 2, nums = [1,2,_] -Explanation: Your function should return k = 2, with the first two elements of nums being 1 and 2 respectively. -It does not matter what you leave beyond the returned k (hence they are underscores). -Example 2: - -Input: nums = [0,0,1,1,1,2,2,3,3,4] -Output: 5, nums = [0,1,2,3,4,_,_,_,_,_] -Explanation: Your function should return k = 5, with the first five elements of nums being 0, 1, 2, 3, and 4 respectively. -It does not matter what you leave beyond the returned k (hence they are underscores). - - */ -let nums=[1 ,2 ,2 ,2 ,45 ,67 ,89 ,89 ,89]; - -const removeDuplicates = (nums) => { - let count = 0; - for (let i = 0; i < nums.length; i++) { - if (i < nums.length - 1 && nums[i] == nums[i + 1]) { - continue; - } - nums[count] = nums[i]; - count++; - } - return count; -}; - -console.log(removeDuplicates(nums)); \ No newline at end of file diff --git a/Arrays/remove_adjacant_duplicates.cpp b/Arrays/remove_adjacant_duplicates.cpp deleted file mode 100644 index cfad981a..00000000 --- a/Arrays/remove_adjacant_duplicates.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters from s and removing them, causing the left and the right side of the deleted substring to concatenate together. - -We repeatedly make k duplicate removals on s until we no longer can. - -Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique. - - - -Example 1: - -Input: s = "abcd", k = 2 -Output: "abcd" -Explanation: There's nothing to delete. -Example 2: - -Input: s = "deeedbbcccbdaa", k = 3 -Output: "aa" -Explanation: -First delete "eee" and "ccc", get "ddbbbdaa" -Then delete "bbb", get "dddaa" -Finally delete "ddd", get "aa" -Example 3: - -Input: s = "pbbcggttciiippooaais", k = 2 -Output: "ps" - - -Constraints: - -1 <= s.length <= 105 -2 <= k <= 104 -s only contains lowercase English letters. -*/ - -class Solution { -public: - /* class Pair{ - public: - char first; - int second; - - Pair(char f, int s){ - first = f; - second = s; - } - }; - */ - string removeDuplicates(string s, int k) { - stack > St; - for(int i = 0; i < s.size(); i++){ - if(St.empty() || s[i] != St.top().first){ - St.push(make_pair(s[i], 1)); - } - else{ - St.top().second++; - int count = St.top().second; - if(count == k){ - St.pop(); - } - } - } - string ans = ""; - while(!St.empty()){ - int count = St.top().second; - while(count--){ - ans += St.top().first; - } - St.pop(); - } - reverse(ans.begin(), ans.end()); - return ans; - } -}; \ No newline at end of file diff --git a/Arrays/richest_customer_wealth.cpp b/Arrays/richest_customer_wealth.cpp deleted file mode 100644 index 77d1c467..00000000 --- a/Arrays/richest_customer_wealth.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* -You are given an m x n integer grid accounts where accounts[i][j] is the amount of money the i​​​​​​​​​​​th​​​​ customer has in the j​​​​​​​​​​​th​​​​ bank. Return the wealth that the richest customer has. - -A customer's wealth is the amount of money they have in all their bank accounts. The richest customer is the customer that has the maximum wealth. - - - -Example 1: - -Input: accounts = [[1,2,3],[3,2,1]] -Output: 6 -Explanation: -1st customer has wealth = 1 + 2 + 3 = 6 -2nd customer has wealth = 3 + 2 + 1 = 6 -Both customers are considered the richest with a wealth of 6 each, so return 6. -Example 2: - -Input: accounts = [[1,5],[7,3],[3,5]] -Output: 10 -Explanation: -1st customer has wealth = 6 -2nd customer has wealth = 10 -3rd customer has wealth = 8 -The 2nd customer is the richest with a wealth of 10. -Example 3: - -Input: accounts = [[2,8,7],[7,1,3],[1,9,5]] -Output: 17 - - -Constraints: - -m == accounts.length -n == accounts[i].length -1 <= m, n <= 50 -1 <= accounts[i][j] <= 100 -*/ -class Solution { -public: - int maximumWealth(vector>& accounts) { - int result = 0; - for(int i = 0; i < accounts.size(); i++){ - int temp = 0; - for(int j = 0; j < accounts[i].size(); j++){ - temp += accounts[i][j]; - } - result = max(temp, result); - } - return result; - } -}; \ No newline at end of file diff --git a/Arrays/rotate_matrix_90degrees.java b/Arrays/rotate_matrix_90degrees.java deleted file mode 100644 index 2400dcca..00000000 --- a/Arrays/rotate_matrix_90degrees.java +++ /dev/null @@ -1,112 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * You are given a n x n 2D matrix A representing an image. - * - * Rotate the image by 90 degrees (clockwise). - * - * You need to do this in place. - * - * Note: If you end up using an additional array, you will only receive partial score. - * - * - * - * Problem Constraints - * 1 <= n <= 1000 - * - * - * - * Input Format - * First argument is a 2D matrix A of integers - * - * - * - * Output Format - * Return the 2D rotated matrix. - * - * - * - * Example Input - * Input 1: - * - * [ - * [1, 2], - * [3, 4] - * ] - * Input 2: - * - * [ - * [1] - * ] - * - * - * Example Output - * Output 1: - * - * [ - * [3, 1], - * [4, 2] - * ] - * Output 2: - * - * [ - * [1] - * ] - * - * - * Example Explanation - * Explanation 1: - * - * After rotating the matrix by 90 degree: - * 1 goes to 2, 2 goes to 4 - * 4 goes to 3, 3 goes to 1 - * Explanation 2: - * - * 2D array remains the ssame as there is only element. - */ -public class RotateMatrix90Degrees { - public static void main(String[] args) { - List> array = new ArrayList<>(); - array.add(Arrays.asList(1, 3, 4, 2)); - array.add(Arrays.asList(2, 9, 6, -1)); - array.add(Arrays.asList(-3, 12, 8, 7)); - array.add(Arrays.asList(10, -2, 0, -9)); - - System.out.println(array); - solve(array); - System.out.println(array); - } - - public static void solve(List> array) { - // O(N *M) time | O(1) space - int rows = array.size(); - int cols = array.get(0).size(); - Integer temp = 0; - - // transpose - for (int i = 0; i < rows; i++) { - List currentRow = array.get(i); - for (int j = i + 1; j < cols; j++) { - temp = array.get(i).get(j); - array.get(i).set(j, array.get(j).get(i)); - array.get(j).set(i, temp); - } - } - - // reverse - for (int i = 0; i < array.size(); i++) { - List currentRow = array.get(i); - int start = 0; - int end = array.get(i).size() - 1; - int midIdx = (int) end / 2; - while (start <= midIdx) swap(start++, end--, currentRow); - } - } - public static void swap(int i, int j, List array) { - int temp = (int) array.get(i); - array.set(i, array.get(j)); - array.set(j, temp); - } -} diff --git a/Arrays/set_matrix_zeroes.java b/Arrays/set_matrix_zeroes.java deleted file mode 100644 index f456cac8..00000000 --- a/Arrays/set_matrix_zeroes.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * You are given a 2D integer matrix A, make all the elements in a row or column zero if the A[i][j] = 0. Specifically, make entire ith row and jth column zero. - * - * - * - * Problem Constraints - * 1 <= A.size() <= 103 - * - * 1 <= A[i].size() <= 103 - * - * 0 <= A[i][j] <= 103 - * - * - * - * Input Format - * First argument is a vector of vector of integers.(2D matrix). - * - * - * - * Output Format - * Return a vector of vector after doing required operations. - * - * - * - * Example Input - * Input 1: - * - * [1,2,3,4] - * [5,6,7,0] - * [9,2,0,4] - * - * - * Example Output - * Output 1: - * - * [1,2,0,0] - * [0,0,0,0] - * [0,0,0,0] - * - * - * Example Explanation - * Explanation 1: - * - * A[2][4] = A[3][3] = 0, so make 2nd row, 3rd row, 3rd column and 4th column zero. - */ - -import java.util.*; -public class SetMatrixZeroes { - public static void main(String[] args) { - List> matrix = new ArrayList<>(); - matrix.add(Arrays.asList(1, 2, 3, 4)); - matrix.add(Arrays.asList(5, 6, 7, 0)); - matrix.add(Arrays.asList(9, 2, 0, 4)); - - solve(matrix); - System.out.println(matrix); - } - public static void solve(List> matrix) { - // O(N*M) time | O(1) space - int col0 = 1, rows = matrix.size(), cols = matrix.get(0).size(); - - for (int i = 0; i < rows; i++) { - if (matrix.get(i).get(0) == 0) col0 = 0; - for (int j = 1; j < cols; j++) { - if (matrix.get(i).get(j) == 0) { - matrix.get(i).set(0, 0); - matrix.get(0).set(j, 0); - } - } - } - //traverse reverse order to avoid override of values - for (int i = rows - 1; i > -1; i--) { - for (int j = cols - 1; j > 0; j--) { - if (matrix.get(i).get(0) == 0 || matrix.get(0).get(j) == 0) { - matrix.get(i).set(j, 0); - } - } - if (col0 == 0) matrix.get(rows).set(0, 0); - } - - } -} diff --git a/Arrays/sign_of_the_product_of_an_array.cpp b/Arrays/sign_of_the_product_of_an_array.cpp new file mode 100644 index 00000000..7aca6f15 --- /dev/null +++ b/Arrays/sign_of_the_product_of_an_array.cpp @@ -0,0 +1,99 @@ +// Leetcode 1822 : Sign of the Product of an Array +/* +QUESTION +There is a function signFunc(x) that returns: + +1 if x is positive. +-1 if x is negative. +0 if x is equal to 0. +You are given an integer array nums. Let product be the product of all values in the array nums. +Return signFunc(product). +Example 1: + +Input: nums = [-1,-2,-3,-4,3,2,1] +Output: 1 +Explanation: The product of all values in the array is 144, and signFunc(144) = 1 +Example 2: + +Input: nums = [1,5,0,2,-3] +Output: 0 +Explanation: The product of all values in the array is 0, and signFunc(0) = 0 +Example 3: + +Input: nums = [-1,1,-1,1,-1] +Output: -1 +Explanation: The product of all values in the array is -1, and signFunc(-1) = -1 + +Constraints: +1 <= nums.length <= 1000 +-100 <= nums[i] <= 100 + +CODE EXPLANATION WITH DRY RUN + +Suppose we have the following input vector: +{-1, -2, -3, -4, 3, 2, 1} +We want to find the sign of the product of all elements in the vector. + +Here's how the code will execute: + +1-We define the input vector nums in the main function. +2-We call the arraySign function with nums as the argument. +3-The arraySign function initializes the sign variable to 1. +4-The function iterates over each element of the nums vector using a range-based for loop. +5-On the first iteration, num is -1. The element is negative, so the sign variable is multiplied by -1, changing its value to -1. +6-On the second iteration, num is -2. The element is negative, so the sign variable is multiplied by -1 again, changing its value to 1. +7-On the third iteration, num is -3. The element is negative, so the sign variable is multiplied by -1, changing its value to -1. +8-On the fourth iteration, num is -4. The element is negative, so the sign variable is multiplied by -1 again, changing its value to 1. +9-On the fifth iteration, num is 3. The element is positive, so the sign variable is not changed and remains 1. +10-On the sixth iteration, num is 2. The element is positive, so the sign variable is not changed and remains 1. +11-On the seventh iteration, num is 1. The element is positive, so the sign variable is not changed and remains 1. +12-The arraySign function returns sign, which has a value of 1. +13-In the main function, we check if sign is equal to 1. It is, so we print "Product is Positive" to the console. +Therefore, the output of the program will be: +"Product is Positive" +*/ +#include +#include +using namespace std; + +// Function to find the sign of the product of all elements in the input vector +int arraySign(vector &nums) +{ + int ans = 1; // Initialize answer to 1 + // Iterate over each element of the vector + for (int i = 0; i < nums.size(); i++) + { + // If element is negative, change answer to -1 + if (nums[i] < 0) + { + ans *= -1; + } + // If element is zero, answer is 0 and loop is exited + else if (nums[i] == 0) + { + ans = 0; + break; + } + } + return ans; // Return answer +} + +int main() +{ + // Example usage + vector nums = {-1, -2, -3, -4, 3, 2, 1}; + int ans = arraySign(nums); // Print the sign of the product of the vector + if (ans == 1) + { + cout << "Product is Positive"; + } + else if (ans == -1) + { + cout << "Product is Negative"; + } + else + { + cout << "Product is Zero"; + } + return 0; +} \ No newline at end of file diff --git a/Arrays/smallest_difference.cpp b/Arrays/smallest_difference.cpp new file mode 100644 index 00000000..ef87f76c --- /dev/null +++ b/Arrays/smallest_difference.cpp @@ -0,0 +1,79 @@ +/* + Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) + whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from + the first array in the first position. + + Note that the absolute difference of two integers is the distance between them on the real number line. + For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. + + You can assume that there will only be one pair of numbers with the smallest difference. + + Sample Input Array1 = [-1, 5, 10, 20, 28, 3] + Sample Input Array2 = [26, 134, 135, 15, 17] + + Sample Output = [28, 26] + + + The code finds the pair of elements from two arrays, array1 and array2, whose absolute difference is the smallest + among all possible pairs. It does this by first sorting both arrays and then using two pointers (idx1 and idx2) + to iterate through the arrays. At each iteration, it compares the current element of array1 and array2, + and updates smallest (the smallest absolute difference so far) and result (the pair of elements that give + the smallest absolute difference) if the absolute difference between the current elements is smaller than + the current smallest. The function returns the result at the end. + + Time complexity: O(nlog(n) + mlog(m)), where n and m are the lengths of array1 and array2, respectively + Space complexity: O(1) +*/ +#include +#include +#include +#include + +using namespace std; + +// SmallestDifference takes in two Vector of integers and returns a pair of integers, one from each Vector, such that their +// absolute difference is as close to zero as possible. If multiple pairs have the same absolute difference, SmallestDifference +// returns the pair whose elements come first in the respective slices. +vector SmallestDifference(vector array1, vector array2) { + // Initialize variables to track the smallest absolute difference seen so far and the current absolute difference + int current = INT_MAX, smallest = INT_MAX; + // Sort the input arrays in ascending order to enable efficient searching for the pair with the smallest absolute difference + sort(array1.begin(), array1.end()); + sort(array2.begin(), array2.end()); + // Initialize variables to track the current index in each array + int idx1 = 0, idx2 = 0; + // Initialize an empty vector to store the pair of integers with the smallest absolute difference + vector result; + // Loop through both arrays until the end of at least one of them is reached + while (idx1 < array1.size() && idx2 < array2.size()) { + // Get the current elements from both arrays + int first = array1[idx1], second = array2[idx2]; + // Compute the absolute difference between the current elements + if (first < second) { + current = second - first; + idx1++; + } else if (second < first) { + current = first - second; + idx2++; + } else { + // If the current elements are equal, we have found a pair with an absolute difference of 0 and can return it + return vector{first, second}; + } + // If the current absolute difference is smaller than the smallest seen so far, update the smallest difference and the result vector + if (smallest > current) { + smallest = current; + result = vector{first, second}; + } + } + // Return the pair of integers with the smallest absolute difference + return result; +} + + +int main() { + vector array1 = {1, 3, 15, 11, 2}; + vector array2 = {23, 127, 235, 19, 8}; + vector result = SmallestDifference(array1, array2); + cout << "Smallest difference pair: [" << result[0] << ", " << result[1] << "]" << endl; + return 0; +} diff --git a/Arrays/smallest_difference.go b/Arrays/smallest_difference.go new file mode 100644 index 00000000..714a829b --- /dev/null +++ b/Arrays/smallest_difference.go @@ -0,0 +1,79 @@ +/* + Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) + whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from + the first array in the first position. + + Note that the absolute difference of two integers is the distance between them on the real number line. + For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. + + You can assume that there will only be one pair of numbers with the smallest difference. + + Sample Input Array1 = [-1, 5, 10, 20, 28, 3] + Sample Input Array2 = [26, 134, 135, 15, 17] + + Sample Output = [28, 26] + + + This code implements the Smallest Difference problem which takes two arrays of integers as input and returns a pair of integers, + one from each array, with the smallest absolute difference between them. + + The function first initializes two variables current and smallest to the maximum integer value. It then sorts both input arrays + in ascending order using the sort.Ints function from the sort package. + + The function then iterates through both arrays using two pointers, idx1 and idx2, initialized to 0. Inside the loop, it compares + the elements at the current indices of the two arrays, first and second, and calculates the absolute difference between + them in the current variable. + + If current is smaller than the smallest variable, it updates smallest to current and assigns the current pair of integers + to the result variable. + + The function returns the result variable, which contains the pair of integers with the smallest absolute difference. + + If there are identical integers in the two input arrays, the function will return them immediately, without any further comparisons. + + O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array +*/ +package main + +import ( + "math" + "sort" +) + +// SmallestDifference takes two integer slices as input and returns a slice with two integers. +// The two integers in the returned slice have the smallest absolute difference among all pairs +// of integers from the two input slices. +func SmallestDifference(array1, array2 []int) []int { + // Initialize variables for the smallest difference and the current difference being calculated + current, smallest := math.MaxInt32, math.MaxInt32 + // Sort the input slices + sort.Ints(array1) + sort.Ints(array2) + // Initialize variables for the indices for the two slices + idx1, idx2 := 0, 0 + // Initialize an empty slice for the result + result := []int{} + // Loop through the two slices until we reach the end of one of the slices + for idx1 < len(array1) && idx2 < len(array2) { + // Get the values at the current indices for the two slices + first, second := array1[idx1], array2[idx2] + // Calculate the current difference between the two values + if first < second { + current = second - first + idx1++ + } else if second < first { + current = first - second + idx2++ + } else { + // If the two values are equal, we can return the pair + return []int{first, second} + } + // Update the smallest difference and result slice if the current difference is smaller + if smallest > current { + smallest = current + result = []int{first, second} + } + } + // Return the pair with the smallest absolute difference + return result +} diff --git a/Arrays/smallest_difference.java b/Arrays/smallest_difference.java index 4c2a21c5..84b926d7 100644 --- a/Arrays/smallest_difference.java +++ b/Arrays/smallest_difference.java @@ -1,55 +1,93 @@ -import java.util.Arrays; +/* + Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) + whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from + the first array in the first position. -/** - * + Note that the absolute difference of two integers is the distance between them on the real number line. + For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. - Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from the first array in the first position. + You can assume that there will only be one pair of numbers with the smallest difference. - Note that the absolute difference of two integers is the distance between them on the real number line. For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. + Sample Input Array1 = [-1, 5, 10, 20, 28, 3] + Sample Input Array2 = [26, 134, 135, 15, 17] - You can assume that there will only be one pair of numbers with the smallest difference. - Sample Input + Sample Output = [28, 26] - arrayOne = [-1, 5, 10, 20, 28, 3] - arrayTwo = [26, 134, 135, 15, 17] - Sample Output + This code implements the Smallest Difference problem which takes two arrays of integers as input and returns a pair of integers, + one from each array, with the smallest absolute difference between them. - [28, 26] + The function first initializes two variables current and smallest to the maximum integer value. It then sorts both input arrays + in ascending order using the sort.Ints function from the sort package. + The function then iterates through both arrays using two pointers, idx1 and idx2, initialized to 0. Inside the loop, it compares + the elements at the current indices of the two arrays, first and second, and calculates the absolute difference between + them in the current variable. - */ -public class SmallestDifference { - public static void main(String[] args) { - int[] arrayOne = {-1, 5, 10, 20, 28, 3}; - int[] arrayTwo = {26, 134, 135, 15, 17}; - int[] result = solve(arrayOne, arrayTwo); - System.out.println(Arrays.toString(result)); - } - public static int[] solve(int[] arrayOne, int[] arrayTwo) { - // O(n(log(n) + m(log(m)) time | O(1) space - Arrays.sort(arrayOne); - Arrays.sort(arrayTwo); - int idxOne = 0; - int idxTwo = 0; + If current is smaller than the smallest variable, it updates smallest to current and assigns the current pair of integers + to the result variable. + + The function returns the result variable, which contains the pair of integers with the smallest absolute difference. + + If there are identical integers in the two input arrays, the function will return them immediately, without any further comparisons. + + O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array +*/ +import java.util.Arrays; + +public class Main { + + public static int[] smallestDifference(int[] array1, int[] array2) { + // Initialize variables for the smallest difference and the current difference being calculated + int current = Integer.MAX_VALUE; int smallest = Integer.MAX_VALUE; - int current; - int[] smallestPair = new int[2]; - while (idxOne < arrayOne.length && idxTwo < arrayTwo.length) { - int firstNum = arrayOne[idxOne]; - int secondNum = arrayTwo[idxTwo]; - if (firstNum < secondNum) { - current = secondNum - firstNum; - idxOne++; - } else if (secondNum < firstNum) { - current = firstNum - secondNum; - idxTwo++; - } else return new int[]{firstNum, secondNum}; - if (current < smallest) { + + // Sort the input arrays + Arrays.sort(array1); + Arrays.sort(array2); + + // Initialize variables for the indices for the two arrays + int idx1 = 0; + int idx2 = 0; + + // Initialize an empty array for the result + int[] result = new int[2]; + + // Loop through the two arrays until we reach the end of one of the arrays + while (idx1 < array1.length && idx2 < array2.length) { + // Get the values at the current indices for the two arrays + int first = array1[idx1]; + int second = array2[idx2]; + + // Calculate the current difference between the two values + if (first < second) { + current = second - first; + idx1++; + } else if (second < first) { + current = first - second; + idx2++; + } else { + // If the two values are equal, we can return the pair + return new int[]{first, second}; + } + + // Update the smallest difference and result array if the current difference is smaller + if (smallest > current) { smallest = current; - smallestPair = new int[]{firstNum, secondNum}; + result[0] = first; + result[1] = second; } } - return smallestPair; + + // Return the pair with the smallest absolute difference + return result; + } + + public static void main(String[] args) { + int[] array1 = {1, 3, 5, 23, 11, 2}; + int[] array2 = {8, 19, 3, 15, 9}; + + int[] result = smallestDifference(array1, array2); + System.out.println(Arrays.toString(result)); // Output: [3, 3] } } diff --git a/Arrays/smallest_difference.js b/Arrays/smallest_difference.js new file mode 100644 index 00000000..8d5829b2 --- /dev/null +++ b/Arrays/smallest_difference.js @@ -0,0 +1,62 @@ +/* + Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) + whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from + the first array in the first position. + + Note that the absolute difference of two integers is the distance between them on the real number line. + For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. + + You can assume that there will only be one pair of numbers with the smallest difference. + + Sample Input Array1 = [-1, 5, 10, 20, 28, 3] + Sample Input Array2 = [26, 134, 135, 15, 17] + + Sample Output = [28, 26] + The smallestDifference function takes in two arrays of integers and finds the pair of values (one from each array) + that have the smallest difference. It does this by sorting the two arrays in ascending order, and then iterating + through both arrays using two index variables. At each step, the function calculates the difference between the + values pointed to by the index variables, updates the current difference, and moves the index of the array with + the smaller value to the right. If the values are equal, the function returns the pair as an array. + The function keeps track of the smallest difference seen so far, and updates the result array accordingly. + Finally, the function returns the result array containing the pair with the smallest difference. + + O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array +*/ +/** + * Given two arrays of integers, finds a pair of values (one value from each array) + * that have the smallest difference. Returns the pair as an array. + * @param {number[]} array1 - The first array of integers + * @param {number[]} array2 - The second array of integers + * @returns {number[]} - The pair of values that have the smallest difference + */ +function smallestDifference(array1, array2) { + let currentDiff = Infinity; // initialize current difference to a large number + let smallestDiff = Infinity; // initialize smallest difference to a large number + let result = []; // initialize result array + array1.sort((a, b) => a - b); // sort the first array in ascending order + array2.sort((a, b) => a - b); // sort the second array in ascending order + let idx1 = 0; // initialize index for first array + let idx2 = 0; // initialize index for second array + while (idx1 < array1.length && idx2 < array2.length) { + const first = array1[idx1]; + const second = array2[idx2]; + if (first < second) { + // if the value from first array is smaller + currentDiff = second - first; // update the current difference + idx1++; // move the index of first array to the right + } else if (second < first) { + // if the value from second array is smaller + currentDiff = first - second; // update the current difference + idx2++; // move the index of second array to the right + } else { + // if the values are equal, the smallest difference is 0 + return [first, second]; + } + if (smallestDiff > currentDiff) { + // if the current difference is smaller than the smallest difference so far + smallestDiff = currentDiff; // update the smallest difference + result = [first, second]; // update the result array + } + } + return result; +} diff --git a/Arrays/smallest_difference.py b/Arrays/smallest_difference.py new file mode 100644 index 00000000..a468367f --- /dev/null +++ b/Arrays/smallest_difference.py @@ -0,0 +1,84 @@ +''' + Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) + whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from + the first array in the first position. + + Note that the absolute difference of two integers is the distance between them on the real number line. + For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. + + You can assume that there will only be one pair of numbers with the smallest difference. + + Sample Input Array1 = [-1, 5, 10, 20, 28, 3] + Sample Input Array2 = [26, 134, 135, 15, 17] + + Sample Output = [28, 26] + + The smallest_difference function takes two arrays of integers as input and returns a list of two integers, where the first integer + is from the first array and the second is from the second array, and the absolute difference between them is the smallest among + all such pairs. It achieves this by sorting the two arrays and then iterating through them in parallel, keeping track of the + current smallest difference and the pair of integers that produced it. The function returns the pair with the smallest difference. + + O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array +''' +from typing import List +import math + +def smallest_difference(array1: List[int], array2: List[int]) -> List[int]: + """ + Finds the pair of integers, one from each of the two given arrays, with the smallest difference between them. + + Args: + array1: A list of integers. + array2: A list of integers. + + Returns: + A list of two integers, where the first integer is from array1 and the second is from array2, and the absolute + difference between them is the smallest among all such pairs. + + Example: + >>> smallest_difference([1, 3, 15, 11, 2], [23, 127, 235, 19, 8]) + [11, 8] + """ + current = math.inf + smallest = math.inf + result = [] + + # Sort both input arrays in ascending order + array1.sort() + array2.sort() + + # Initialize two pointers, one for each array + idx1, idx2 = 0, 0 + + # Loop through both arrays while the pointers are still within bounds + while idx1 < len(array1) and idx2 < len(array2): + first, second = array1[idx1], array2[idx2] + + # Calculate the absolute difference between the two current numbers + current = abs(first - second) + + # Update the smallest difference and the result if the current difference is smaller + if current < smallest: + smallest = current + result = [first, second] + + # If the first number is smaller than the second number, increment the pointer + # for the first array, otherwise increment the pointer for the second array. + if first < second: + idx1 += 1 + else: + idx2 += 1 + + # Return the result + return result + +def main(): + array1 = [1, 3, 15, 11, 2] + array2 = [23, 127, 235, 19, 8] + result = smallest_difference(array1, array2) + print("Array 1:", array1) + print("Array 2:", array2) + print("Smallest Difference Pair:", result) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Arrays/sorted_square_array,js b/Arrays/sorted_square_array,js new file mode 100644 index 00000000..fbcd5671 --- /dev/null +++ b/Arrays/sorted_square_array,js @@ -0,0 +1,70 @@ +/* + Write a function that takes in a non-empty array of integers that are sorted + in ascending order and returns a new array of the same length with the squares + of the original integers also sorted in ascending order. + + Sample Input: [-6, 1, 2, 3, 4] + Output: [1, 4, 6, 16, 36] + + Explanation: + + The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. + + Here's a step-by-step explanation of the code: + + 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. + 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the + index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. + 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and + end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. + 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` + index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. + This ensures that the greater squared value is placed at the end of the result array. + 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position + by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the + end of the result array. + 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. + 7. Repeat the process until the start and end positions cross each other. + 8. Finally, return the result array, which contains the squared values of the input array elements + in non-decreasing order. + + The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. + + The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. + + The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. + + Overall, the function has a linear time complexity and linear space complexity. +*/ +function sortedSquaredArray(array) { + // Initialize an empty result array of the same length as the original array + const result = Array(array.length).fill(0); + + // Set the start and end positions and the end_pos + let end_pos = array.length - 1; + let start = 0; + let sq1 = 0; + let sq2 = 0; + let end = array.length - 1; + + // Using the two-pointer approach, calculate the square of the absolute value and add the greatest value to the end of the result array + while (start <= end) { + sq1 = array[start] * array[start]; + sq2 = array[end] * array[end]; + + if (sq1 > sq2) { + result[end_pos] = sq1; + start++; // Square of the start pointer is greater, so increment start by 1 + } else { + result[end_pos] = sq2; + end--; // Square of the end pointer is greater, so decrement end by 1 + } + end_pos--; + } + + return result; +} + +const arr = [-6, 1, 2, 3, 4, 5]; +const result = sortedSquaredArray(arr); +console.log(result); diff --git a/Arrays/sorted_square_array.cpp b/Arrays/sorted_square_array.cpp new file mode 100644 index 00000000..d7405bc6 --- /dev/null +++ b/Arrays/sorted_square_array.cpp @@ -0,0 +1,81 @@ +/* + Write a function that takes in a non-empty array of integers that are sorted + in ascending order and returns a new array of the same length with the squares + of the original integers also sorted in ascending order. + + Sample Input: [-6, 1, 2, 3, 4] + Output: [1, 4, 6, 16, 36] + + Explanation: + + The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. + + Here's a step-by-step explanation of the code: + + 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. + 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the + index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. + 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and + end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. + 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` + index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. + This ensures that the greater squared value is placed at the end of the result array. + 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position + by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the + end of the result array. + 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. + 7. Repeat the process until the start and end positions cross each other. + 8. Finally, return the result array, which contains the squared values of the input array elements + in non-decreasing order. + + The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. + + The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. + + The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. + + Overall, the function has a linear time complexity and linear space complexity. +*/ +#include +#include + +std::vector sortedSquaredArray(std::vector& array) { + // Initialize an empty result vector of the same length as the original array + std::vector result(array.size()); + + // Set the start and end positions and the end_pos + int end_pos = array.size() - 1; + int start = 0; + int sq1 = 0; + int sq2 = 0; + int end = array.size() - 1; + + // Using the two-pointer approach, calculate the square of the absolute value and add the greatest value to the end of the result vector + while (start <= end) { + sq1 = array[start] * array[start]; + sq2 = array[end] * array[end]; + + if (sq1 > sq2) { + result[end_pos] = sq1; + start++; // Square of the start pointer is greater, so increment start by 1 + } else { + result[end_pos] = sq2; + end--; // Square of the end pointer is greater, so decrement end by 1 + } + end_pos--; + } + + return result; +} + +int main() { + std::vector arr = {-6, 1, 2, 3, 4, 5}; + std::vector result = sortedSquaredArray(arr); + + for (int num : result) { + std::cout << num << " "; + } + std::cout << std::endl; + + return 0; +} diff --git a/Arrays/sorted_square_array.go b/Arrays/sorted_square_array.go index cf31a0e5..9de8bb25 100644 --- a/Arrays/sorted_square_array.go +++ b/Arrays/sorted_square_array.go @@ -1,10 +1,40 @@ /* - Write a function that takes in a non-empty array of integers that are sorted - in ascending order and returns a new array of the same length with the squares - of the original integers also sorted in ascending order. + Write a function that takes in a non-empty array of integers that are sorted + in ascending order and returns a new array of the same length with the squares + of the original integers also sorted in ascending order. - Sample Input: [-6, 1, 2, 3, 4] - Output: [1, 4, 6, 16, 36] + Sample Input: [-6, 1, 2, 3, 4] + Output: [1, 4, 6, 16, 36] + + Explanation: + + The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. + + Here's a step-by-step explanation of the code: + + 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. + 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the + index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. + 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and + end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. + 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` + index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. + This ensures that the greater squared value is placed at the end of the result array. + 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position + by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the + end of the result array. + 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. + 7. Repeat the process until the start and end positions cross each other. + 8. Finally, return the result array, which contains the squared values of the input array elements + in non-decreasing order. + + The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. + + The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. + + The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. + + Overall, the function has a linear time complexity and linear space complexity. */ package main diff --git a/Arrays/sorted_square_array.java b/Arrays/sorted_square_array.java new file mode 100644 index 00000000..a2c5526f --- /dev/null +++ b/Arrays/sorted_square_array.java @@ -0,0 +1,81 @@ +/* + Write a function that takes in a non-empty array of integers that are sorted + in ascending order and returns a new array of the same length with the squares + of the original integers also sorted in ascending order. + + Sample Input: [-6, 1, 2, 3, 4] + Output: [1, 4, 6, 16, 36] + + Explanation: + + The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. + + Here's a step-by-step explanation of the code: + + 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. + 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the + index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. + 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and + end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. + 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` + index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. + This ensures that the greater squared value is placed at the end of the result array. + 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position + by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the + end of the result array. + 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. + 7. Repeat the process until the start and end positions cross each other. + 8. Finally, return the result array, which contains the squared values of the input array elements + in non-decreasing order. + + The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. + + The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. + + The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. + + Overall, the function has a linear time complexity and linear space complexity. +*/ +import java.util.Arrays; + +public class Main { + + public static int[] sortedSquaredArray(int[] array) { + // Initialize an empty result array of the same length as the original array + int[] result = new int[array.length]; + + // Set the start and end positions and the end_pos + int end_pos = array.length - 1; + int start = 0; + int sq1 = 0; + int sq2 = 0; + int end = array.length - 1; + + // Using the two-pointer approach, calculate the square of the absolute value and add the greatest value to the end of the result array + while (start <= end) { + sq1 = array[start] * array[start]; + sq2 = array[end] * array[end]; + + if (sq1 > sq2) { + result[end_pos] = sq1; + start++; // Square of the start pointer is greater, so increment start by 1 + } else { + result[end_pos] = sq2; + end--; // Square of the end pointer is greater, so decrement end by 1 + } + end_pos--; + } + + return result; + } + + public static void main(String[] args) { + int[] arr = {-6, 1, 2, 3, 4, 5}; + int[] result = sortedSquaredArray(arr); + + for (int num : result) { + System.out.print(num + " "); + } + System.out.println(); + } +} diff --git a/Arrays/sorted_square_array.py b/Arrays/sorted_square_array.py new file mode 100644 index 00000000..c2024b4c --- /dev/null +++ b/Arrays/sorted_square_array.py @@ -0,0 +1,67 @@ +''' + Write a function that takes in a non-empty array of integers that are sorted + in ascending order and returns a new array of the same length with the squares + of the original integers also sorted in ascending order. + + Sample Input: [-6, 1, 2, 3, 4] + Output: [1, 4, 6, 16, 36] + + Explanation: + + The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. + + Here's a step-by-step explanation of the code: + + 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. + 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the + index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. + 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and + end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. + 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` + index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. + This ensures that the greater squared value is placed at the end of the result array. + 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position + by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the + end of the result array. + 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. + 7. Repeat the process until the start and end positions cross each other. + 8. Finally, return the result array, which contains the squared values of the input array elements + in non-decreasing order. + + The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. + + The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. + + The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. + + Overall, the function has a linear time complexity and linear space complexity. +''' +def sortedSquaredArray(array): + # Initialize an empty result array of the same length as the original array + result = [0] * len(array) + + # Set the start and end positions and the end_pos + end_pos = len(array) - 1 + start = 0 + sq1 = 0 + sq2 = 0 + end = len(array) - 1 + + # Using the two-pointer approach, calculate the square of the absolute value and add the greatest value to the end of the result array + while start <= end: + sq1 = array[start] * array[start] + sq2 = array[end] * array[end] + + if sq1 > sq2: + result[end_pos] = sq1 + start += 1 # Square of the start pointer is greater, so increment start by 1 + else: + result[end_pos] = sq2 + end -= 1 # Square of the end pointer is greater, so decrement end by 1 + end_pos -= 1 + + return result + +arr = [-6, 1, 2, 3, 4, 5] +result = sortedSquaredArray(arr) +print(result) diff --git a/Arrays/special_index.java b/Arrays/special_index.java deleted file mode 100644 index e53507c6..00000000 --- a/Arrays/special_index.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Given an array, arr[] of size N, the task is to find the count of array indices such that removing an element from these indices makes the sum of even-indexed and odd-indexed array elements equal. - * - * - * - * Problem Constraints - * 1 <= n <= 105 - * -105 <= A[i] <= 105 - * - * - * Input Format - * First argument contains an array A of integers of size N - * - * - * Output Format - * Return the count of array indices such that removing an element from these indices makes the sum of even-indexed and odd-indexed array elements equal. - * - * - * - * Example Input - * Input 1: - * A=[2, 1, 6, 4] - * Input 2: - * - * A=[1, 1, 1] - * - * - * Example Output - * Output 1: - * 1 - * Output 2: - * - * 3 - * - * - * Example Explanation - * Explanation 1: - * Removing arr[1] from the array modifies arr[] to { 2, 6, 4 } such that, arr[0] + arr[2] = arr[1]. - * Therefore, the required output is 1. - * Explanation 2: - * - * Removing arr[0] from the given array modifies arr[] to { 1, 1 } such that arr[0] = arr[1] - * Removing arr[1] from the given array modifies arr[] to { 1, 1 } such that arr[0] = arr[1] - * Removing arr[2] from the given array modifies arr[] to { 1, 1 } such that arr[0] = arr[1] - * Therefore, the required output is 3. - */ - -package InterviewProblems; - -public class SpecialIndex { - public static void main(String[] args) { - int a[] = {1, 1, 1}; - int ans = solve(a); - System.out.println(ans); - } - public static int solve(int[] arr) { - // O(N) time | O(1) space - - // Without using extra space - int len = arr.length; - if (len == 1) return 1; - if (len == 2) return 0; - - int totalEvenSum = 0; - int totalOddSum = 0; - for (int i = 0; i < len; i++) { - if (i % 2 == 0) totalEvenSum += arr[i]; - else totalOddSum += arr[i]; - } - - int prevEvenSum = 0; - int prevOddSum = 0; - int currentEvenSum = 0; - int currentOldSum = 0; - int temp1; - int temp2; - int ans = 0; - - for (int i = 0; i < len; i++) { - - if (i % 2 == 0) currentEvenSum = prevEvenSum + arr[i]; - else currentOldSum = prevOddSum + arr[i]; - - temp1 = prevEvenSum + (totalOddSum - currentOldSum); - temp2 = prevOddSum + (totalEvenSum - currentEvenSum); - if (temp1 == temp2) ans++; - - prevEvenSum = currentEvenSum; - prevOddSum = currentOldSum; - - } - return ans; - } - // Using Prefix Sum -// // even prefix sum | O(N) time -// int[] evenPrefixSum = new int[len]; -// evenPrefixSum[0] = arr[0]; -// -// for (int i = 1; i < len; i++) { -// int currentNum = arr[i]; -// evenPrefixSum[i] = i % 2 != 0 ? evenPrefixSum[i-1] : evenPrefixSum[i-1] + currentNum; -// } -// -// // odd prefix sum | O(N) time -// int[] oddPrefixSum = new int[len]; -// oddPrefixSum[0] = 0; -// -// for (int i = 1; i < len; i++) { -// int currentNum = arr[i]; -// oddPrefixSum[i] = i % 2 == 0 ? oddPrefixSum[i-1] : oddPrefixSum[i-1] + currentNum; -// } -// -// // find special index/s -// if (oddPrefixSum[len-1] == evenPrefixSum[len-1] - evenPrefixSum[0]) ans++; // 0th Index -// for (int i = 1; i < len; i++) { -// int currentEvenSum = evenPrefixSum[i-1] + (oddPrefixSum[len-1] - oddPrefixSum[i]); -// int currentOddSum = oddPrefixSum[i-1] + (evenPrefixSum[len-1] - evenPrefixSum[i]); -// -// if (currentEvenSum == currentOddSum) ans++; -// } - -// return ans; -// } -} diff --git a/Arrays/spiral_order_2.java b/Arrays/spiral_order_2.java deleted file mode 100644 index f7607527..00000000 --- a/Arrays/spiral_order_2.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Given an integer A, generate a square matrix filled with elements from 1 to A2 in spiral order and return the generated square matrix. - * - * - * - * Problem Constraints - * 1 <= A <= 1000 - * - * - * - * Input Format - * First and only argument is integer A - * - * - * Output Format - * Return a 2-D matrix which consists of the elements added in spiral order. - * - * - * - * Example Input - * Input 1: - * - * 1 - * Input 2: - * - * 2 - * Input 3: - * - * 5 - * - * - * Example Output - * Output 1: - * - * [ [1] ] - * Output 2: - * - * [ [1, 2], [4, 3] ] - * Output 2: - * - * [ [1, 2, 3, 4, 5], [16, 17, 18, 19, 6], [15, 24, 25, 20, 7], [14, 23, 22, 21, 8], [13, 12, 11, 10, 9] ] - * - * - * Example Explanation - * Explanation 1: - * - * Only 1 is to be arranged. - * Explanation 2: - * - * 1 --> 2 - * | - * | - * 4<--- 3 - * Explanation 3: - */ - -package Matrices; - -import java.util.Arrays; - -public class SpiralOrder2 { - public static void main(String[] args) { - final long startTime = System.currentTimeMillis(); - final long beforeUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - - int size = 5; - int[][] matrix = solve(size); - System.out.println(Arrays.deepToString(matrix)); - - final long endTime = System.currentTimeMillis(); - final long afterUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - final long actualMemUsed = afterUsedMem-beforeUsedMem; - System.out.println("Runtime " + (endTime - startTime) + " ms"); - System.out.println("Space " + actualMemUsed + " B"); - } - - public static int[][] solve(int size) { - // O(N^2) | time O(N^2) space where N is size. - int[][] matrix = new int[size][size]; - int startRow = 0, - startCol = 0, - endRow = size - 1, - endCol = size - 1; - int currentNum =1; - - while (startRow <= endRow && startCol <= endCol) { - - for (int col = startCol; col <= endCol; col++) - matrix[startRow][col] = currentNum++; - - for (int row = startRow + 1; row <= endRow; row++) - matrix[row][endCol] = currentNum++; - - for (int col = endCol - 1; col >= startCol; col--) - matrix[endRow][col] = currentNum++; - - for (int row = endRow - 1; row > startRow; row--) - matrix[row][startCol] = currentNum++; - - startRow++; - startCol++; - endRow--; - endCol--; - } - return matrix; - } -} diff --git a/Arrays/subarray_sums.java b/Arrays/subarray_sums.java deleted file mode 100644 index 61a2c1c9..00000000 --- a/Arrays/subarray_sums.java +++ /dev/null @@ -1,92 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; - -/** - * ou are given an integer array A of length N. - * You have to find the sum of all subarray sums of A. - * More formally, a subarray is defined as a contiguous part of an array which we can obtain by deleting zero or more elements from either end of the array. - * A subarray sum denotes the sum of all the elements of that subarray. - * - * - * - * Problem Constraints - * 1 <= N <= 105 - * 1 <= Ai <= 10 4 - * - * - * Input Format - * The first argument is the integer array A. - * - * - * Output Format - * Return a single integer denoting the sum of all subarray sums of the given array. - * - * - * Example Input - * Input 1: - * A = [1, 2, 3] - * Input 2: - * - * A = [2, 1, 3] - * - * - * Example Output - * Output 1: - * 20 - * Output 2: - * - * 19 - * - * - * Example Explanation - * Explanation 1: - * The different subarrays for the given array are: [1], [2], [3], [1, 2], [2, 3], [1, 2, 3]. - * Their sums are: 1 + 2 + 3 + 3 + 5 + 6 = 20 - * Explanation 2: - * - * Similiar to the first example, the sum of all subarray sums for this array is 19. - */ -public class SubarraysSum { - public static void main(String[] args) { - ArrayList array = new ArrayList<>( - Arrays.asList(2, 9, 5 ) - ); - System.out.println(solve(array)); - } - public static long solve(ArrayList array) { - long totalSubarraysSum = 0; - - // Build prefix array : O(n) time | O(n) space -// ArrayList prefixSum = new ArrayList<>(); -// prefixSum.add(array.get(0)); -// for (int i = 1; i < array.size(); i++) { -// int currentNum = array.get(i); -// int currentPrefixSum = prefixSum.get(i - 1) + currentNum; -// prefixSum.add(currentPrefixSum); -// } - // Sum all subArrays -// for (int i = 0; i < array.size(); i++) { -// for (int j = i; j < array.size(); j++) { -// if (i == 0) totalSubarraysSum += prefixSum.get(j); -// else totalSubarraysSum += prefixSum.get(j) - prefixSum.get(i - 1); -// } -// } - // Without prefix array O(n) time | O(1) space -// long totalSubarraySum = 0; -// for (int i = 0; i < array.size(); i++) { -// int currentSum = 0; -// for (int j = i; j < array.size(); j++) { -// currentSum += array.get(j); -// totalSubarraysSum += currentSum; -// } -// } - - // No.of subarrays = (i + 1) * (N - i); i is starting index. - // Optimal O(n) time | O(1) space solution - for (int i = 0; i < array.size(); i++) { - long currentSubarraySum = (long) (i + 1) * (array.size() - i) * array.get(i); - totalSubarraysSum += currentSubarraySum; - } - return totalSubarraysSum; - } -} diff --git a/Arrays/subarray_with_given_sum_and_length.java b/Arrays/subarray_with_given_sum_and_length.java deleted file mode 100644 index 9f1521e2..00000000 --- a/Arrays/subarray_with_given_sum_and_length.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Given an array A of length N. Also given are integers B and C. - * - * Return 1 if there exists a subarray with length B having sum C and 0 otherwise - * - * - * - * Problem Constraints - * 1 <= N <= 105 - * - * 1 <= A[i] <= 104 - * - * 1 <= B <= N - * - * 1 <= C <= 109 - * - * - * - * Input Format - * First argument A is an array of integers. - * - * The remaining arguments B and C are integers - * - * - * - * Output Format - * Return 1 if such a subarray exist and 0 otherwise - * - * - * Example Input - * Input 1: - * A = [4, 3, 2, 6] - * B = 2 - * C = 5 - * Input 2: - * - * A = [4, 2, 2] - * B = 2 - * C = 8 - * - * - * Example Output - * Output 1: - * 1 - * Output 2: - * - * 0 - * - * - * Example Explanation - * Explanation 1: - * The subarray [3, 2] is of length 2 and sum 5. - * Explanation 2: - * There are no such subarray. - */ - -package SlidingWindow; - -public class SubarrayWithGivenSumAndLength { - public static void main(String[] args) { - final long startTime = System.currentTimeMillis(); - final long beforeUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - - int[] array = {4, 3, 2, 6}; - int b = 2; - int c = 5; - int ans = solve(array, b, c); - System.out.println(ans); - - final long endTime = System.currentTimeMillis(); - final long afterUsedMem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); - final long actualMemUsed = afterUsedMem-beforeUsedMem; - System.out.println("Runtime " + (endTime - startTime) + " ms"); - System.out.println("Space " + actualMemUsed + " B"); - } - - public static int solve(int[] array, int b, int c) { - //O(n) time | O(1) space - int sum = 0; - int len = array.length; - for (int i = 0; i < b; i++) // find prefix sum of length b - sum += array[i]; - - // use sliding window approach to find sum of length c with length b. - int startIdx = 1; // 1 because already calculated the subarray sum of length b in above for loop. - int endIdx = b; - - while (endIdx < len) { - - sum = sum + array[endIdx] - array[startIdx - 1]; // add array[endIdx] num and remove array[startIdx - 1] - if (sum == c) // found subarray of sum c, so return 1. - return 1; - - startIdx++; - endIdx++; - } - - - return 0; - } -} \ No newline at end of file diff --git a/Arrays/subarray_with_least_avg.java b/Arrays/subarray_with_least_avg.java deleted file mode 100644 index 48ae6199..00000000 --- a/Arrays/subarray_with_least_avg.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Given an array of size N, find the subarray of size K with the least average. - * - * - * - * Problem Constraints - * 1<=k<=N<=1e5 - * -1e5<=A[i]<=1e5 - * - * - * Input Format - * First argument contains an array A of integers of size N. - * Second argument contains integer k. - * - * - * Output Format - * Return the index of the first element of the subarray of size k that has least average. - * Array indexing starts from 0. - * - * - * Example Input - * Input 1: - * A=[3, 7, 90, 20, 10, 50, 40] - * B=3 - * Input 2: - * - * A=[3, 7, 5, 20, -10, 0, 12] - * B=2 - * - * - * Example Output - * Output 1: - * 3 - * Output 2: - * - * 4 - * - * - * Example Explanation - * Explanation 1: - * Subarray between indexes 3 and 5 - * The subarray {20, 10, 50} has the least average - * among all subarrays of size 3. - * Explanation 2: - * - * Subarray between [4, 5] has minimum average - */ - -package SlidingWindow; - -public class SubArrayWithLeastAverage { - public static void main(String[] args) { -// int[] arr = {3, 7, 90, 20, -10, 0, 12}; - int[] arr = {431, 313, 53, 251, 105, 423, 326, 297, - 218, 89, 394, 365, 348, 474, 157, 262, 33, 187, - 67, 79, 495, 199, 175, 228, 27, 305, 496, 331, - 40, 98, 405, 221, 327, 488, 252, 73, 218, 152, - 313, 274, 195, 353, 225, 292, 426, 257, 418, - 364, 344, 349, 181}; - int size = 12; - int ans = solve(arr, size); - System.out.println(ans); - } - public static int solve(int[] arr, int size) { - // O(N) time | O(1) space - int currentSum = 0; - int idx = 0; - int len = arr.length; - - // sliding window - for (int i = 0; i < size; i++) - currentSum += arr[i]; - - int startIdx = 1; - int endIdx = size; - int minSum = currentSum; - // slide remaining windows - while (endIdx < len) { - currentSum = currentSum + arr[endIdx] - arr[startIdx - 1]; - if (currentSum < minSum) { - minSum = currentSum; - idx = startIdx; - } - startIdx++; - endIdx++; - } - return idx; - } -} diff --git a/Arrays/subarray_with_zero_sum.go b/Arrays/subarray_with_zero_sum.go deleted file mode 100644 index aa3da454..00000000 --- a/Arrays/subarray_with_zero_sum.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import "fmt" - -func IsSubArrayWithZeroSum(Arr []int) bool { - prefixSum := make(map[int]bool) - sum := 0 - for i := 0; i < len(Arr); i++ { - sum += Arr[i] - if sum == 0 || prefixSum[sum] == true { - return true - } - prefixSum[sum] = true - } - return false -} - -func main() { - Arr := []int{1, 2, 3, 4, -7, 1} - fmt.Println(IsSubArrayWithZeroSum(Arr)) - Arr = []int{1, 2, 3, 4, 5, 6, 7} - fmt.Println(IsSubArrayWithZeroSum(Arr)) -} \ No newline at end of file diff --git a/Arrays/three_largest_no.py b/Arrays/three_largest_no.py new file mode 100644 index 00000000..46b879d2 --- /dev/null +++ b/Arrays/three_largest_no.py @@ -0,0 +1,43 @@ + + +""" +Write a function that takes in an array of at least three integers and, +without sorting the input array, returns a sorted array of the three largest +integers in the input array. + +# Approach + +- We will use three variables to store the three max values and initialize them with int_minimum +- We will iterate through the array and compare the values with the three variables +- If the value is greater than the first variable, we will update the variables +- If the value is greater than the second variable, we will update the variables +- If the value is greater than the third variable, we will update the variables +- Return the three variables as a sorted array + +""" + + + +def three_max_no(arr:list)-> list: + # give int minvalue to a,b, + max_1,max_2,max_3= -9999,-9999,-9999 + # we will iterate through the array and compare the values + for i in arr: + if i > max_1: + # if the value is greater than the first variable, we will update the variables + max_1,max_2,max_3 = i, max_1, max_2 + elif i > max_2: + # if the value is greater than the second variable, we will update the variables + max_2, max_3 = i, max_2 + elif i > max_3: + # if the value is greater than the third variable, we will update the variables + max_3 = i + # return the three max values as a sorted array + return [max_1, max_2, max_3] + + +# example-1 +arr=[141,1,17,-7,-17,-27,18,541,8,7,7] +print(three_max_no(arr)) + +# sample output [541, 141, 18] diff --git a/Arrays/total_hamming_distance.cpp b/Arrays/total_hamming_distance.cpp new file mode 100644 index 00000000..04f5857c --- /dev/null +++ b/Arrays/total_hamming_distance.cpp @@ -0,0 +1,108 @@ +/* + +Introduction: + + This documentation provides a detailed explanation of the problem statement, algorithm, and implementation of + calculating the sum of Hamming distances between all pairs of integers in an integer array. + Hamming distance measures the number of positions at which corresponding bits are different between two numbers. + The algorithm aims to find the sum of Hamming distances for all possible pairs of integers in the given array. + +Problem Statement: + + Given an integer array nums, the task is to calculate the sum of Hamming distances between all the pairs of integers in nums. + The goal is to determine the total count of differing bits at each position for all possible pairs of integers in the array. + +Hamming Distance : + + Hamming distance is a metric for comparing two binary data strings. + While comparing two binary strings of equal length, Hamming distance is the number of bit positions in which the two bits are different. + +Sample Input : [4,14,2] +expected Output : 6 + +Explaination : In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 (just showing the four bits relevant in this case). +Total Hamming distance = HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6. + +Method 1 : + + (Brute Force Method) We can iterate over the whole array and find all pairs calculate Hamming distance and add all the distances. + Time complexity : O(n^2) + Space Complexity : O(1) + +Method 2 : + + 1. Initialize a variable `totalDistance` to store the sum of Hamming distances. + 2. Iterate over each bit position from 0 to 31 (assuming integers are 32-bit). + 3. For each bit position `i`, initialize two variables: `countZeros` and `countOnes` to keep track of the count of zeros and ones at that position. + 4. Iterate over each element `num` in `nums`. + a. Right shift `num` by `i` positions and perform a bitwise AND with 1 to check the value at bit position `i`. + b. If the result is 0, increment `countZeros` by 1; otherwise, increment `countOnes` by 1. + 5. Add `countZeros * countOnes` to `totalDistance`. This calculates the Hamming distance for the current bit position and adds it to the running total. + 6. Repeat steps 3-5 for all bit positions. + 7. Return `totalDistance` as the sum of Hamming distances between all pairs of integers in `nums`. + + Time Complexity : O(k*n) where , + k = maximum number of bits required to represent a number in the array + n = size of array + + Space Complexity : O(1) + + +Explaination : + + Number ==> Binary Representation + 4 0 1 0 0 + 14 1 1 1 0 + 2 0 0 1 0 + + The idea is to count differences at individual bit positions. We traverse from 0 to 31 and count numbers with i’th bit set. + Let this count be ‘c'. There would be “n-c” numbers with i’th bit not set. + So count of differences at i’th bit would be “count * (n-count)”, the reason for this formula is as every pair having one element + which has set bit at i’th position and second element having unset bit at i’th position contributes exactly 1 to sum. + + 1st bit = 2*1 = 2 + 2nd bit = 2*1 = 2 + 3rd bit = 2*1 = 2 + 4th bit = 3*0 = 0 +-------------------------- + Total = 6 + + +*/ + +// CODE : + +// #include +#include +#include +using namespace std; + +int totalHammingDistance(vector& nums) { + int totalDistance=0; + int n=nums.size(); + // Here we are assuing that the 32 bits are sufficient to represent all the numbers in the array + for(int i=0;i<32;i++){ + int c=0;// c represents number of set bits of a particular position in the whole array. + for(int j=0;j> n; + vector nums(n); + for(int i=0;i> nums[i]; + } + int total_hamming_distance = totalHammingDistance(nums); + cout << total_hamming_distance << endl; + return 0; +} \ No newline at end of file diff --git a/Arrays/transpose_matrix.java b/Arrays/transpose_matrix.java deleted file mode 100644 index 26eb1335..00000000 --- a/Arrays/transpose_matrix.java +++ /dev/null @@ -1,90 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * You are given a matrix A, you have to return another matrix which is the transpose of A. - * - * NOTE: Transpose of a matrix A is defined as - AT[i][j] = A[j][i] ; Where 1 ≤ i ≤ col and 1 ≤ j ≤ row. The tranpose of a matrix switches the element at (i, j)th index to (j, i)th index, and the element at (j, i)th index to (i, j)th index. - * - * - * Problem Constraints - * - * 1 <= A.size() <= 1000 - * - * 1 <= A[i].size() <= 1000 - * - * 1 <= A[i][j] <= 1000 - * - * - * - * Input Format - * - * First argument is a 2D matrix of integers. - * - * - * - * Output Format - * - * You have to return the Transpose of this 2D matrix. - * - * - * - * Example Input - * - * Input 1: - * - * A = [[1, 2, 3],[4, 5, 6],[7, 8, 9]] - * Input 2: - * - * A = [[1, 2],[1, 2],[1, 2]] - * - * - * Example Output - * - * Output 1: - * - * [[1, 4, 7], [2, 5, 8], [3, 6, 9]] - * Output 2: - * - * [[1, 1, 1], [2, 2, 2]] - * - * - * Example Explanation - * - * Explanation 1: - * - * Clearly after converting rows to column and columns to rows of [[1, 2, 3],[4, 5, 6],[7, 8, 9]] - * we will get - * [[1, 4, 7], - * [2, 5, 8], - * [3, 6, 9]]. - */ -public class TransposeMatrix { - public static void main(String[] args) { - List> array = new ArrayList<>(); - array.add(Arrays.asList(1, 4, 7)); - array.add(Arrays.asList(2, 5, 8)); - array.add(Arrays.asList(3 ,6, 9)); - - List> result = solve(array); - for (List list: result) { - System.out.println(list); - } - } - public static List> solve(List> array) { -// O(N *M) time | O(N * M) space - List> result = new ArrayList<>(); - int row = array.size(); - int col = array.get(0).size(); - for (int i = 0; i < col; i++) { - List currentRow = new ArrayList<>(); - for (int j = 0; j < row; j++) { - int currentNum = array.get(j).get(i); - currentRow.add(currentNum); - } - result.add(currentRow); - } - return result; - } -} diff --git a/Arrays/triplet_sum.cpp b/Arrays/triplet_sum.cpp new file mode 100644 index 00000000..0dfa6c78 --- /dev/null +++ b/Arrays/triplet_sum.cpp @@ -0,0 +1,75 @@ +/* +Given an array of integers, nums, and an integer value, target, +determine if there are any three integers in nums whose sum equals the target. +Return TRUE if three such integers are found in the array. Otherwise, return FALSE. +*/ + +#include +#include +#include +/* + This implementation uses the two pointer technique to find all triplets in the input array that sum up to the target sum. + We first sort the input array in non-decreasing order to simplify the process of finding triplets. We then loop through + the array and use two pointers, one starting from the left and one starting from the right, to find triplets that sum up + to the target sum. For each iteration of the loop, we set the left pointer to the index immediately to the right of the + current index, and the right pointer to the index of the last element in the array. We then move the left and right pointers + towards the center, checking at each step whether the triplet formed by the current indices sums up to the target sum. + If it does, we add the triplet to the result vector and continue searching for other triplets. + If the sum is less than the target sum, we move the left pointer towards the center to find larger numbers. + If the sum is greater than the target sum, we move the right pointer towards the center to find smaller numbers. +*/ +std::vector> threeNumberSum(std::vector& nums, int targetSum) { + // Sort the input array in non-decreasing order + std::sort(nums.begin(), nums.end()); + + std::vector> triplets; + + // Loop through the array + for (int i = 0; i < nums.size() - 2; i++) { + int left = i + 1; + int right = nums.size() - 1; + + // While the left index is less than the right index + while (left < right) { + int currentSum = nums[i] + nums[left] + nums[right]; + + if (currentSum == targetSum) { + // If the current triplet sums up to the target sum, add it to the result vector + triplets.push_back({ nums[i], nums[left], nums[right] }); + + // Move the left and right indices towards the center to find other triplets + left++; + right--; + } else if (currentSum < targetSum) { + // If the current triplet sums up to less than the target sum, move the left index towards the center to find larger numbers + left++; + } else { + // If the current triplet sums up to more than the target sum, move the right index towards the center to find smaller numbers + right--; + } + } + } + + return triplets; +} + +int main() { + // Example usage + std::vector nums = { 12, 3, 1, 2, -6, 5, -8, 6 }; + int targetSum = 0; + + std::vector> triplets = threeNumberSum(nums, targetSum); + + for (std::vector& triplet : triplets) { + std::cout << "["; + for (int i = 0; i < triplet.size(); i++) { + std::cout << triplet[i]; + if (i < triplet.size() - 1) { + std::cout << ", "; + } + } + std::cout << "]" << std::endl; + } + + return 0; +} diff --git a/Arrays/triplet_sum.go b/Arrays/triplet_sum.go new file mode 100644 index 00000000..cf72184d --- /dev/null +++ b/Arrays/triplet_sum.go @@ -0,0 +1,81 @@ +/* +Given an array of integers, nums, and an integer value, target, +determine if there are any three integers in nums whose sum equals the target. +Return TRUE if three such integers are found in the array. Otherwise, return FALSE. +*/ + +package main + +import ( + "fmt" + "sort" + "strings" +) + +// Time complexity : Sorting the array O(n log(n)) and Nested loop to find triplet O(n^{2}) which can be simplified to O(n^{2}) +// Space complexity is O(1) since we use a fixed amount of extra space in memory. +// FindSumOfThree is our challenge function +func findSumOfThree(nums []int, target int) bool { + // Sorting the input vector + sort.Sort(sort.IntSlice(nums)) + + // We create two pointers to track our indices and, variable to store our triple sum + low, high, triple := 0, 0, 0 + + // Fix one element at a time and find the other two + for i := 0; i < len(nums) - 2; i++ { + // Set the indices of the two pointers + // Index of the first of the remaining elements + low = i + 1 + + // Last index + high = len(nums) - 1 + + for low < high { + // Check if the sum of the triple is equal to the sum + triple = nums[i] + nums[low] + nums[high] + + // Found a triple whose sum equals the target + if triple == target { + return true + + // Move low pointer forward if the triple sum is less than the required sum + } else if triple < target { + low += 1 + } else { + // Move the high pointer backwards if the triple sum is greater than the required sum + high -= 1 + } + } + } + return false +} + +// Driver code +func main() { + numsLists := [][]int { + {3, 7, 1, 2, 8, 4, 5}, + {-1, 2, 1, -4, 5, -3}, + {2, 3, 4, 1, 7, 9}, + {1, -1, 0}, + {2, 4, 2, 7, 6, 3, 1}, + } + testLists := [][]int { + {10, 20, 21}, + {-8, 0, 7}, + {8, 10, 20}, + {1, -1, 0}, + {8, 11, 15}, + } + for i, nList := range numsLists { + fmt.Printf("%d. Input array: %s\n", i + 1, strings.Replace(fmt.Sprint(nList), " ", ", ", -1)) + for _, tList := range testLists[i] { + if findSumOfThree(nList, tList) { + fmt.Printf(" Sum for %d exists\n", tList) + } else { + fmt.Printf(" Sum for %d does not exist\n", tList) + } + } + fmt.Printf("%s\n", strings.Repeat("-", 100)) + } +} \ No newline at end of file diff --git a/Arrays/triplet_sum.java b/Arrays/triplet_sum.java new file mode 100644 index 00000000..bf8faf11 --- /dev/null +++ b/Arrays/triplet_sum.java @@ -0,0 +1,76 @@ +/* +Given an array of integers, nums, and an integer value, target, +determine if there are any three integers in nums whose sum equals the target. +Return TRUE if three such integers are found in the array. Otherwise, return FALSE. +*/ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/* +We sort the input array in non-decreasing order using the Arrays.sort() method, and then loop through +the array using a for loop. We use two pointers, one starting from the left and one starting from the right, +to find triplets that sum up to the target sum. For each iteration of the loop, we set the left pointer to +the index immediately to the right of the current index, and the right pointer to the index of the last +element in the array. We then move the left and right pointers towards the center, checking at each step +whether the triplet formed by the current indices sums up to the target sum. If it does, we add the triplet +to the result list and continue searching for other triplets. If the sum is less than the target sum, we +move the left pointer towards the center to find larger numbers. If the sum is greater than the target +sum, we move the right pointer towards the center to find smaller numbers. +Finally, we print out the result list using a nested for loop to iterate through each triplet and each element in each triplet. +*/ +public class ThreeNumberSum { + + public static List> threeNumberSum(int[] nums, int targetSum) { + Arrays.sort(nums); // Sort the input array in non-decreasing order + List> triplets = new ArrayList<>(); + + // Loop through the array + for (int i = 0; i < nums.length - 2; i++) { + int left = i + 1; + int right = nums.length - 1; + + // While the left index is less than the right index + while (left < right) { + int currentSum = nums[i] + nums[left] + nums[right]; + + if (currentSum == targetSum) { + // If the current triplet sums up to the target sum, add it to the result list + triplets.add(Arrays.asList(nums[i], nums[left], nums[right])); + + // Move the left and right indices towards the center to find other triplets + left++; + right--; + } else if (currentSum < targetSum) { + // If the current triplet sums up to less than the target sum, move the left index towards the center to find larger numbers + left++; + } else { + // If the current triplet sums up to more than the target sum, move the right index towards the center to find smaller numbers + right--; + } + } + } + + return triplets; + } + + public static void main(String[] args) { + // Example usage + int[] nums = { 12, 3, 1, 2, -6, 5, -8, 6 }; + int targetSum = 0; + + List> triplets = threeNumberSum(nums, targetSum); + + for (List triplet : triplets) { + System.out.print("["); + for (int i = 0; i < triplet.size(); i++) { + System.out.print(triplet.get(i)); + if (i < triplet.size() - 1) { + System.out.print(", "); + } + } + System.out.println("]"); + } + } +} diff --git a/Arrays/triplet_sum.js b/Arrays/triplet_sum.js new file mode 100644 index 00000000..282f016b --- /dev/null +++ b/Arrays/triplet_sum.js @@ -0,0 +1,73 @@ +/* +Given an array of integers, nums, and an integer value, target, +determine if there are any three integers in nums whose sum equals the target. +Return TRUE if three such integers are found in the array. Otherwise, return FALSE. + +Sample Input : 3, 7, 1, 2, 8, 4, 5 Target : 18 +Output : True +Sample Input : 0 -1 1 Target : 2 +Output : False + +APPROACH: + +1) We need to sort the array for our strategy to work. +2) Iterating from index 0, we check three numbers: + - value at index i (starting at 0) + - low: value at index i + 1 + - high: value at index nums.length - 1 (the last value, also the highest) +3) Take the sum of these three values. + - If sum equals target, return true + - If sum is less than the target, increment "low" and run this step again + - If sum is higher than the target, decrement "high" and run this step again +4) If low meets high, we ran out of numbers to try. Back to step 2. +5) Return false at the end of everything because we found no matching sums + +For a similar problem: +https://leetcode.com/problems/3sum/ + +*/ + + +const sumOfThree = (nums, target) => { + // function only applies to arrays with at least 3 values + if (nums.length < 3) return false + + // Sort the array + nums.sort() + + let low, high + + // Iterate through each value (up to 3rd from last) + for (let i = 0; i < nums.length - 2; i++) { + + //Assign pointers and check the sum + low = i + 1 + high = nums.length - 1 + + while (low < high) { + + let sum = nums[i] + nums[low] + nums[high] + + // match found, return true + if (sum === target) { + return true + + // sum is too low, increase low pointer + } else if (sum < target) { + low++ + + // sum is too high, decrease high pointer + } else { + high-- + } + } + } + + return false +} + +console.log(sumOfThree([3, 7, 1, 2, 8, 4, 5], 18)) // true +console.log(sumOfThree([3, 7, 1, 2, 8, 4, 5], 50)) // false +console.log(sumOfThree([0, -1, 1], 2)) // false +console.log(sumOfThree([0, -1, 1], 0)) // true + diff --git a/Arrays/triplet_sum.py b/Arrays/triplet_sum.py new file mode 100644 index 00000000..0bb884ef --- /dev/null +++ b/Arrays/triplet_sum.py @@ -0,0 +1,41 @@ +''' +Given an array of integers, nums, and an integer value, target, +determine if there are any three integers in nums whose sum equals the target. +Return TRUE if three such integers are found in the array. Otherwise, return FALSE. +''' + +def find3Numbers(A, arr_size, sum): + + # Sort the elements + A.sort() + + # Now fix the first element + # one by one and find the + # other two elements + for i in range(0, arr_size-2): + + # index of the first element + # in the remaining elements + l = i + 1 + + # index of the last element + r = arr_size-1 + while (l < r): + + if( A[i] + A[l] + A[r] == sum): + return True + elif (A[i] + A[l] + A[r] < sum): + l += 1 + else: # A[i] + A[l] + A[r] > sum + r -= 1 + + # If we reach here, then + # no triplet was found + return False + +# Driver program to test above function +A = [1, 2, 3, 4, 5, 6] +sum = 9 +arr_size = len(A) + +print(find3Numbers(A, arr_size, sum)) \ No newline at end of file diff --git a/CCTI/urlify.go b/Arrays/urlify.go similarity index 100% rename from CCTI/urlify.go rename to Arrays/urlify.go diff --git a/Arrays/validate_subsequence.go b/Arrays/validate_subsequence.go deleted file mode 100644 index 9876e048..00000000 --- a/Arrays/validate_subsequence.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - Given two non-empty arrays of integers, write a function that determines - whether the second array is a subsequence of the first one. - - A subsequence of an array is a set of numbers that aren't necessarily adjacent - in the array but that are in the same order as they appear in the array. For - instance, the numbers - - Sample Input : Array [1, 2, 3, 4, 5,] Seq [2, 4, 5] - Output : True - Sample Input : Array [77, 2, 3, 4, 5,] Seq [2, 77, 5] - Output : False - - -*/ -package main - -import "fmt" - - -func IsValidSubsequence(array []int, sequence []int) bool { - // Write your code here. - index := 0 - for i := 0; i < len(array) && index < len(sequence); i++ { - if array[i] == sequence[index] { - index++ - } - } - return index == len(sequence) -} - -func main() { - arr := []int{1, 2, 3, 4, 5, 6} - seq := []int{2, 4, 6} - msg := IsValidSubsequence(arr, seq) - fmt.Println(msg) -} \ No newline at end of file diff --git a/Arrays/validate_subsequence.java b/Arrays/validate_subsequence.java deleted file mode 100644 index c74bbe47..00000000 --- a/Arrays/validate_subsequence.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * - - Given two non-empty arrays of integers, write a function that determines whether the second array is a subsequence of the first one. - - A subsequence of an array is a set of numbers that aren't necessarily adjacent in the array but that are in the same order as they appear in the array. For instance, the numbers [1, 3, 4] form a subsequence of the array [1, 2, 3, 4], and so do the numbers [2, 4]. Note that a single number in an array and the array itself are both valid subsequences of the array. - Sample Input - - array = [5, 1, 22, 25, 6, -1, 8, 10] - sequence = [1, 6, -1, 10] - - Sample Output - - true - - - */ -public class ValidateSubsequence { - public static void main(String[] args){ - int[] array = {5, 1, 22, 25, 6, -1, 8, 10}; - int[] sequence = {1, 6, -1, 19}; - - System.out.println(solve(array, sequence)); - } - public static boolean solve(int[] array, int[] sequence) { - - int seqIdx = 0; - for(var num: array) { - if(seqIdx == sequence.length) break; - if(sequence[seqIdx] == num ) seqIdx++; - } - return seqIdx == sequence.length; - } -} \ No newline at end of file diff --git a/Backtracking/Generate_Parentheses.py b/Backtracking/Generate_Parentheses.py new file mode 100644 index 00000000..e2b62a46 --- /dev/null +++ b/Backtracking/Generate_Parentheses.py @@ -0,0 +1,29 @@ +''' + Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. + Example 1: + Input: n = 3 + Output: ["((()))","(()())","(())()","()(())","()()()"] + + Example 2: + Input: n = 1 + Output: ["()"] + Constraints: + 1 <= n <= 8 +''' +class Solution: + #reference: GeeksforGeeks for comments + '''To form all the sequences of balanced bracket subsequences with n pairs. So there are n opening brackets and n closing brackets. So the subsequence will be of length 2*n. There is a simple idea, the i’th character can be ‘{‘ if and only if the count of ‘{‘ till i’th is less than n and i’th character can be ‘}’ if and only if the count of ‘{‘ is greater than the count of ‘}’ till index i. If these two cases are followed then the resulting subsequence will always be balanced''' + + def generate(self,n,open,close,s,final_arr): + if(open==n and close==n): + final_arr.append(s) + return + if(open List[str]: + final_arr = [] + Solution.generate(self,n,0,0,"",final_arr) + return final_arr \ No newline at end of file diff --git a/Backtracking/geenrate_parentheses.go b/Backtracking/geenrate_parentheses.go new file mode 100644 index 00000000..6f678833 --- /dev/null +++ b/Backtracking/geenrate_parentheses.go @@ -0,0 +1,78 @@ +// Implementation of generating all combinations of well-formed parentheses +/* + The generateParenthesis function takes an integer n as input, which represents the number of pairs of + parentheses to generate. It initializes an empty array result to store the valid combinations and calls + the backtrack helper function to generate all the combinations. + + The backtrack function takes four arguments: + + a pointer to the result array to add the valid combinations + the current string cur + the number of open brackets open + the number of closed brackets close + the maximum number of pairs of parentheses max + + The function first checks if the current string has reached the maximum length, i.e., len(cur) == max*2. + If it has, the function appends the current string to the result array and returns. + + If there are still open brackets left to add, i.e., open < max, the function appends an open bracket to + the current string and recursively calls backtrack with open+1 and close. This represents adding an open + bracket to the current combination. + + If there are more closed brackets than open brackets, i.e., close < open, the function appends a + closed bracket to the current string and recursively calls backtrack with open and close+1. + This represents adding a closed bracket to the current combination. + + In the main function, we call generateParenthesis with n=3 and print the resulting array. + This will output all possible combinations of three pairs of well-formed parentheses. + + Input: n = 3 + + Output: ["((()))","(()())","(())()","()(())","()()()"] + + Explanation: + + There are 5 valid combinations of well-formed parentheses with 3 pairs of parentheses. + The output lists all of them. + + The time complexity of the above program is O(4^n / sqrt(n)), where n is the number of parentheses. + This is because there are a total of 2n positions in the output string, and at each position, + we have two choices - either to put an opening or closing parenthesis. Therefore, the total + number of possible combinations is 2^(2n). However, not all of these combinations are valid, + as some may violate the well-formedness condition. We can use Catalan number to determine the + actual number of valid combinations. The nth Catalan number is given by (2n)! / (n+1)!n!, which + is approximately equal to 4^n / sqrt(n*pi). Thus, the time complexity is O(4^n / sqrt(n)). +*/ +package main + +import "fmt" + +func generateParenthesis(n int) []string { + var result []string + backtrack(&result, "", 0, 0, n) + return result +} + +// helper function to backtrack and generate all valid combinations +func backtrack(result *[]string, cur string, open int, close int, max int) { + // base case: if the current string has reached the maximum length, add it to the result + if len(cur) == max*2 { + *result = append(*result, cur) + return + } + + // if there are still open brackets left to add, add one and recurse + if open < max { + backtrack(result, cur+"(", open+1, close, max) + } + + // if there are more closed brackets than open brackets, add a closed bracket and recurse + if close < open { + backtrack(result, cur+")", open, close+1, max) + } +} + +func main() { + result := generateParenthesis(3) + fmt.Println(result) +} diff --git a/Backtracking/n_queen.cpp b/Backtracking/n_queen.cpp new file mode 100644 index 00000000..d681dda0 --- /dev/null +++ b/Backtracking/n_queen.cpp @@ -0,0 +1,80 @@ +#include +#include + +using namespace std; + +// Function to check if it's safe to place a queen at board[row][col] +bool isSafe(vector& board, int row, int col, int N) { + // Check the row on the left side + for (int i = 0; i < col; ++i) { + if (board[row][i] == 'Q') { + return false; + } + } + + // Check upper diagonal on the left side + for (int i = row, j = col; i >= 0 && j >= 0; --i, --j) { + if (board[i][j] == 'Q') { + return false; + } + } + + // Check lower diagonal on the left side + for (int i = row, j = col; i < N && j >= 0; ++i, --j) { + if (board[i][j] == 'Q') { + return false; + } + } + + return true; +} + +// Recursive function to solve N-Queens problem +bool solveNQueens(vector& board, int col, int N) { + if (col == N) { + // All queens are placed successfully + return true; + } + + for (int i = 0; i < N; ++i) { + if (isSafe(board, i, col, N)) { + // Place queen + board[i][col] = 'Q'; + + // Recur to place the rest of the queens + if (solveNQueens(board, col + 1, N)) { + return true; + } + + // If placing queen in board[i][col] doesn't lead to a solution, backtrack + board[i][col] = '.'; + } + } + + // If no queen can be placed in this column, return false + return false; +} + +// Function to solve N-Queens problem and print the solution +void solveNQueens(int N) { + vector board(N, string(N, '.')); + + if (solveNQueens(board, 0, N)) { + // Print the solution + for (int i = 0; i < N; ++i) { + cout << board[i] << endl; + } + } else { + cout << "No solution exists." << endl; + } +} + +int main() { + int N; + cout << "Enter the number of queens (N): "; + cin >> N; + + solveNQueens(N); + + return 0; +} diff --git a/Backtracking/n_queens.java b/Backtracking/n_queens.java new file mode 100644 index 00000000..5070d2c0 --- /dev/null +++ b/Backtracking/n_queens.java @@ -0,0 +1,112 @@ +/** + * Problem :- N-Queens + https://leetcode.com/problems/n-queens/description/ + + + Approach:- + The approach uses backtracking to generate all possible configurations of queens on the board and checks the validity of each configuration. + It maintains a boolean board to represent the placement of queens, where `true` indicates the presence of a queen in a particular cell. + The algorithm starts by placing a queen in the first row and proceeds recursively to the next row, checking all possible column positions for the queen. + If a valid position is found, the algorithm moves to the next row and repeats the process. If all N queens are placed on the board, + a valid solution is found and added to the list of solutions. + + +To check the validity of a queen's position, the algorithm verifies three conditions: +1. Vertical Check: It checks the columns of the previous rows to ensure that there are no queens in the same column. +2. Left Diagonal Check: It checks the diagonal elements on the left side of the current position to ensure that there are no queens present. +3. Right Diagonal Check: It checks the diagonal elements on the right side of the current position to ensure that there are no queens present. + +The algorithm continues this process, backtracking whenever it encounters an invalid position or explores all possibilities. Once all valid configurations are found, they are converted into a list of strings representing the board configurations, and the list of solutions is returned. + + + + +Time Complexity: O(N!) + In the worst case, the backtracking algorithm explores all possible configurations,. However, with pruning techniques, the actual runtime is significantly lower. + +Space Complexity: O(N^2) + because the boolean board of size NxN is used to represent the placement of queens, and the list of solutions also occupies additional space. + + */ + +class Solution { + public List> solveNQueens(int n) { + List> list = new ArrayList<>(); + + // If the board size is 2 or 3, no solution is possible, so return an empty list + if (n == 2 || n == 3) { + return list; + } + + // If the board size is 1, there is only one solution with a single queen in the only cell + if (n == 1) { + String ans = "Q"; + ArrayList a = new ArrayList<>(); + a.add(ans); + list.add(a); + return list; + } + + boolean arr[][] = new boolean[n][n]; // Create a boolean board to represent the placement of queens + queens(arr, 0, list); // Solve the N-queens problem recursively + return list; // Return the list of solutions + } + + // Recursive function to solve the N-queens problem + static void queens(boolean board[][], int row, List> list) { + if (row == board.length) { + ArrayList arr = new ArrayList<>(); + + // Convert the boolean board to a list of strings representing the board configuration + for (int j = 0; j < board.length; j++) { + String ans = ""; + for (int i = 0; i < board[j].length; i++) { + if (board[j][i] == false) { + ans += "."; + } else { + ans += "Q"; + } + } + arr.add(ans); + } + list.add(arr); // Add the board configuration to the list of solutions + return; + } + + for (int col = 0; col < board.length; col++) { + if (isSafe(board, row, col)) { + board[row][col] = true; // Place a queen in the current position + queens(board, row + 1, list); // Recursively solve the problem for the next row + board[row][col] = false; // Backtrack and remove the queen from the current position + } + } + } + + // Function to check if it is safe to place a queen in a given position + private static boolean isSafe(boolean[][] board, int row, int col) { + // Check the vertical column for any previously placed queens + for (int i = 1; i <= row; i++) { + if (board[row - i][col]) { + return false; + } + } + + // Check the left diagonal for any previously placed queens + int max = Math.min(row, col); + for (int i = 1; i <= max; i++) { + if (board[row - i][col - i]) { + return false; + } + } + + // Check the right diagonal for any previously placed queens + int maxtimes = Math.min(row, board.length - col - 1); + for (int i = 1; i <= maxtimes; i++) { + if (board[row - i][col + i]) { + return false; + } + } + + return true; // It is safe to place a queen in the given position + } +} diff --git a/Backtracking/sudoko_solver.java b/Backtracking/sudoko_solver.java new file mode 100644 index 00000000..c726d7e5 --- /dev/null +++ b/Backtracking/sudoko_solver.java @@ -0,0 +1,103 @@ +/*Write a program to solve a Sudoku puzzle by filling the empty cells. + +A sudoku solution must satisfy all of the following rules: + +Each of the digits 1-9 must occur exactly once in each row. +Each of the digits 1-9 must occur exactly once in each column. +Each of the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid. +The '.' character indicates empty cells. + + + +Example 1: + + +Input: board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]] +Output: [["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]] +Explanation: The input board is shown above and the only valid solution is shown below: + + */ +package Backtracking; + +public class sudokoSolver { + + + public void solveSudoku(char[][] board) { + Solver(board, 0); + } + + static boolean Solver(char[][] board , int total) + { + if(total==81) + { + return true; + } + int sr = total/9; + int sc = total%9; + + if(board[sr][sc]!='.') + { + return Solver(board, total+1); + } + for(int i=1 ; i<=9 ; i++) + { + char curr = (char) (i+'0'); + if(canElementBePlaced(board , sr , sc , curr)) + { + board[sr][sc]=curr; + if (Solver(board , total+1)) + { + return true; + } + board[sr][sc]='.'; + } + } + return false; + } + static void print(char[][] board) + { + for(int i=0; i<9 ;i++) + { + for(int j=0 ; j<9 ; j++) + { + System.out.print(board[i][j]+", "); + } + System.out.println(); + } + } + + static boolean canElementBePlaced(char[][] board , int r , int c , char k) + { + for(int i=0 ; i<9 ; i++) + { + if(board[r][i]==k) + { + return false; + } + } + + for(int j =0 ; j<9 ; j++) + { + if(board[j][c]==k) + { + return false; + } + } + + int sr = r-(r%3); + int sc = c-(c%3); + + for(int i=sr; i<=sr+2;i++) + { + for(int j=sc ; j<=sc+2; j++) + { + if (board[i][j]==k) + { + return false; + } + } + } + return true; + } + +} diff --git a/Binary Search/BinarySearchRecursive.java b/Binary Search/BinarySearchRecursive.java new file mode 100644 index 00000000..56dceb79 --- /dev/null +++ b/Binary Search/BinarySearchRecursive.java @@ -0,0 +1,48 @@ + +/* + * Problem : Implement Binary Search using Recursion. + * + * RECURSION : Function that calls itself is called recursion. + * + * Algorithm: + * 1) Get an sorted array and a target to be found in the array. + * 2) Find the middle index by dividing the first and last element. + * 3) If the middle element is equal to target , return the mid index. + * 4) If the middle element is greater than the target , in the next recursive call make last element as mid-1 + * 5) If the middle element is smaller than the target , in the next recursive call make first element as mid+1 + * 6) If start is less than end ,ie,if the element not found , return -1. + * + * Time complexity: worst case: O(logN) , N - size of the array. + * + * Space Complexity: O(logN) (since we use stack for function calls) + * + */ + + +public class BinarySearchRecursive { + + public static void main(String[] args) { + int[] arr = {1,2,5,89,99,101}; + int target = 1; + System.out.println(binarySearch(arr , target,0 , arr.length-1)); + } + + static int binarySearch(int[] arr ,int target, int start , int end) { + + int mid = (start + end)/2; + + if (start > end) { + return -1; + } + if (arr[mid] == target) { + return mid; + } + + else if (arr[mid] > target) { + return binarySearch(arr , target , start , mid-1); + } + + return binarySearch(arr ,target , mid+1, end); + + } +} diff --git a/Binary Search/binary_search.cpp b/Binary Search/binary_search.cpp new file mode 100644 index 00000000..835ec7a5 --- /dev/null +++ b/Binary Search/binary_search.cpp @@ -0,0 +1,61 @@ +/* + The binarySearch function takes in a sorted vector of integers arr and a target value target to search for. + The function initializes the left and right pointers to the first and last indices of the array respectively. + The function uses a while loop to keep searching until the left and right pointers meet. + Inside the loop, the function calculates the middle index by adding the left and right indices and dividing by 2. + If the middle element is equal to the target, the function returns the middle index. + If the target is greater than the middle element, the function discards the left half of the array and moves the left pointer to the middle + 1 index. + If the target is smaller than the middle element, the function discards the right half of the array and moves the right pointer to the middle - 1 index. + If the target is not found in the array, the function returns -1. + The driver code initializes a sorted array and a target value, and calls the binarySearch function to search for the target value. + The function returns the index of the target value, or -1 if it is not found. + The driver code prints the result of the search to the console. +*/ +#include +#include + +using namespace std; + +// Binary search function +// Takes in a sorted vector of integers and a target value to search for +// Returns the index of the target value if found, -1 otherwise +int binarySearch(vector arr, int target) { + int left = 0; + int right = arr.size() - 1; + + // Loop until left and right pointers meet + while (left <= right) { + int mid = left + (right - left) / 2; + + // If the target is found, return the index + if (arr[mid] == target) { + return mid; + } + // If target is greater than the middle element, discard the left half + else if (arr[mid] < target) { + left = mid + 1; + } + // If target is smaller than the middle element, discard the right half + else { + right = mid - 1; + } + } + + // Target not found + return -1; +} + +// Driver code to test binary search function +int main() { + vector arr = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}; + int target = 12; + int result = binarySearch(arr, target); + + if (result == -1) { + cout << "Element not found" << endl; + } + else { + cout << "Element found at index " << result << endl; + } + return 0; +} diff --git a/Binary Search/binary_search.js b/Binary Search/binary_search.js new file mode 100644 index 00000000..24a59582 --- /dev/null +++ b/Binary Search/binary_search.js @@ -0,0 +1,30 @@ +/** + * Finds the first and last occurrence of a target value in a sorted array. + * + * @param {number[]} N - The sorted array of numbers. + * @param {number} T - The target value to search for. + * @returns {number[]} An array containing the first and last index of the target value, or [-1,-1] if not found. + */ +const searchRange = function(N, T) { + // Helper function to perform binary search on the array + const find = (target, arr, left=0, right=arr.length) => { + while (left <= right) { + // Calculate the middle index + let mid = left + right >> 1; + // If the middle element is less than the target, move the left pointer to mid + 1 + if (arr[mid] < target) left = mid + 1; + // If the middle element is greater than or equal to the target, move the right pointer to mid - 1 + else right = mid - 1; + } + // Return the left pointer, which will be the index of the target or the insertion point if not found + return left; + }; + + // Find the leftmost index of the target value + let Tleft = find(T, N); + // If the target value is not found in the array, return [-1,-1] + if (N[Tleft] !== T) return [-1,-1]; + // Find the rightmost index of the target value + return [Tleft, find(T+1, N, Tleft) - 1]; + }; + \ No newline at end of file diff --git a/Binary_Search/binary_search.py b/Binary Search/binary_search.py similarity index 97% rename from Binary_Search/binary_search.py rename to Binary Search/binary_search.py index 8f3a5daf..a0ba8c7a 100644 --- a/Binary_Search/binary_search.py +++ b/Binary Search/binary_search.py @@ -1,48 +1,48 @@ -""" - Intuition: - If you have to guess a magic number from 1-100, the best first attempt would be to guess '50' - or in other words, the middle. If I tell you that the magic number is higher, - you now don't need to consider all numbers 1-50, - and if it is lower you wouldn't need to consider numbers 50-100!! - - In Binary Search, we follow the same idea, - 1. Compar the target with the middle element. - 2. If the target is higher, then the target can only lie in the right (greater) subarray. We re-calculate mid and repeat step 1. - 3. If the target is lower, the target can only lie in the left (lower) half. We re-calculate mid and repeat step 1. - - Binary search can only operate on a sorted array. - Further reading: https://en.wikipedia.org/wiki/Binary_search_algorithm -""" - - - - -import math - -def binary_search(lst, target): - if not lst: - return -1 - lo = 0 - hi = len(lst)-1 - - while lo <= hi: - mid = math.floor(lo + (hi - lo) / 2) # Find mid. math.floor is used to round floats down. - if lst[mid] < target: # Element in mid is lower than target. - lo = mid + 1 # Our low (start) becomes the element after mid. - elif lst[mid] > target: # Element in mid is higher than target. - hi = mid - 1 # Our high (end) becomes the element before mid. - elif lst[mid] == target: - print(f"Found {target} at index {mid}.") - return mid - print(f"Target {target} not found.") - return -1 - - -arr = [10, 20, 30, 50, 60, 80, 110, 130, 140, 170] -binary_search(arr, 80) -binary_search(arr, 10) -binary_search(arr, 110) -binary_search(arr, 20) -binary_search(arr, 140) -binary_search(arr, 2) -binary_search(arr, 1) +""" + Intuition: + If you have to guess a magic number from 1-100, the best first attempt would be to guess '50' + or in other words, the middle. If I tell you that the magic number is higher, + you now don't need to consider all numbers 1-50, + and if it is lower you wouldn't need to consider numbers 50-100!! + + In Binary Search, we follow the same idea, + 1. Compar the target with the middle element. + 2. If the target is higher, then the target can only lie in the right (greater) subarray. We re-calculate mid and repeat step 1. + 3. If the target is lower, the target can only lie in the left (lower) half. We re-calculate mid and repeat step 1. + + Binary search can only operate on a sorted array. + Further reading: https://en.wikipedia.org/wiki/Binary_search_algorithm +""" + + + + +import math + +def binary_search(lst, target): + if not lst: + return -1 + lo = 0 + hi = len(lst)-1 + + while lo <= hi: + mid = math.floor(lo + (hi - lo) / 2) # Find mid. math.floor is used to round floats down. + if lst[mid] < target: # Element in mid is lower than target. + lo = mid + 1 # Our low (start) becomes the element after mid. + elif lst[mid] > target: # Element in mid is higher than target. + hi = mid - 1 # Our high (end) becomes the element before mid. + elif lst[mid] == target: + print(f"Found {target} at index {mid}.") + return mid + print(f"Target {target} not found.") + return -1 + + +arr = [10, 20, 30, 50, 60, 80, 110, 130, 140, 170] +binary_search(arr, 80) +binary_search(arr, 10) +binary_search(arr, 110) +binary_search(arr, 20) +binary_search(arr, 140) +binary_search(arr, 2) +binary_search(arr, 1) diff --git a/Binary_Search/binary_search_iterative.go b/Binary Search/binary_search_iterative.go similarity index 100% rename from Binary_Search/binary_search_iterative.go rename to Binary Search/binary_search_iterative.go diff --git a/Binary_Search/binary_search_recursive.go b/Binary Search/binary_search_recursive.go similarity index 100% rename from Binary_Search/binary_search_recursive.go rename to Binary Search/binary_search_recursive.go diff --git a/Binary Search/binary_serach_first_and_last_occurence.py b/Binary Search/binary_serach_first_and_last_occurence.py new file mode 100644 index 00000000..be1a79d6 --- /dev/null +++ b/Binary Search/binary_serach_first_and_last_occurence.py @@ -0,0 +1,40 @@ +class Solution: + def __init__(self): + self.start_index=-1 + self.end_index=-1 + + ''' Time complexity - O(logn), Space complexity - O(1)'''' + '''Once the binary Search is implemented, to find the start_index, we store the current index where we find the target and implement search again on the left side of the array''' + def binarySearchFirst(self,arr,low,high,target): + if(low>high): + return + mid=(low+high)//2 + if(arr[mid]>target): + Solution.binarySearchFirst(self,arr,low,mid-1,target) + elif(arr[mid]high): + return + mid=(low+high)//2 + if(arr[mid]>target): + Solution.binarySearchLast(self,arr,low,mid-1,target) + elif(arr[mid] List[int]: + + #implementing binarySearchLast to populate the self.end_index + Solution.binarySearchLast(self,nums,0,len(nums)-1,target) + + #implementing binarySearchFirst to populate the self.start_index + Solution.binarySearchFirst(self,nums,0,len(nums)-1,target) + return [self.start_index, self.end_index] \ No newline at end of file diff --git a/Binary Search/first_and_last_pos.js b/Binary Search/first_and_last_pos.js new file mode 100644 index 00000000..da2e1fbf --- /dev/null +++ b/Binary Search/first_and_last_pos.js @@ -0,0 +1,105 @@ +/* + -----------QUESTION-------------- + +Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. +If target is not found in the array, return [-1, -1]. Devise an algorithm with O(log n) runtime complexity. + +Example 1: + Input: nums = [5,7,7,8,8,10], target = 8 + Output: [3,4] + +Example 2: + Input: nums = [5,7,7,8,8,10], target = 6 + Output: [-1,-1] + +Example 3: + Input: nums = [], target = 0 + Output: [-1,-1] + + +Constraints: + 0 <= nums.length <= 105 + -109 <= nums[i] <= 109 + nums is a non-decreasing array (increasing order) + -109 <= target <= 109 +*/ + + +// ------------SOLUTION------------- + +/* +An intuitive Binary Search algorithm is used to find target value indices of occurence since it is given an increasing array. We perform two searches one for first occurence and second for last occurence. + +Two pointer start and end for 0th and (N-1)th index to iteratively find the mid of array. + +First follow the common left-based binary search to find the target value first occurence. +If found we proceed further check in left subarray as target may still be presnt either to left or right of mid. + +To find last occurence index we again iterate to right subarray from start as the current value but reset last to N-1. + +(start does not require to begin again from 0th index as in first pass we have covered upto start index. This does not affect overall complexity but reduces number of comparisions.) +*/ + + +/* + +Time Complexity: + O(log N) - Each time the sub-array reduces to half as + N/2 -> N/4 -> N/8 ... after k iterations this results to O(1) + N/ 2^k = 1 + N = 2^k (take a log) + k = log(N) (the complexity is defined by how many times the loop executes which is k times) + + T.C = O(log N) + +Space Complexity: + O(1) - Constant space for storing variables + +*/ + +var searchRange = function(nums, target) { + + let ans = [-1, -1]; + // array of resulting indices pre-initialised with -1 in case the target is not found + + let start = 0, end = nums.length - 1, mid; + + + //First Occurence + while (start <= end) { + mid = Math.floor((start + end) / 2); + + if (nums[mid] === target) { + ans[0] = mid; + end = mid - 1; + //we continue to search in left subarray to find the first occurence position of target value + } + else if (nums[mid] > target) { + end = mid - 1; + } + else { + start = mid + 1; + } + } + + + //Last Occurence + end = nums.length - 1; + while (start <= end) { + mid = Math.floor((start + end) / 2); + + if (nums[mid] === target) { + ans[1] = mid; + start = mid + 1; + //for last occurence we search the subarray right to the middle index + } + else if (nums[mid] > target) { + end = mid - 1; + } + else { + start = mid + 1; + } + } + + return ans; +}; \ No newline at end of file diff --git a/Binary Search/first_and_last_pos_of_element.cpp b/Binary Search/first_and_last_pos_of_element.cpp new file mode 100644 index 00000000..52d33555 --- /dev/null +++ b/Binary Search/first_and_last_pos_of_element.cpp @@ -0,0 +1,70 @@ +/* +Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. + +If target is not found in the array, return [-1, -1]. + +You must write an algorithm with O(log n) runtime complexity. + + + +Example 1: + +Input: nums = [5,7,7,8,8,10], target = 8 +Output: [3,4] +Example 2: + +Input: nums = [5,7,7,8,8,10], target = 6 +Output: [-1,-1] +Example 3: + +Input: nums = [], target = 0 +Output: [-1,-1] + + +Constraints: + +0 <= nums.length <= 105 +-109 <= nums[i] <= 109 +nums is a non-decreasing array. +-109 <= target <= 109 + +*/ +#include +class Solution { +public: + int get_index(vector& nums, int target, bool found){ + int ans = -1; + int start = 0, end = nums.size() - 1; + while(start <= end){ + int mid = start + (end - start) / 2; + if(nums[mid] == target){ + ans = mid; + if(found){ + end = mid - 1; // search in left part + } + else{ + start = mid + 1; // search in right part + } + } + else if(nums[mid] > target){ + end = mid - 1; + } + else{ + start = mid + 1; + } + } + return ans; + } + vector searchRange(vector& nums, int target) { + vector ans(2, -1); + int first = get_index(nums, target, true); + if(first == -1) + return ans; + int last = get_index(nums, target, false); + //ans.push_back(first); + //ans.push_back(last); + ans[0] = first; + ans[1] = last; + return ans; + } +}; \ No newline at end of file diff --git a/Binary Search/first_and_last_position.java b/Binary Search/first_and_last_position.java new file mode 100644 index 00000000..ae3f09b1 --- /dev/null +++ b/Binary Search/first_and_last_position.java @@ -0,0 +1,67 @@ +/* + Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value +If target is not found in the array, return [-1, -1]. + +You must write an algorithm with O(log n) runtime complexity. + +Example 1: + +Input: nums = [5,7,7,8,8,10], target = 8 +Output: [3,4] +Example 2: + +Input: nums = [5,7,7,8,8,10], target = 6 +Output: [-1,-1] + +How this Code Works? +The firstPos method will give the starting position of the target element. We will find the mid and check if the mid element is equals to the target we will check if previous element is equal to target or not if it is then place low to mid-1 if not just update the high to mid-1. Next condition is if the element is greater then target it means we have to search in the left side of array hence high=mid-1.And last condition if the element is less than the target do low=mid-1. If this method returns Integer.MAX_VALUE if means element is not present. Hence return {-1,-1} as answer. + +The lastPost method works same way as the above method. Passed extra paramter of low because it's now already clear that from where we have to start as we already got the first index. + */ +class FirstandLastPosition { + public int[] searchRange(int[] nums, int target) { + if(nums.length==0) return new int[]{-1,-1}; + int a=firstPos(nums, target); + int b=0; + if(a!=Integer.MAX_VALUE){ + b=lastPos(nums,target,a); + }else{ + return new int[]{-1,-1}; + } + return new int[]{a,b}; + } + private int firstPos(int[] arr,int target){ + int ans=Integer.MAX_VALUE,low=0,high=arr.length-1; + + while(low<=high){ + int mid=low+(high-low)/2; + + if(arr[mid]==target){ + ans=Math.min(ans,mid); + if(mid>0 && arr[mid-1]==target) low=mid-1; + high=mid-1; + }else if(arr[mid]>target){ + high=mid-1; + }else if(arr[mid]target){ + high=mid-1; + }else{ + low=mid+1; + } + } + return ans; + } +} diff --git a/Binary Search/first_last_pos.java b/Binary Search/first_last_pos.java new file mode 100644 index 00000000..4d9e8a94 --- /dev/null +++ b/Binary Search/first_last_pos.java @@ -0,0 +1,72 @@ +/* Given a sorted array arr containing n elements with possibly duplicate elements, +the task is to find indexes of first and last occurrences of an element x in the given array. + +Example 1: Input: arr [] = [ 1, 3, 5, 5, 5, 5, 67, 123, 125 ] , target = 5 +OUTPUT: [2 5] +Explanation: First occurrence of 5 is at index 2 and last occurrence of 5 is at index 5. + +Example 2: Input: arr[] = [1,2,3,,5,6,7,8],target = 4 +OUTPUT: [-1,-1] + +APPROACH: + #We will separately write methods for first occurence and last occurence of the element + + # Follows the standard binary search algorithm with little moddification for first and last occurence + # For the first occurence , return the start index at the end of the loop + # For the last occurence , return the end index at the end of loop*/ +import java.util.*; +public class FirstLastPos{ + public static int firstOccurence(int[] arr,int target){ + int start = 0,end=arr.length-1; + while(start <= end){ + int mid = start + (end-start)/2; + if (arr[mid]== target){ + end = mid -1; + } + else if(arr[mid] > target){ + end = mid -1; + } + else if(arr[mid] < target){ + start = mid + 1; + } + } + if(start < arr.length && arr[start] == target){ + return start; + } + else{ + return -1; + } + } + + public static int lastOccurence(int[] arr,int target){ + int start = 0,end=arr.length-1; + while(start <= end){ + int mid = start + (end-start)/2; + if (arr[mid]== target){ + start = mid + 1; + } + else if(arr[mid] > target){ + end = mid -1; + } + else if(arr[mid] < target){ + start = mid + 1; + } + } + if(end >= 0 && arr[end] == target){ + return end; + } + else{ + return -1; + } + } + + public static void main(String[] args){ + int[] arr = {1,5,7,7,7,9,11,14}; + int target = 7; + int[] result = new int[2]; + result[0] = firstOccurence(arr,target); + result[1] = lastOccurence(arr,target); + System.out.println(Arrays.toString(result)); + + } +} \ No newline at end of file diff --git a/Binary_Search/first_occurance.go b/Binary Search/first_occurance.go similarity index 100% rename from Binary_Search/first_occurance.go rename to Binary Search/first_occurance.go diff --git a/Binary Search/first_occurence.java b/Binary Search/first_occurence.java new file mode 100644 index 00000000..816fee33 --- /dev/null +++ b/Binary Search/first_occurence.java @@ -0,0 +1,56 @@ +/* Finding the index of the first occurence of a number in a sorted array: +Given a sorted array and a number x,find the first occurence of x in the +array. If the element is not found , return -1. + +Solve it in O(logN) complexity + +Example : + +INPUT : nums = [1,5,7,7,7,9,11,14], target = 7 +OUTPUT: 2 + +INPUT : nums = [1,2,4,6,45,55], target = 20 +OUTPUT: -1 + +Approach: + + # Approach will be similar to other binary search problems, but there is a need to find first occurence, + So ,if the element found is equal to target, make the end pointer point before of the mid value. + + # Once the condition violates and loop terminates ,check if the target value is equal to the start pointer value. + if it is equal, return the start pointer. if it is not return -1; + +*/ + + + +public class FirstOccurence{ + public static int firstOccurence(int[] arr,int target){ + int start = 0,end=arr.length-1; + while(start <= end){ + int mid = start + (end-start)/2; + if (arr[mid]== target){ + end = mid -1; + } + else if(arr[mid] > target){ + end = mid -1; + } + else if(arr[mid] < target){ + start = mid + 1; + } + } + if(start < arr.length && arr[start] == target){ + return start; + } + else{ + return -1; + } + } + + public static void main(String[] args){ + int[] arr = {1,5,7,7,7,9,11,14}; + int target = 7; + System.out.print(firstOccurence(arr,target)); + + } +} \ No newline at end of file diff --git a/Binary Search/first_true.cpp b/Binary Search/first_true.cpp new file mode 100644 index 00000000..6a42a624 --- /dev/null +++ b/Binary Search/first_true.cpp @@ -0,0 +1,44 @@ +/* + An array of boolean values is divided into two sections; the left section consists of all false and the + right section consists of all true. Find the First True in a Sorted Boolean Array of the + right section, i.e. the index of the first true element. If there is no true element, return -1. + + Input: arr = [false, false, true, true, true] + Output: 2 + + Explanation: first true's index is 2. +*/ +#include +#include + +int findBoundary(std::vector& arr) { + int low = 0; // Initialize the low pointer to the beginning of the vector. + int high = arr.size() - 1; // Initialize the high pointer to the end of the vector. + int bv = -1; // Initialize bv (boundary value) to -1. + + while (low <= high) { + int mid = low + (high - low) / 2; // Calculate the middle index. + + if (!arr[mid]) { + // If the element at the middle index is 'false', + // it means that the last 'true' value should be on the right side. + low = mid + 1; // Move the low pointer to the right of mid. + } else { + // If the element at the middle index is 'true', + // update bv to the current middle index and continue searching on the left side. + bv = mid; // Update bv to the current middle index. + high = mid - 1; // Move the high pointer to the left of mid. + } + } + + // The loop ends when low > high, indicating that the search is complete. + // bv contains the index of the last 'true' value encountered. + return bv; +} + +int main() { + std::vector arr = {false, false, false, true, true, true, true}; + int boundary = findBoundary(arr); + std::cout << "Boundary Index: " << boundary << std::endl; + return 0; +} diff --git a/Binary Search/first_true.go b/Binary Search/first_true.go new file mode 100644 index 00000000..f39c1e20 --- /dev/null +++ b/Binary Search/first_true.go @@ -0,0 +1,47 @@ +/* + An array of boolean values is divided into two sections; the left section consists of all false and the + right section consists of all true. Find the First True in a Sorted Boolean Array of the + right section, i.e. the index of the first true element. If there is no true element, return -1. + + Input: arr = [false, false, true, true, true] + Output: 2 + + Explanation: first true's index is 2. +*/ + +package main + +import ( + "fmt" +) + +func findBoundary(arr []bool) int { + low := 0 // Initialize the low pointer to the beginning of the slice. + high := len(arr) - 1 // Initialize the high pointer to the end of the slice. + bv := -1 // Initialize bv (boundary value) to -1. + + for low <= high { + mid := low + (high - low) / 2 // Calculate the middle index. + + if !arr[mid] { + // If the element at the middle index is 'false', + // it means that the last 'true' value should be on the right side. + low = mid + 1 // Move the low pointer to the right of mid. + } else { + // If the element at the middle index is 'true', + // update bv to the current middle index and continue searching on the left side. + bv = mid // Update bv to the current middle index. + high = mid - 1 // Move the high pointer to the left of mid. + } + } + + // The loop ends when low > high, indicating that the search is complete. + // bv contains the index of the last 'true' value encountered. + return bv +} + +func main() { + arr := []bool{false, false, false, true, true, true, true} + boundary := findBoundary(arr) + fmt.Println("Boundary Index:", boundary) +} diff --git a/Binary Search/first_true.java b/Binary Search/first_true.java new file mode 100644 index 00000000..0c3d3380 --- /dev/null +++ b/Binary Search/first_true.java @@ -0,0 +1,41 @@ +/* + An array of boolean values is divided into two sections; the left section consists of all false and the + right section consists of all true. Find the First True in a Sorted Boolean Array of the + right section, i.e. the index of the first true element. If there is no true element, return -1. + + Input: arr = [false, false, true, true, true] + Output: 2 + + Explanation: first true's index is 2. +*/ +public import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +class Solution { + public static int findBoundary(List arr) { + int low = 0; // Initialize the low pointer to the beginning of the list. + int high = arr.size() - 1; // Initialize the high pointer to the end of the list. + int bv = -1; // Initialize bv (boundary value) to -1. + + while (low <= high) { + int mid = low + (high - low) / 2; // Calculate the middle index. + + if (arr.get(mid) == false) { + // If the element at the middle index is 'false', + // it means that the last 'true' value should be on the right side. + low = mid + 1; // Move the low pointer to the right of mid. + } else { + // If the element at the middle index is 'true', + // update bv to the current middle index and continue searching on the left side. + bv = mid; // Update bv to the current middle index. + high = mid - 1; // Move the high pointer to the left of mid. + } + } + + // The loop ends when low > high, indicating that the search is complete. + // bv contains the index of the last 'true' value encountered. + return bv; + } +} diff --git a/Binary Search/first_true.js b/Binary Search/first_true.js new file mode 100644 index 00000000..5854e59a --- /dev/null +++ b/Binary Search/first_true.js @@ -0,0 +1,38 @@ +/* + An array of boolean values is divided into two sections; the left section consists of all false and the + right section consists of all true. Find the First True in a Sorted Boolean Array of the + right section, i.e. the index of the first true element. If there is no true element, return -1. + + Input: arr = [false, false, true, true, true] + Output: 2 + + Explanation: first true's index is 2. +*/ +function findBoundary(arr) { + let low = 0; // Initialize the low pointer to the beginning of the array. + let high = arr.length - 1; // Initialize the high pointer to the end of the array. + let bv = -1; // Initialize bv (boundary value) to -1. + + while (low <= high) { + let mid = low + Math.floor((high - low) / 2); // Calculate the middle index. + + if (!arr[mid]) { + // If the element at the middle index is 'false', + // it means that the last 'true' value should be on the right side. + low = mid + 1; // Move the low pointer to the right of mid. + } else { + // If the element at the middle index is 'true', + // update bv to the current middle index and continue searching on the left side. + bv = mid; // Update bv to the current middle index. + high = mid - 1; // Move the high pointer to the left of mid. + } + } + + // The loop ends when low > high, indicating that the search is complete. + // bv contains the index of the last 'true' value encountered. + return bv; +} + +const arr = [false, false, false, true, true, true, true]; +const boundary = findBoundary(arr); +console.log("Boundary Index:", boundary); diff --git a/Binary Search/first_true.py b/Binary Search/first_true.py new file mode 100644 index 00000000..edc140f9 --- /dev/null +++ b/Binary Search/first_true.py @@ -0,0 +1,35 @@ +''' + An array of boolean values is divided into two sections; the left section consists of all false and the + right section consists of all true. Find the First True in a Sorted Boolean Array of the + right section, i.e. the index of the first true element. If there is no true element, return -1. + + Input: arr = [false, false, true, true, true] + Output: 2 + + Explanation: first true's index is 2. +''' +def findBoundary(arr): + low = 0 # Initialize the low pointer to the beginning of the list. + high = len(arr) - 1 # Initialize the high pointer to the end of the list. + bv = -1 # Initialize bv (boundary value) to -1. + + while low <= high: + mid = low + (high - low) // 2 # Calculate the middle index. + + if not arr[mid]: + # If the element at the middle index is 'false', + # it means that the last 'true' value should be on the right side. + low = mid + 1 # Move the low pointer to the right of mid. + else: + # If the element at the middle index is 'true', + # update bv to the current middle index and continue searching on the left side. + bv = mid # Update bv to the current middle index. + high = mid - 1 # Move the high pointer to the left of mid. + + # The loop ends when low > high, indicating that the search is complete. + # bv contains the index of the last 'true' value encountered. + return bv + +arr = [False, False, False, True, True, True, True] +boundary = findBoundary(arr) +print("Boundary Index:", boundary) diff --git a/Binary_Search/CeilOfTarget.java b/Binary Search/floor_of_target.java similarity index 58% rename from Binary_Search/CeilOfTarget.java rename to Binary Search/floor_of_target.java index 41f67461..93df8e39 100644 --- a/Binary_Search/CeilOfTarget.java +++ b/Binary Search/floor_of_target.java @@ -1,27 +1,24 @@ -/* Ceiling of an element in a sorted array +/* Floor of an element in a sorted array You are given a sorted array nums and a target . - Find the index of the smallest element that is greater than or equal to target + Find the index of the greatest element that is less than or equal to target EXAMPLES: INPUT : nums = [2,4,5,7,9,11,18,25], target = 18 OUTPUT: 6 INPUT : nums = [2,4,5,7,9,11,18,25], target = 10 - OUTPUT: 5 + OUTPUT: 4 APPROACH : - We will implement this problem using BinarySearch since the array is sorted. + We will implement this problem using BinarySearch since the array is sorted and will be similar to ceil + but with slight modification */ + +public class FloorOfTarget{ - - - - -public class CeilOfTarget{ - - public static int search_ceil(int[] nums,int target){ + public static int search_floor(int[] nums,int target){ int start = 0,end = nums.length-1; while(start <=end){ int mid = start +(end-start)/2; @@ -35,11 +32,11 @@ else if(nums[mid] > target){ start = mid +1; } } - return start; // returns the nearest element to target if the target is not found + return end; // returns the nearest element to target if the target is not found } public static void main(String[] args){ int[] nums = {2,4,5,7,9,11,18,25}; - int target = 18; - System.out.print(search_ceil(nums,target)); + int target = 10; // output will be 4 in this case. + System.out.print(search_floor(nums,target)); } } diff --git a/Binary_Search/IndexPosition.java b/Binary Search/index_position.java similarity index 100% rename from Binary_Search/IndexPosition.java rename to Binary Search/index_position.java diff --git a/Binary Search/infinity_array.java b/Binary Search/infinity_array.java new file mode 100644 index 00000000..322b4b23 --- /dev/null +++ b/Binary Search/infinity_array.java @@ -0,0 +1,49 @@ +/*Binary Search in an Infinite array in Java + * Given an array whose size is not fixed,find the index of the target element in the array. + * EXAMPLE: [5,15,34,56,77,87...] (array can be of any size) + * Target = 56; + * Output: 3; + * + * APPROACH: + * The approach will be similar to normal binary search but in reverse order + * ie; For the start and end value, find the range which will be taking the target in the range. + * We will not specify the array length since the array size is infinity. + * Send the start and end value to the search function and return the position of the element. + * + */ + +public class InfinityArray { + public static void main(String[] args){ + int[] nums = {2,5,7,9,14,45,56,77,89,101};// can be of any size + int target = 56; + System.out.println(range(nums,target)); + } + + public static int range(int[] nums,int target){ + int start=0,end=1; + while(target > nums[end]){ + int temp = end +1; + end = end + (end-start)*2; + start = temp; + } + return search(nums,target,start,end); + } + public static int search(int[] nums, int target,int start,int end) { + while(start<=end){ + int mid = start + (end-start)/2; + // if middle value is equal to target return the mid index + if(nums[mid] == target){ + return mid; + } + // if mid value is greater than the target, search the left sub array + else if(nums[mid] > target){ + end = mid - 1; + } + // if mid value is lesser than the target, search the right sub array + else if(nums[mid] < target){ + start = mid + 1; + } + } + return -1; + } +} diff --git a/Binary_Search/last_occurance.go b/Binary Search/last_occurance.go similarity index 100% rename from Binary_Search/last_occurance.go rename to Binary Search/last_occurance.go diff --git a/Binary Search/median_of_two_sorted_arrays.cpp b/Binary Search/median_of_two_sorted_arrays.cpp new file mode 100644 index 00000000..00ff5919 --- /dev/null +++ b/Binary Search/median_of_two_sorted_arrays.cpp @@ -0,0 +1,89 @@ +/* +Approach: + let nums1 = [1, 3, 4, 7, 10, 12], nums2 = [2, 3, 6, 15] + + In order to find the median, we need a single sorted array. So a naive approach is that, just merge the 2 sorted arrays and find the median of that array. + This will have a time Complexity of O(n1 + n2), Space Complexity of O(n1 + n2) + + Now, lets optimise it. + So, the sorted form of the given array is arr = [1, 2, 3, 3, 4, 6, 7, 10, 12, 15]. To find the median of the array, we need to select the 2 mid elements and average it out. + If we observe the sorted array carefully, then we notice that the 2 middle elements are arr[4] = 4 and arr[5] = 6, => arr[4] <= arr[5]. + + Thought process: + Now, since the arrays are sorted, so the binary searh may be able to solve the problem. + Observation 1: If we partition the sorted array arr into 2 halves, then for sure we know that there would be 5 elements on left half and 5 elements on right half. + Now, we can select 5 elements for right half from nums1 and nums2 combinedly and similarly the rest of the elements for the left half. + Example: + 1. left => [1, 3, 4, 7, 2], right => [10, 12, 3, 6, 15] + 2. left => [1, 3, 4, 2, 3], right => [7, 10, 12, 6, 15] + 3. left => [1, 3, 2, 3, 6], right => [4, 7, 10, 12, 15] + + Observation 2: All the elements on left half is lesser than all the elements on the right half. + Now, according to the observation, I have to check that all the elements in the left <= all the elements in the right. This can be done by just comparing the maximum of left half <= minimum of right half. + + Hence, the problem boils down to a searching problem; but how to identify the binary search approach?? + Suppose, we partition the element as example 1, then the max of left[] = 7 and min of right[] = 3, but according to the 2nd observation, it is not valid. So, in order to have a correct max in left, I need to reduce its value and consequestly, the min of right[] should be increased. That means, I have to move leftwards in nums1 to have a correct max value in left half. +*/ + +// Leetcode link: https://leetcode.com/problems/median-of-two-sorted-arrays/ + +#include + +class Solution { +public: + double findMedianSortedArrays(std::vector& nums1, std::vector& nums2) { + int n1 = nums1.size(); // stores the length of nums1 array + int n2 = nums2.size(); // stores the length of nums2 array + + if(n2 > n1) return findMedianSortedArrays(nums2, nums1); + + // according to approach described above, I am applying binary search on nums1 array + int lo = 0, hi = n1; + while(lo <= hi){ + int mid1 = (lo+hi)/2; // mid of nums1 + int mid2 = (n1 + n2 + 1)/2 - mid1; + + std::pair maxleft, minright; + maxleft.first = mid1 == 0 ? INT_MIN : nums1[mid1-1]; + maxleft.second = mid2 == 0 ? INT_MIN : nums2[mid2-1]; + + minright.first = mid1 == n1 ? INT_MAX : nums1[mid1]; + minright.second = mid2 == n2 ? INT_MAX : nums2[mid2]; + + if(maxleft.first <= minright.second and maxleft.second <= minright.first){ + if((n1+n2)%2 == 1){ + return std::max(maxleft.first, maxleft.second); + } else { + return (std::max(maxleft.first, maxleft.second) + std::min(minright.first, minright.second))/2.0; + } + } else if (maxleft.first > minright.second){ + hi = mid1-1; + } else { + lo = mid1+1; + } + } + } +}; + +int main(int argc, char const *argv[]) +{ + int n; + + std::cin>>n; + std::vector arr1(n, 0); + for(int i=0;i>arr1[i]; + } + + std::cin>>n; + std::vector arr2(n, 0); + for(int i=0;i>arr2[i]; + } + + Solution sol = Solution(); + double res = sol.findMedianSortedArrays(arr1, arr2); + + std::cout< a - b); + const n = merged.length; + if (n % 2 === 0) { + const middle = n / 2; + return (merged[middle - 1] + merged[middle]) / 2; + } else { + const middle = Math.floor(n / 2); + return merged[middle]; + } +} + +// Approach 2: Binary Search +function findMedianSortedArrays(arr1, arr2) { + // If arr1 is longer than arr2, swap them to ensure arr1 is shorter + if (arr1.length > arr2.length) { + [arr1, arr2] = [arr2, arr1]; + } + + const m = arr1.length; + const n = arr2.length; + let left = 0; + let right = m; + + while (left <= right) { + // Partition arr1 and arr2 + const partition1 = Math.floor((left + right) / 2); + const partition2 = Math.floor((m + n + 1) / 2) - partition1; + + // Calculate the max and min elements of the left and right partitions + const maxLeft1 = partition1 === 0 ? -Infinity : arr1[partition1 - 1]; + const minRight1 = partition1 === m ? Infinity : arr1[partition1]; + const maxLeft2 = partition2 === 0 ? -Infinity : arr2[partition2 - 1]; + const minRight2 = partition2 === n ? Infinity : arr2[partition2]; + + // If the partitions are correctly balanced, return the median + if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) { + if ((m + n) % 2 === 0) { + return ( + (Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2 + ); + } else { + return Math.max(maxLeft1, maxLeft2); + } + } else if (maxLeft1 > minRight2) { + // If maxLeft1 is too big, move partition1 to the left + right = partition1 - 1; + } else { + // If maxLeft2 is too big, move partition1 to the right + left = partition1 + 1; + } + } +} diff --git a/Binary Search/minimum_in_rotated_sorted_array.cpp b/Binary Search/minimum_in_rotated_sorted_array.cpp new file mode 100644 index 00000000..3d68af73 --- /dev/null +++ b/Binary Search/minimum_in_rotated_sorted_array.cpp @@ -0,0 +1,55 @@ +/* + Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become: + + [4,5,6,7,0,1,2] if it was rotated 4 times. + [0,1,2,4,5,6,7] if it was rotated 7 times. + Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]]. + + Given the sorted rotated array nums of unique elements, return the minimum element of this array. + + You must write an algorithm that runs in O(log n) time. + + Example 1: + Input: nums = [3,4,5,1,2] + Output: 1 + Explanation: The original array was [1,2,3,4,5] rotated 3 times. + + Example 2: + Input: nums = [4,5,6,7,0,1,2] + Output: 0 + Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times. + + Example 3: + Input: nums = [11,13,15,17] + Output: 11 + Explanation: The original array was [11,13,15,17] and it was rotated 4 times. + + Constraints: + n == nums.length + 1 <= n <= 5000 + -5000 <= nums[i] <= 5000 + All the integers of nums are unique. + nums is sorted and rotated between 1 and n times. +*/ + +class Solution { +public: + int findMin(vector& nums) { + int n = nums.size(); + int start = 0, end = n - 1; + while(start <= end){ + int mid = start + (end - start) / 2; + int next = (mid + 1) % n; + int prev = (mid - 1 + n) % n; + if(nums[mid] <= nums[prev] && nums[mid] <= nums[next]) + return nums[mid]; + if(nums[mid] <= nums[end]){ + end = mid - 1; + } + else if(nums[mid] >= nums[start]){ + start = mid + 1; + } + } + return -1; + } +}; \ No newline at end of file diff --git a/Binary_Search/PerfectSquare.java b/Binary Search/perfect_square.java similarity index 100% rename from Binary_Search/PerfectSquare.java rename to Binary Search/perfect_square.java diff --git a/Binary Search/search_in_rotated_sorted_array.cpp b/Binary Search/search_in_rotated_sorted_array.cpp new file mode 100644 index 00000000..b1c083f0 --- /dev/null +++ b/Binary Search/search_in_rotated_sorted_array.cpp @@ -0,0 +1,88 @@ +/* + Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums. + You must write an algorithm with O(log n) runtime complexity. + + Input: nums = [4,5,6,7,0,1,2], target = 0 + Output: 4 + + Input: nums = [4,5,6,7,0,1,2], target = 3 + Output: -1 + + Input: nums = [1], target = 0 + Output: -1 + + Constraints: + > 1 <= nums.length <= 5000 + > -104 <= nums[i] <= 104 + > All values of nums are unique. + > nums is an ascending array that is possibly rotated. + > -104 <= target <= 104 + + APPROACH + 1. Find the index at which the array has been rotated with the help of Binary Search. + 2. Store the index of rotation as INDX and find the range where target might be found using the following comparision: + RANGE = {INDX+1, HIGH} if TARGET < NUMS[LOW] + RANGE = {LOW, INDX-1} if TARGET > NUMS{HIGH] + 3. Perform Binary Search for TARGET in the required range. + 4. If target is not found return -1. + + TIME COMPLEXITY : O(NlogN) SPACE COMPLEXITY: O(1) +*/ +class Solution { +public: + int search(vector& nums, int target) { + // Initialize Variable for later Usage + int n=nums.size(); + int low=0; + int high=n-1; + + //Find the Rotation Point in the Rotated Array + while(low<=high){ + int mid=(low+high)/2; + if(mid==0 || mid==n-1){ + low=mid; + break; + } + if(nums[mid-1]>nums[mid] && nums[mid+1]>nums[mid]){ + low=mid; + break; + } + else if(nums[mid]>nums[low] && nums[mid]>nums[high]){ + low=mid+1; + } + else{ + high=mid-1; + } + } + + // Re-initialize Variables Needed + int indx=low; + low=0, high=n-1; + if(target==nums[indx]){ + return indx; + } + else if(target>nums[high]){ + high=indx-1; + } + else if(targetnums[mid]){ + low=mid+1; + } + else{ + high=mid-1; + } + } + + // If target not found return -1 + return -1; + } +}; \ No newline at end of file diff --git a/Binary Search/search_in_sorted_rotated_array.cpp b/Binary Search/search_in_sorted_rotated_array.cpp new file mode 100644 index 00000000..3b1563ec --- /dev/null +++ b/Binary Search/search_in_sorted_rotated_array.cpp @@ -0,0 +1,60 @@ +/* + There is an integer array nums sorted in ascending order (with distinct values). + + Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. + + Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums. + + You must write an algorithm with O(log n) runtime complexity. + + Example 1: + Input: nums = [4,5,6,7,0,1,2], target = 0 + Output: 4 + + Example 2: + Input: nums = [4,5,6,7,0,1,2], target = 3 + Output: -1 + + Example 3: + Input: nums = [1], target = 0 + Output: -1 + + Constraints: + 1 <= nums.length <= 5000 + -104 <= nums[i] <= 104 + All values of nums are unique. + nums is an ascending array that is possibly rotated. + -104 <= target <= 104 + +*/ + +class Solution { +public: + int search(vector& nums, int target) { + int start = 0, end = nums.size() - 1; + int ans = -1; + while(start <= end){ + int mid = start + (end - start) / 2; + if(target == nums[mid]){ + return mid; + } + if(nums[start] <= nums[mid]){ + if(target >= nums[start] && target <= nums[mid]){ + end = mid - 1; + } + else{ + start = mid + 1; + } + } + else{ + if(target >= nums[mid] && target <= nums[end]){ + start = mid + 1; + } + else { + end = mid - 1; + } + } + } + return ans; + } +}; \ No newline at end of file diff --git a/Binary Search/search_in_sorted_rotated_array.go b/Binary Search/search_in_sorted_rotated_array.go new file mode 100644 index 00000000..09fedaf0 --- /dev/null +++ b/Binary Search/search_in_sorted_rotated_array.go @@ -0,0 +1,74 @@ +/* + The code snippet represents a function `SearchInSortedMatrix` that searches for a target value in a sorted matrix and returns the position (row and column) of the target if found, or [-1, -1] if not found. + + Here's how the code works: + + 1. Initialize the starting position at the top-right corner of the matrix, i.e., `row = 0` and `col = len(matrix[0]) - 1`. + + 2. Enter a loop that continues as long as the current position is within the matrix bounds, i.e., `row < len(matrix)` and `col >= 0`. + + 3. Inside the loop, compare the value at the current position (`matrix[row][col]`) with the target value: + - If the value is greater than the target, it means the target value must be in a lower column, so decrement the column + index (`col--`). + - If the value is less than the target, it means the target value must be in a higher row, so increment the row index + (`row++`). + - If the value is equal to the target, it means the target value is found. Return the position as [row, col]. + + 4. If the loop completes without finding the target value, it means the target is not present in the matrix. Return [-1, -1] + to indicate that the target was not found. + + The code takes advantage of the sorted nature of the matrix to perform an efficient search. By starting at the top-right + corner and comparing the current value with the target, it eliminates rows and columns in each iteration, narrowing down the search space until the target is found or the entire matrix is traversed. + + Overall, the code has a time complexity of O(N + M), where N is the number of rows in the matrix and M is the number of + columns, as it performs a linear search through the matrix. + + Time complexity: O(n + m) time where n is the length of the matrix's rows and m is the length of the matrix's columns + Space complexity: O(1) +*/ +package main + +import "fmt" + +func SearchInSortedMatrix(matrix [][]int, target int) []int { + // Initialize the starting position at the top-right corner of the matrix + row, col := 0, len(matrix[0])-1 + + // Continue the loop as long as the current position is within the matrix bounds + for row < len(matrix) && col >= 0 { + // Compare the value at the current position with the target value + if matrix[row][col] > target { + // If the value is greater than the target, move to the left column + col-- + } else if matrix[row][col] < target { + // If the value is less than the target, move to the next row + row++ + } else { + // If the value is equal to the target, return the position [row, col] + return []int{row, col} + } + } + + // If the loop completes without finding the target, return [-1, -1] + return []int{-1, -1} +} + + +func main() { + matrix := [][]int{ + {1, 4, 7, 12, 15, 1000}, + {2, 5, 19, 31, 32, 1001}, + {3, 8, 24, 33, 35, 1002}, + {40, 41, 42, 44, 45, 1003}, + {99, 100, 103, 106, 128, 1004}, + } + + target := 33 + + result := SearchInSortedMatrix(matrix, target) + if result[0] == -1 && result[1] == -1 { + fmt.Println("Target not found in the matrix") + } else { + fmt.Println("Target found at position:", result) + } +} \ No newline at end of file diff --git a/Binary Search/search_in_sorted_rotated_array.java b/Binary Search/search_in_sorted_rotated_array.java new file mode 100644 index 00000000..d84fbe0b --- /dev/null +++ b/Binary Search/search_in_sorted_rotated_array.java @@ -0,0 +1,98 @@ +/*There is an integer array nums sorted in ascending order (with distinct values). + +Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. + +Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums. + +You must write an algorithm with O(log n) runtime complexity. + + + +Example 1: + +Input: nums = [4,5,6,7,0,1,2], target = 0 +Output: 4 +Example 2: + +Input: nums = [4,5,6,7,0,1,2], target = 3 +Output: -1 +Example 3: + +Input: nums = [1], target = 0 +Output: -1 + */ + + +public class searchInRotatedArray { + + //Brute force solution + + public static int searchTarget(int nums[] , int target) + { + int result = -1; + for(int i=0 ; i=target) + { + end = mid-1; + } + + else + { + start =mid+1; + } + } + + + else + { + if(nums[mid]<=target && targethigh): + return -1 + mid = (low+high)//2 + if(nums[mid]target): + return Solution.binarySearch(nums,low,mid-1,target) + else: + return mid + + def search(self, nums: List[int], target: int) -> int: + #Finding the index where the array was rotated and binary searching the first and second parts for the target value. + rotated_index = -1 + for i in range(len(nums)-1): + if(nums[i]>nums[i+1]): + rotated_index = i + break + + return Solution.binarySearch(nums,0,rotated_index,target) & Solution.binarySearch(nums,rotated_index+1,len(nums)-1,target) + \ No newline at end of file diff --git a/Binary Search/search_insert_position.js b/Binary Search/search_insert_position.js new file mode 100644 index 00000000..da532a62 --- /dev/null +++ b/Binary Search/search_insert_position.js @@ -0,0 +1,69 @@ +// https://leetcode.com/problems/search-insert-position/description/ + +/* + +Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. + +You must write an algorithm with O(log n) runtime complexity. + +Example 1: + +Input: nums = [1,3,5,6], target = 5 +Output: 2 +Example 2: + +Input: nums = [1,3,5,6], target = 2 +Output: 1 +Example 3: + +Input: nums = [1,3,5,6], target = 7 +Output: 4 + +Explanation: + In a given sorted array this code uses binary search where we divide the array into two parts and check if the number is present then we return the index of element otherwise we return what should be index of that target in the given array. + The loops iterates until the start index and end index of array is same. + We calculate the middle index on each iteration of loop as start index / end index might change on the basis of last iteration result. + If the middle index element is the target element then we return middle index otherwise we loop through the entire array until last one or two elements left. + On the basis of last one or two elements we decide what is going to be the index of target in the array. +*/ + +function findSearchPosition(nums, target) { + let startIndex = 0; + let endIndex = nums.length - 1; + + while (startIndex <= endIndex) { + const midIndex = startIndex + Math.floor((endIndex - startIndex) / 2); + + // Base case if middle index element is our target then we will return the middle index + if (nums[midIndex] === target) { + return midIndex; + } + + // In case if element is not present in the given array then in the final iteration when only last one or two elements are remaining we will check what should be our target number index in the given array + if (startIndex === endIndex || startIndex + 1 === endIndex) { + // If element at start index is greater then target then target then start index will be assigned to target + if (nums[startIndex] > target) { + return startIndex; + // if target is greater then end index element then end index + 1 will be assigned to target + } else if (nums[endIndex] < target) { + return endIndex + 1; + } else { + return startIndex + 1; + } + } + + // if the target element is greater then the middle index element then we will change the start index next to middle index otherwise we will change last index to middle index - 1 + if (nums[midIndex] < target) { + startIndex = midIndex + 1; + } else { + endIndex = midIndex - 1; + } + } +} + +//driver code +var nums = [1, 3, 5, 6]; +console.log(findSearchPosition(nums, 8)); + +//Input: nums = [1,3,5,6], target = 8 +// Output: 4 diff --git a/Binary_Search/SquareRoot.java b/Binary Search/square_root.java similarity index 100% rename from Binary_Search/SquareRoot.java rename to Binary Search/square_root.java diff --git a/Bit Manipulation/bloom_filter.cpp b/Bit Manipulation/bloom_filter.cpp new file mode 100644 index 00000000..31bba2ef --- /dev/null +++ b/Bit Manipulation/bloom_filter.cpp @@ -0,0 +1,135 @@ +// Bloom Filter Implementation +// Question: How does the Bloom filter work, and how is it implemented in the C++ + +// Approach and Explanation + +// 1.Hash Functions: The Bloom filter uses four hash functions (`h1`, `h2`, `h3`, `h4`) to map input strings to positions in the bit array. These hash functions generate different indexes based on the input string and the size of the bit array. + +// 2. Bit Array: The Bloom filter uses a `std::bitset<1000000>` called `bitarray` to represent the filter. It is a fixed-size bit array where each bit represents a position that can be set (1) or unset (0). + +// 3. Lookup Operation: The `lookup` function takes a reference to the `bitarray` and a string `s` as input. It calculates the hash values for `s` using the four hash functions and checks if the corresponding positions in the `bitarray` are all set (1). If all positions are set, it returns `true`, indicating that the element is possibly present in the filter. Otherwise, it returns `false`. + +// 4. Insert Operation: The `insert` function takes a reference to the `bitarray` and a string `s` as input. It first checks if the element is already present in the filter by calling the `lookup` function. If the element is already present, it prints a message indicating that it is probably already present. Otherwise, it calculates the hash values for `s` using the four hash functions and sets the corresponding positions in the `bitarray` to 1, indicating the insertion of the element. + +// 5. Main Function: In the `main` function, a `bitarray` of size 1000000 is created. A set of strings (`sarray`) is defined, representing the elements to be added to the filter. For each string `s` in `sarray`, it checks if `s` is already present in the filter using the `lookup` function. If it is present, it prints a message indicating that it is already present. Otherwise, it inserts `s` into the filter using the `insert` function. + +// The Bloom filter provides a probabilistic way to test whether an element is possibly in a set. It may return false positives, indicating that an element is possibly in the set when it's not, but it will never give false negatives. The accuracy of the filter depends on the size of the bit array, the number of hash functions used, and the number of elements inserted. + +// In this implementation, the size of the bit array is fixed at 1000000, and four hash functions are used. The elements are inserted by setting the corresponding positions in the bit array to 1. The `lookup` function checks if all the positions for an element are set (1), indicating its possible presence in the filter. + +// Time Complexity-> O(1) on average, O(n) in the worst case +// Space Complexity-> O(n) + +#include +#include +#include +#define ll long long + +// Hash function 1 +// This function calculates the hash value of a string using a simple additive approach. +int h1(std::string s, int arrSize) +{ + ll int hash = 0; + for (int i = 0; i < s.size(); i++) + { + hash = (hash + ((int)s[i])); // Add the ASCII value of each character to the hash + hash = hash % arrSize; // Take the modulus to ensure the hash value is within the array size + } + return hash; +} + +// Hash function 2 +// This function calculates the hash value of a string using an exponential approach. +int h2(std::string s, int arrSize) +{ + ll int hash = 1; + for (int i = 0; i < s.size(); i++) + { + hash = hash + pow(19, i) * s[i]; // Multiply each character by a power of 19 and add to the hash + hash = hash % arrSize; // Take the modulus to ensure the hash value is within the array size + } + return hash % arrSize; +} + +// Hash function 3 +// This function calculates the hash value of a string using a polynomial approach. +int h3(std::string s, int arrSize) +{ + ll int hash = 7; + for (int i = 0; i < s.size(); i++) + { + hash = (hash * 31 + s[i]) % arrSize; // Multiply the hash by 31, add the character, and take the modulus + } + return hash % arrSize; +} + +// Hash function 4 +// This function calculates the hash value of a string using a combination of multiplication and exponentiation. +int h4(std::string s, int arrSize) +{ + ll int hash = 3; + int p = 7; + for (int i = 0; i < s.size(); i++) + { + hash += hash * 7 + s[i] * pow(p, i); // Multiply the hash by 7, add the character multiplied by a power of 7 + hash = hash % arrSize; // Take the modulus to ensure the hash value is within the array size + } + return hash; +} + +// Lookup operation +// This function checks if a given string is present in the bit array. +bool lookup(std::bitset<1000000> &bitarray, std::string s) +{ + int a = h1(s, bitarray.size()); // Calculate hash using h1 + int b = h2(s, bitarray.size()); // Calculate hash using h2 + int c = h3(s, bitarray.size()); // Calculate hash using h3 + int d = h4(s, bitarray.size()); // Calculate hash using h4 + + return bitarray[a] && bitarray[b] && bitarray[c] && bitarray[d]; // Check if all corresponding bits are set in the bit array +} + +// Insert operation +// This function inserts a string into the bit array. +void insert(std::bitset<1000000> &bitarray, std::string s) +{ + // Check if the element is already present or not + if (lookup(bitarray, s)) + { + std::cout << s << " is probably already present" << std::endl; + } + else + { + int a = h1(s, bitarray.size()); // Calculate hash using h1 + int b = h2(s, bitarray.size()); // Calculate hash using h2 + int c = h3(s, bitarray.size()); // Calculate hash using h3 + int d = h4(s, bitarray.size()); // Calculate hash using h4 + + bitarray[a] = true; // Set corresponding bit in the bit array for hash a + bitarray[b] = true; // Set corresponding bit in the bit array for hash b + bitarray[c] = true; // Set corresponding bit in the bit array for hash c + bitarray[d] = true; // Set corresponding bit in the bit array for hash d + + std::cout << s << " inserted" << std::endl; + } +} + +int main() +{ + std::bitset<1000000> bitarray; + std::string sarray[] = {"apple", "banana", "cherry", "orange", "grape", "kiwi"}; + + for (const auto &s : sarray) + { + if (lookup(bitarray, s)) + { + std::cout << s << " is already present" << std::endl; + } + else + { + insert(bitarray, s); + } + } + + return 0; +} diff --git a/Bit Manipulation/bloom_filter.py b/Bit Manipulation/bloom_filter.py new file mode 100644 index 00000000..816193da --- /dev/null +++ b/Bit Manipulation/bloom_filter.py @@ -0,0 +1,81 @@ +# Bloom Filter Implementation +# Question: How can I implement a Bloom filter in Python? + +# explanation of the approach: + +# 1. The `BloomFilter` class is defined to encapsulate the functionality of the Bloom filter. It takes two parameters during initialization: `capacity` (expected number of elements to be added to the filter) and `false_positive_rate` (desired false positive rate). + +# 2. The `calculate_size()` method is used to calculate the size of the bit array based on the capacity and false positive rate. It uses the formula `- (capacity * log(false_positive_rate)) / (log(2) ** 2)` to determine the size. + +# 3. The `calculate_num_hashes()` method calculates the number of hash functions to be used based on the size of the bit array and the capacity. It uses the formula `(size / capacity) * log(2)` to determine the number of hashes. + +# 4. The `bit_array` attribute is created using the `bitarray` library and initialized with the calculated size. All bits in the array are initially set to 0. + +# 5. The `add()` method is used to add an item to the Bloom filter. It iterates over the range of `num_hashes` and calculates the index for each hash function using the `mmh3.hash()` function. The index is obtained by taking the modulo of the hash value with the size of the bit array. The corresponding bit in the bit array is set to 1. + +# 6. The `contains()` method is used to check if an item is present in the Bloom filter. It follows a similar process as the `add()` method, iterating over the range of `num_hashes` and calculating the index for each hash function. If any of the corresponding bits in the bit array are 0, it indicates that the item is not present and False is returned. If all the bits are 1, it means the item might be present (false positive) and True is returned. + +# 7. In the example usage, a `BloomFilter` object is created with a capacity of 1000 and a false positive rate of 0.01. + +# 8. Three items ("apple", "banana", "cherry") are added to the Bloom filter using the `add()` method. + +# 9. The `contains()` method is used to check if certain items ("apple", "banana", "cherry", "orange") are present in the Bloom filter. The result is printed for each item. + +# The Bloom filter uses the MurmurHash3 hash function (`mmh3.hash()`) to generate hash values for the items. The number of hash functions and the size of the bit array are calculated based on the desired false positive rate and capacity. The Bloom filter provides a probabilistic check for membership, with a possibility of false positives but no false negatives. + + +import math +import mmh3 +from bitarray import bitarray + + +class BloomFilter: + def __init__(self, capacity, false_positive_rate): + self.capacity = capacity + self.false_positive_rate = false_positive_rate + self.size = self.calculate_size() + self.num_hashes = self.calculate_num_hashes() + self.bit_array = bitarray(self.size) + self.bit_array.setall(0) + + def calculate_size(self): + # Calculate the size of the bit array based on the capacity and false positive rate + size = - (self.capacity * math.log(self.false_positive_rate) + ) / (math.log(2) ** 2) + return int(size) + + def calculate_num_hashes(self): + # Calculate the number of hash functions based on the size and capacity + num_hashes = (self.size / self.capacity) * math.log(2) + return int(num_hashes) + + def add(self, item): + # Add an item to the Bloom filter + for seed in range(self.num_hashes): + # Generate a hash value using the MurmurHash3 algorithm with different seeds + index = mmh3.hash(item, seed) % self.size + self.bit_array[index] = 1 + + def contains(self, item): + # Check if an item is possibly in the Bloom filter + for seed in range(self.num_hashes): + # Generate a hash value using the MurmurHash3 algorithm with different seeds + index = mmh3.hash(item, seed) % self.size + if self.bit_array[index] == 0: + return False + return True + + +# Create a Bloom filter with a capacity of 1000 items and a false positive rate of 0.01 +bloom_filter = BloomFilter(capacity=1000, false_positive_rate=0.01) + +# Add items to the filter +bloom_filter.add("apple") +bloom_filter.add("banana") +bloom_filter.add("cherry") + +# Check if items are in the filter +print(bloom_filter.contains("apple")) # Expected output: True +print(bloom_filter.contains("banana")) # Expected output: True +print(bloom_filter.contains("cherry")) # Expected output: True +print(bloom_filter.contains("orange")) # Expected output: False diff --git a/Bit_Manipulation/count_bits.go b/Bit Manipulation/count_bits.go similarity index 100% rename from Bit_Manipulation/count_bits.go rename to Bit Manipulation/count_bits.go diff --git a/Bit Manipulation/interesting_array.java b/Bit Manipulation/interesting_array.java new file mode 100644 index 00000000..52d9cbf0 --- /dev/null +++ b/Bit Manipulation/interesting_array.java @@ -0,0 +1,26 @@ +package BitManipulation; + +import com.google.common.base.Stopwatch; + +public class InterestingArray { + public static void main(String[] args) { + Stopwatch timer = Stopwatch.createStarted(); + + int[] array = {9, 14, 27, 81, 197, 0, 1}; + String ans = solve(array); + System.out.println(ans); + + System.out.println("Runtime " + timer); + } + + public static String solve(int[] array) { + // O(N) time | O(1) space + int oddCount = 0; + + for (var num : array) + if (num % 2 != 0) + oddCount += 1; + + return oddCount % 2 != 0 ? "No" : "Yes"; + } +} diff --git a/Bit Manipulation/mod_array.java b/Bit Manipulation/mod_array.java new file mode 100644 index 00000000..6321cd0c --- /dev/null +++ b/Bit Manipulation/mod_array.java @@ -0,0 +1,120 @@ +/** + * You are given a large number in the form of a array A of size N where each element denotes a digit of the number. + * You are also given a number B. You have to find out the value of A % B and return it. + * + * + * + * Problem Constraints + * 1 <= N <= 105 + * 0 <= Ai <= 9 + * 1 <= B <= 109 + * + * + * Input Format + * The first argument is an integer array A. + * The second argument is an integer B. + * + * + * Output Format + * Return a single integer denoting the value of A % B. + * + * + * Example Input + * Input 1: + * A = [1, 4, 3] + * B = 2 + * Input 2: + * + * A = [4, 3, 5, 3, 5, 3, 2, 1] + * B = 47 + * + * + * Example Output + * Output 1: + * 1 + * Output 2: + * + * 20 + * + * + * Example Explanation + * Explanation 1: + * 143 is an odd number so 143 % 2 = 1. + * Explanation 2: + * + * 43535321 % 47 = 20 + */ + +/** + * You are given a large number in the form of a array A of size N where each element denotes a digit of the number. + * You are also given a number B. You have to find out the value of A % B and return it. + * + * + * + * Problem Constraints + * 1 <= N <= 105 + * 0 <= Ai <= 9 + * 1 <= B <= 109 + * + * + * Input Format + * The first argument is an integer array A. + * The second argument is an integer B. + * + * + * Output Format + * Return a single integer denoting the value of A % B. + * + * + * Example Input + * Input 1: + * A = [1, 4, 3] + * B = 2 + * Input 2: + * + * A = [4, 3, 5, 3, 5, 3, 2, 1] + * B = 47 + * + * + * Example Output + * Output 1: + * 1 + * Output 2: + * + * 20 + * + * + * Example Explanation + * Explanation 1: + * 143 is an odd number so 143 % 2 = 1. + * Explanation 2: + * + * 43535321 % 47 = 20 + */ + + +package ModularArithmetic; + +public class ModArray { + public static void main(String[] args) { + int[] array = {4, 3, 5, 3, 5, 3, 2, 1}; + int divisor = 47; + int ans = solve(array, divisor); + System.out.println(ans); + } + public static int solve(int[] array, int divisor) { + // O(N) time | O(1) space + long res = 0; + int len = array.length - 1; + long POWER_10 = 1; + long MOD = divisor; + + for (int i = len; i > -1; i--) { + long currentDigit = array[i]; + long currentValue = (currentDigit * POWER_10) % MOD; + res = (res + currentValue) % MOD; + POWER_10 = (POWER_10 * 10) % MOD; + } + return (int) res; + } +} diff --git a/Bit Manipulation/number_of_1_bits.java b/Bit Manipulation/number_of_1_bits.java new file mode 100644 index 00000000..eb292191 --- /dev/null +++ b/Bit Manipulation/number_of_1_bits.java @@ -0,0 +1,56 @@ +/** + * Write a function that takes an integer and returns the number of 1 bits it has. + * + * + * Problem Constraints + * 1 <= A <= 109 + * + * + * Input Format + * First and only argument contains integer A + * + * + * Output Format + * Return an integer as the answer + * + * + * Example Input + * Input 1: + * 11 + * Input 2: + * 6 + * + * + * Example Output + * Output 1: + * 3 + * Output 2: + * 2 + * + * + * Example Explanation + * Explaination 1: + * 11 is represented as 1011 in binary. + * Explaination 2: + * 6 is represented as 110 in binary. + */ + +package BitManipulation; + +public class NumberOf1Bits { + public static void main(String[] args) { + int num = 11; + int ans = solve(num); + System.out.println(ans); + } + public static int solve(int num) { + // O(Log(N)) tine | O(1) space + int currentNum = num, count = 0; + + while (currentNum != 0) { + if ( (currentNum & 1) == 1) count++; + currentNum = currentNum >> 1; + } + return count; + } +} diff --git a/Bit_Manipulation/parity_of_a_word.go b/Bit Manipulation/parity_of_a_word.go similarity index 100% rename from Bit_Manipulation/parity_of_a_word.go rename to Bit Manipulation/parity_of_a_word.go diff --git a/Bit Manipulation/power_of_2.cpp b/Bit Manipulation/power_of_2.cpp new file mode 100644 index 00000000..0b9572e3 --- /dev/null +++ b/Bit Manipulation/power_of_2.cpp @@ -0,0 +1,34 @@ +/* + Given an integer n, return true if it is a power of two. Otherwise, return false. + + An integer n is a power of two, if there exists an integer x such that n == 2x. + + Example 1: + Input: n = 1 + Output: true + Explanation: 20 = 1 + + Example 2: + Input: n = 16 + Output: true + Explanation: 24 = 16 + + Example 3: + Input: n = 3 + Output: false + + Constraints: + + -231 <= n <= 231 - 1 + + Follow up: Could you solve it without loops/recursion? + +*/ + + +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && !(n &(n - 1)); + } +}; \ No newline at end of file diff --git a/Bit Manipulation/reduce_to_zero.cpp b/Bit Manipulation/reduce_to_zero.cpp new file mode 100644 index 00000000..fb0a8674 --- /dev/null +++ b/Bit Manipulation/reduce_to_zero.cpp @@ -0,0 +1,55 @@ +/* +Given an integer num, return the number of steps to reduce it to zero. + +In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. + + + +Example 1: + +Input: num = 14 +Output: 6 +Explanation: +Step 1) 14 is even; divide by 2 and obtain 7. +Step 2) 7 is odd; subtract 1 and obtain 6. +Step 3) 6 is even; divide by 2 and obtain 3. +Step 4) 3 is odd; subtract 1 and obtain 2. +Step 5) 2 is even; divide by 2 and obtain 1. +Step 6) 1 is odd; subtract 1 and obtain 0. +Example 2: + +Input: num = 8 +Output: 4 +Explanation: +Step 1) 8 is even; divide by 2 and obtain 4. +Step 2) 4 is even; divide by 2 and obtain 2. +Step 3) 2 is even; divide by 2 and obtain 1. +Step 4) 1 is odd; subtract 1 and obtain 0. +Example 3: + +Input: num = 123 +Output: 12 + + +Constraints: + +0 <= num <= 106 +*/ + +#include +class Solution { +public: + int numberOfSteps (int num) { + int count = 0; + while(num){ + if(!(num & 1)){ + num = num >> 1; + } + else{ + num--; + } + count++; + } + return count; + } +}; \ No newline at end of file diff --git a/Bit_Manipulation/setbits.cpp b/Bit Manipulation/setbits.cpp similarity index 96% rename from Bit_Manipulation/setbits.cpp rename to Bit Manipulation/setbits.cpp index edc23c8a..5b7ed619 100644 --- a/Bit_Manipulation/setbits.cpp +++ b/Bit Manipulation/setbits.cpp @@ -1,18 +1,18 @@ -// Program to count the number of bits that are set to 1 in an integer -// The following program tests bits one at a time starting with the least-significant bit. -// Since we perform O(1) computation per bit, the time complexity is O(n) where n is number of bits in the integer -// Best case time complexity is O(1), if the input io 0 -#include -using namespace std; -int find_set_bits(int n){ - int set_bits = 0; - while(n){ - set_bits += (n & 1); - n >>= 1; - } - return set_bits; -} -int main(){ - cout << find_set_bits(4) << endl; - return 0; -} +// Program to count the number of bits that are set to 1 in an integer +// The following program tests bits one at a time starting with the least-significant bit. +// Since we perform O(1) computation per bit, the time complexity is O(n) where n is number of bits in the integer +// Best case time complexity is O(1), if the input io 0 +#include +using namespace std; +int find_set_bits(int n){ + int set_bits = 0; + while(n){ + set_bits += (n & 1); + n >>= 1; + } + return set_bits; +} +int main(){ + cout << find_set_bits(4) << endl; + return 0; +} diff --git a/Bit Manipulation/single_number.java b/Bit Manipulation/single_number.java new file mode 100644 index 00000000..ff3a6cf6 --- /dev/null +++ b/Bit Manipulation/single_number.java @@ -0,0 +1,73 @@ +/** + * Given an array of integers A, every element appears twice except for one. Find that integer that occurs once. + * + * NOTE: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? + * + * + * + * Problem Constraints + * 1 <= |A| <= 2000000 + * + * 0 <= A[i] <= INTMAX + * + * + * + * Input Format + * The first and only argument of input contains an integer array A. + * + * + * + * Output Format + * Return a single integer denoting the single element. + * + * + * + * Example Input + * Input 1: + * + * A = [1, 2, 2, 3, 1] + * Input 2: + * + * A = [1, 2, 2] + * + * + * Example Output + * Output 1: + * + * 3 + * Output 2: + * + * 1 + * + * + * Example Explanation + * Explanation 1: + * + * 3 occurs once. + * Explanation 2: + * + * 1 occurs once. + */ +package BitManipulation; + +import com.google.common.base.Stopwatch; + +public class SingleNumber { + public static void main(String[] args) { + Stopwatch timer = Stopwatch.createStarted(); + + int[] arr = {1, 2, 2, 3, 1}; + int ans = solve(arr); + System.out.println(ans); + + System.out.println("Runtime " + timer); + } + public static int solve(int[] array) { + // O(N) time | O(1) space + int ans = 0; + + for (int num : array) ans ^= num; + + return ans; + } +} diff --git a/Bit Manipulation/subarrays_with_bitwise_OR_1.java b/Bit Manipulation/subarrays_with_bitwise_OR_1.java new file mode 100644 index 00000000..eda139d2 --- /dev/null +++ b/Bit Manipulation/subarrays_with_bitwise_OR_1.java @@ -0,0 +1,68 @@ +/** + * Problem Description + * Given an array B of length A with elements 1 or 0. Find the number of subarrays such that the bitwise OR of all the elements present in the subarray is 1. + * + * + * Problem Constraints + * 1 <= A <= 105 + * + * + * Input Format + * The first argument is a single integer A. + * The second argument is an integer array B. + * + * + * Output Format + * Return the number of subarrays with bitwise array 1. + * + * + * Example Input + * Input 1: + * A = 3 + * B = [1, 0, 1] + * Input 2: + * A = 2 + * B = [1, 0] + * + * + * Example Output + * Output 1: + * 5 + * Output2: + * 2 + * + * + * Example Explanation + * Explanation 1: + * The subarrays are :- [1], [0], [1], [1, 0], [0, 1], [1, 0, 1] + * Except the subarray [0] all the other subarrays has a Bitwise OR = 1 + * Explanation 2: + * The subarrays are :- [1], [0], [1, 0] + * Except the subarray [0] all the other subarrays has a Bitwise OR = 1 + */ + +package BitManipulation; + +public class subArrayWithBitwiseOR1 { + public static void main(String[] args) { + int[] array = {1, 0, 1}; + int n = 3; + int ans = solve(array, n); + System.out.println(ans); + } + public static int solve(int[] array, int len) { + // O(N) time | O(1) space + int possibleSubarraysWithThisIdx = 0; + int ans = 0; + + for (int i = 0; i < len; i++) { + int currentNum = array[i]; + if (currentNum == 1) + possibleSubarraysWithThisIdx = i + 1; + + ans += possibleSubarraysWithThisIdx; + } + + return ans; + } +} diff --git a/Bit_Manipulation/count_bits_test.go b/Bit_Manipulation/count_bits_test.go deleted file mode 100644 index 2ee26983..00000000 --- a/Bit_Manipulation/count_bits_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package bitmanipulation - -import ( - "testing" - "regexp" -) - -func TestCountBits(t *testing.T) { - msg, err := CountBits(5) - if msg != 2 && err != nil { - t.Fatalf(`CountBits(5) = %q %v, want "", error`, msg, err) - } - -} \ No newline at end of file diff --git a/CCTI/Graphs_cycle_detection_dfs.cpp b/CCTI/Graphs_cycle_detection_dfs.cpp deleted file mode 100644 index 3fb137a8..00000000 --- a/CCTI/Graphs_cycle_detection_dfs.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include -using namespace std; -template -class Graph{ - map > L; - public: - Graph(){ - - } - void add_edge(int u, int v, bool bidir = false){ - L[u].push_back(v); - if(bidir){ - L[v].push_back(u); - } - } - void print_graph(){ - for(auto i : L){ - cout << i.first << "->"; - for(auto x: i.second){ - cout << x << ","; - } - cout << endl; - } - } - bool is_cyclic_helper(T node, map &visited, map &in_stack){ - visited[node] = true; - in_stack[node] = true; - for(T neighbour : L[node]){ - if((!visited[neighbour] && is_cyclic_helper(neighbour, visited, in_stack)) || in_stack[neighbour]){ - return true; - } - } - in_stack[node] = false; - return false; - } - bool is_cyclic(){ - map visited; - map in_stack; - for(auto i : L){ - T node = i.first; - bool result = is_cyclic_helper(node, visited, in_stack); - if(result){ - return true; - } - else{ - return false; - } - - } - } -}; -int main(){ - Graph g; - g.add_edge(0, 2); - g.add_edge(0, 1); - g.add_edge(2, 3); - g.add_edge(2, 4); - g.add_edge(3, 4); - //g.add_edge(5, 0); // uncomment to make cycle - //g.add_edge(3, 0); //uncomment to make cycle - g.add_edge(4, 5); - g.add_edge(1, 5); - if(g.is_cyclic()){ - cout << "Graph is Cyclic\n"; - } - else{ - cout << "Graph is Not Cyclic\n"; - } -} \ No newline at end of file diff --git a/CCTI/arrays_strings_check_permutations.cpp b/CCTI/arrays_strings_check_permutations.cpp deleted file mode 100644 index 1b029443..00000000 --- a/CCTI/arrays_strings_check_permutations.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Implement an algorithm to determine if a string is permutation of other. -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -bool check_permutation(string s, string t){ - // Time complexity O(n log (n)) n = len of string - // but its clean simple and easy to understand - if(s.length() != t.length()) - return false; - sort(s.begin(), s.end()); - sort(t.begin(), t.end()); - if(s.compare(t) == 0) - return true; - return false; -} -bool check_permutation_fast(string s, string t){ - // TC O(n) - if(s.length() != t.length()) - return false; - map letters; - for(char c : s){ - letters[c]++; - } - for(char c : t){ - letters[c]--; - if(letters[c] < 0){ - return false; - } - } - return true; -} -void print_ans(bool ans){ - if(ans){ - cout << "YES\n"; - } - else{ - cout << "NO\n"; - } -} -int main(){ - check_permutation("ABDE", "DEBA") == true ? print_ans(true) : print_ans(false); - check_permutation("ABCD", "DEFG") == true ? print_ans(true) : print_ans(false); - check_permutation_fast("AA", "DEBA") == true ? print_ans(true) : print_ans(false); - check_permutation_fast("ADEB", "ABED") == true ? print_ans(true) : print_ans(false); - return 0; -} \ No newline at end of file diff --git a/CCTI/arrays_strings_is_unique.cpp b/CCTI/arrays_strings_is_unique.cpp deleted file mode 100644 index 1cee4c23..00000000 --- a/CCTI/arrays_strings_is_unique.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Implement an algorithm to determine if a string has all unique characters. -// what if you cannot use additional data structures? -// Program Author : Abhisek Kumar Gupta -// Approach 1 : compare every character of string with other character TC O(n^2) -// Approach 2 : Sort the string and compare neighbouring character for dups -// TC of approach 2 : O(n log(n)) -#include -using namespace std; -bool is_unique_normal(string s){ - // using additional data structure - // TC O(min(c, n)) c being size of character set n = len of string - // SC is O(1) here - if(s.length() > 128) - return false; - bool visited[128]; - for(int i = 0; i < s.length(); i++){ - int val = s[i] - 'a'; - if(visited[val]){ - return false; - } - visited[val] = true; - } - return true; -} -bool is_unique(string s){ - // without using additional data structures - // here we reduce our space usage - if(s.length() > 128) - return false; - int checker = 0; - for(int i = 0; i < s.length(); i++){ - int val = s[i] - 'a'; - if(checker & (1 << val)) - return false; - checker |= (1 << val); - } - return true; -} -void print_ans(bool ans, string s){ - if(ans){ - cout << s << " is unique\n"; - } - else{ - cout << s << " is not unique\n"; - } -} -int main(){ - string s = "ABCDD"; - string t = "ABCD"; - string u = "AAAAAABCD"; - is_unique_normal(s) == true ? print_ans(true, s) : print_ans(false, s); - is_unique_normal(t) == true ? print_ans(true, t) : print_ans(false, t); - is_unique_normal(u) == true ? print_ans(true, u) : print_ans(false, u); - cout << "\n***********************\n"; - is_unique(s) == true ? print_ans(true, s) : print_ans(false, s); - is_unique(t) == true ? print_ans(true, t) : print_ans(false, t); - is_unique(u) == true ? print_ans(true, u) : print_ans(false, u); - return 0; -} \ No newline at end of file diff --git a/CCTI/arrays_strings_one_away.cpp b/CCTI/arrays_strings_one_away.cpp deleted file mode 100644 index 13114755..00000000 --- a/CCTI/arrays_strings_one_away.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Given two strings write a function to check if they are one edit(or zero edits) away -// Program Author : Abhisek Kumar Gupta - -// Sample Input : Pale Ple -// Output : Yes Pale -// Sample Input : Pale bae -// Output : No -#include -using namespace std; -bool one_edit_replace(string a, string b){ - bool found_difference = false; - for(int i = 0; i < a.length(); i++){ - if(a[i] != b[i]){ - if(found_difference){ - return false; - } - found_difference = true; - } - } - return true; -} -bool one_edit_insert(string a, string b){ - int i1 = 0, i2 = 0; - while(i1 < a.length() && i2 < b.length()){ - if(a[i1] != b[i2]){ - if(i1 != i2){ - return false; - } - i2++; - } - else{ - i1++; - i2++; - } - } - return true; -} -bool is_one_edit_away(string a, string b){ - if(a.length() == b.length()){ - return one_edit_replace(a, b); - } - else if(a.length() + 1 == b.length()){ - return one_edit_insert(a, b); - } - else if(a.length() - 1 == b.length()){ - return one_edit_insert(b, a); - } - return false; -} -void print_ans(bool ans){ - if(ans){ - cout << "YES\n"; - } - else{ - cout << "NO\n"; - } -} -int main(){ - is_one_edit_away("Pale", "Ple") == true ? print_ans(true) : print_ans(false); - is_one_edit_away("Pales", "Pale") == true ? print_ans(true) : print_ans(false); - is_one_edit_away("Pale", "bale") == true ? print_ans(true) : print_ans(false); - is_one_edit_away("Pale", "bae") == true ? print_ans(true) : print_ans(false); - return 0; -} diff --git a/CCTI/arrays_strings_pallindromic_permutations_effecient.cpp b/CCTI/arrays_strings_pallindromic_permutations_effecient.cpp deleted file mode 100644 index 166d7b81..00000000 --- a/CCTI/arrays_strings_pallindromic_permutations_effecient.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Write a function to check if it is a permutation of a pallindrome using bitvector -// Program Author : Abhisek Kumar Gupta -// Sample Input : aabcb -// Output : Yes -// Sample Input : ab -// Output : No -#include -using namespace std; -bool check_exactly_for_one_set_bit(int bit_vector){ - return (bit_vector & (bit_vector - 1)) == 0; -} -int toggle(int bit_vector, int x){ - if(x < 0) return bit_vector; - int mask = 1 << x; - if((bit_vector & mask) == 0){ - bit_vector |= mask; - } - else{ - bit_vector &= ~mask; - } - return bit_vector; -} - -int create_bit_vector(string a){ - int bit_vector = 0, mask = 0; - for(int i = 0; i < a.length(); i++){ - int x = a[i] - 'a'; - bit_vector = toggle(bit_vector, x); - /* - mask = 1 << x; - if((bit_vector & mask) == 0){ - bit_vector |= mask; - } - else{ - bit_vector &= ~mask; - } - */ - } - return bit_vector; -} -void print_ans(bool ans){ - if(ans){ - cout << "YES\n"; - } - else{ - cout << "NO\n"; - } -} -bool is_permutation_pallindrome(string a){ - int bit_vector = create_bit_vector(a); - return bit_vector == 0 || check_exactly_for_one_set_bit(bit_vector); -} -int main(){ - is_permutation_pallindrome("aabcb") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome("ab") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome("ABA") == true ? print_ans(true) : print_ans(false); - is_permutation_pallindrome("racecarf") == true ? print_ans(true) : print_ans(false); - return 0; -} \ No newline at end of file diff --git a/CCTI/arrays_strings_urlify.cpp b/CCTI/arrays_strings_urlify.cpp deleted file mode 100644 index 80403421..00000000 --- a/CCTI/arrays_strings_urlify.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Implement an algorithm to replace all spaces with %20. -// Sample Input : Mr Ashish Lala -// Output: Mr%20Ashish%20Lala -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -void urlify(string s, int length){ - int space_count = 0; - for(int i = 0; i < length; i++){ - if(s[i] == ' '){ - space_count++; - } - } - int index = length + (space_count * 3); - int new_length = index; - for(int i = length - 1; i >= 0; i--){ - if(s[i] == ' '){ - s[index - 1] = '0'; - s[index - 2] = '2'; - s[index - 3] = '%'; - index = index - 3; - } - else{ - s[index - 1] = s[i]; - index--; - } - } - for(int i = index; i < new_length; i++){ - cout << s[i]; - } - -} -int main(){ - string s = "Mr John Smith "; - int length = 13; - urlify(s, 13); - return 0; -} \ No newline at end of file diff --git a/CCTI/linked_list_delete_middle.cpp b/CCTI/linked_list_delete_middle.cpp deleted file mode 100644 index c2ad9ed3..00000000 --- a/CCTI/linked_list_delete_middle.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Delete Middle node [given only access to that node] -// Note : No head node is given -// Program Author : Abhisek Kumar Gupta -// Sample Input : 5 4 3 2 1, delete 3rd node -// Output: 5 4 2 1 -#include -using namespace std; -class Node{ - public: - int data; - Node *next; - - Node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_head(Node *&head, int data){ - Node *n = new Node(data); - n->next = head; - head = n; -} -void make_linked_list(Node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_head(head, data); - cin >> data; - } -} -void print_linked_list(Node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -bool delete_from_middle(Node *that_node){ - if(that_node == NULL || that_node->next == NULL){ - return false; - } - Node *forward = that_node->next; - that_node->data = forward->data; - that_node->next = forward->next; - delete forward; -} -int main(){ - Node *head = NULL; - make_linked_list(head); - print_linked_list(head); cout << endl; - - if(delete_from_middle(head->next->next)){ - print_linked_list(head); - } - return 0; -} \ No newline at end of file diff --git a/CCTI/linked_list_kth_node_from_end.cpp b/CCTI/linked_list_kth_node_from_end.cpp deleted file mode 100644 index fdc9e80d..00000000 --- a/CCTI/linked_list_kth_node_from_end.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Finding Kth node from end of a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -node* kth_node_from_end(node *head, int k){ - if(head->next == NULL || head == NULL){ - return head; - } - node *slow = head; - node *fast = head; - while(k--){ - fast = fast->next; - } - while(fast){ - fast = fast->next; - slow = slow->next; - } - return slow; -} -node* kth_node_from_end_recursive(node *head, int k, int &i){ - if(head == NULL) - return NULL; - node *n = kth_node_from_end_recursive(head->next, k, i); - i = i + 1; - // cout << "i is " << i << "\n"; - if(i == k){ - // cout<<"i equals k\n"; - return head; - } - return n; -} -int main(){ - /* - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - int k; - cin >> k; - node *kth_node = kth_node_from_end(head, k); - cout << endl; - cout << kth_node->data << endl; - */ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - int k; - cin >> k; - int i = 0; - node *kth_node = kth_node_from_end_recursive(head, k, i); - cout << endl; - cout << kth_node->data << endl; - - return 0; -} \ No newline at end of file diff --git a/CCTI/linked_list_sum_lists.cpp b/CCTI/linked_list_sum_lists.cpp deleted file mode 100644 index e3aeb672..00000000 --- a/CCTI/linked_list_sum_lists.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// sum Lists : you have two numbers represented by a linked list -// where each node contains a single digit, write a function -// to add two numbers and returns the sum as linked list -// Program Author: Abhisek Kumar Gupta -#include -using namespace std; -class Node{ - public: - int data; - Node *next; - - Node(int d){ - data = d; - } -}; - -void insert_at_head(Node *&head, int data){ - Node *new_node = new Node(data); - new_node->next = head; - head = new_node; -} -void print_linked_list(Node *head){ - if(head == NULL) - return; - cout << head->data << "->"; - print_linked_list(head->next); -} -void make_linked_list(Node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_head(head, data); - cin >> data; - } -} -Node* add_lists(Node *l1, Node *l2, int carry){ - - if(l1 == NULL && l2 == NULL && carry == 0) - return NULL; - int value = carry; - - if(l1 != NULL) - value += l1->data; - - if(l2 != NULL) - value += l2->data; - - Node *result = new Node(value % 10); - - if(l1 != NULL || l2 != NULL) - result->next = add_lists(l1 == NULL ? NULL : l1->next, l2 == NULL ? NULL : l2->next, value >= 10 ? 1 : 0); - else - result->next = NULL; - - return result; -} -int main(){ - Node *head1 = NULL; - Node *head2 = NULL; - make_linked_list(head1); - make_linked_list(head2); - print_linked_list(head1); - cout << endl; - print_linked_list(head2); - Node *result = add_lists(head1, head2, 0); - cout << endl; - print_linked_list(result); -} \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 18c91471..82a2301c 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -12,28 +12,36 @@ and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. +## Conflict resolution + +When multiple contributors disagree on the direction for a particular patch or the general direction of the project, the conflict should be resolved by communication. The people who disagree should get together, try to understand each other's points of view, and work to find a design that addresses everyone's concerns. + +This is usually sufficient to resolve issues. If you cannot come to an agreement, ask for the advice of a more senior member of the project. + +Be wary of agreement by attrition, where one person argues a point repeatedly until other participants give up in the interests of moving on. This is not conflict resolution, as it does not address everyone's concerns. Be wary of agreement by compromise, where two good competing solutions are merged into one mediocre solution. A conflict is addressed when the participants agree that the final solution is better than all the conflicting proposals. Sometimes the solution is more work than either of the proposals. Embrace the yak shave. + ## Our Standards Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +114,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 05e35a88..806e47c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,110 @@ ## Contributing +## Fork this repository + +Get involved! Fork this repository by clicking on the fork button on the top of this page. This will create a copy of this repository in your account. + +- [How to Fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo) + +Before you get started, we encourage you to read these documents which describe some of our community norms: + +Our [code of conduct](https://github.com/akgmage/data-structures-and-algorithms/blob/main/CODE_OF_CONDUCT.md), which stipulates explicitly that everyone must be gracious, respectful, and professional. This also documents our conflict resolution policy and encourages people to ask questions. + Contributions are always welcome! -- Pick any good first issue +- Pick any good first issue and add comment on it (Example: "I'll take this up"), or Add classic DSA problem which is currently not present in this repo +- Read description on the issue (Mostly it will be the link to the question) - Add question on top of file - Add sample input and output - Explain approach with comments +- Add Time and Space complexity +- Take care of Readability (Code is written once and read multiple times, so keep this in mind) - Provide link for further reading (optional) -- Send a PR +- Send a Pull Request (PR) against main branch and mention issue number in the request format (#ISSUENUMBER) + +# Example program + +```go +/* + Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) + whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from + the first array in the first position. + + Note that the absolute difference of two integers is the distance between them on the real number line. + For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. + + You can assume that there will only be one pair of numbers with the smallest difference. + + Sample Input Array1 = [-1, 5, 10, 20, 28, 3] + Sample Input Array2 = [26, 134, 135, 15, 17] + + Sample Output = [28, 26] + + Explanation : + + This code implements the Smallest Difference problem which takes two arrays of integers as input and returns a pair of integers, + one from each array, with the smallest absolute difference between them. + + The function first initializes two variables current and smallest to the maximum integer value. It then sorts both input arrays + in ascending order using the sort.Ints function from the sort package. + + The function then iterates through both arrays using two pointers, idx1 and idx2, initialized to 0. Inside the loop, it compares + the elements at the current indices of the two arrays, first and second, and calculates the absolute difference between + them in the current variable. + + If current is smaller than the smallest variable, it updates smallest to current and assigns the current pair of integers + to the result variable. + + The function returns the result variable, which contains the pair of integers with the smallest absolute difference. + + If there are identical integers in the two input arrays, the function will return them immediately, without any further comparisons. + + O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array +*/ + +package main + +import ( + "math" + "sort" +) + +// SmallestDifference takes two integer slices as input and returns a slice with two integers. +// The two integers in the returned slice have the smallest absolute difference among all pairs +// of integers from the two input slices. +func SmallestDifference(array1, array2 []int) []int { + // Initialize variables for the smallest difference and the current difference being calculated + current, smallest := math.MaxInt32, math.MaxInt32 + // Sort the input slices + sort.Ints(array1) + sort.Ints(array2) + // Initialize variables for the indices for the two slices + idx1, idx2 := 0, 0 + // Initialize an empty slice for the result + result := []int{} + // Loop through the two slices until we reach the end of one of the slices + for idx1 < len(array1) && idx2 < len(array2) { + // Get the values at the current indices for the two slices + first, second := array1[idx1], array2[idx2] + // Calculate the current difference between the two values + if first < second { + current = second - first + idx1++ + } else if second < first { + current = first - second + idx2++ + } else { + // If the two values are equal, we can return the pair + return []int{first, second} + } + // Update the smallest difference and result slice if the current difference is smaller + if smallest > current { + smallest = current + result = []int{first, second} + } + } + // Return the pair with the smallest absolute difference + return result +} + +``` diff --git a/Dynamic Programming/best_time_to_buy_and_sell_stock.cpp b/Dynamic Programming/best_time_to_buy_and_sell_stock.cpp new file mode 100644 index 00000000..1bd13c62 --- /dev/null +++ b/Dynamic Programming/best_time_to_buy_and_sell_stock.cpp @@ -0,0 +1,42 @@ +// Best Time to buy and sell stock +/* + Explanation: + We start by initializing the minimum price to the maximum integer value and the maximum profit to 0. + We loop through the prices array, and for each price: + If the price is less than the current minimum price, we update the minimum price. + Otherwise, if the difference between the price and the minimum price is greater than the current maximum profit, we update the maximum profit. + Finally, we return the maximum profit + + Sample Input [7, 1, 5, 3, 6, 4] + Output: 5 buy at 1 sell at 6 + + Time Complexity: O(n), where n is the length of the prices array. + Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit +*/ +#include +#include +#include + +using namespace std; + +int maxProfit(vector& prices) { + int minPrice = INT_MAX; // initialize to maximum value to start with + int maxProfit = 0;// initialize to 0 + + for (int price : prices) { + minPrice = min(minPrice, price); // update minimum price seen so far + maxProfit = max(maxProfit, price - minPrice); // update maximum profit seen so far + } + + return maxProfit; +} + +int main() { + // example usage + vector prices {7, 1, 5, 3, 6, 4}; + int max_profit = maxProfit(prices); + cout << "Max profit: " << max_profit << endl; + + return 0; +} + diff --git a/Dynamic Programming/best_time_to_buy_and_sell_stock.go b/Dynamic Programming/best_time_to_buy_and_sell_stock.go new file mode 100644 index 00000000..ebaa477f --- /dev/null +++ b/Dynamic Programming/best_time_to_buy_and_sell_stock.go @@ -0,0 +1,39 @@ +// Best Time to buy and sell stock +/* + Explanation: + We start by initializing the minimum price to the maximum integer value and the maximum profit to 0. + We loop through the prices array, and for each price: + If the price is less than the current minimum price, we update the minimum price. + Otherwise, if the difference between the price and the minimum price is greater than the current maximum profit, we update the maximum profit. + Finally, we return the maximum profit + + Sample Input [7, 1, 5, 3, 6, 4] + Output: 5 buy at 1 sell at 6 + + Time Complexity: O(n), where n is the length of the prices array. + Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit +*/ +package main + +import ( + "fmt" + "math" +) + +func maxProfit(prices []int) int { + minPrice := math.MaxInt32 // Initialize minimum price to maximum integer value + maxProfit := 0 // Initialize maximum profit to 0 + for _, price := range prices { + if price < minPrice { + minPrice = price // Update minimum price + } else if price-minPrice > maxProfit { + maxProfit = price - minPrice // Update maximum profit + } + } + return maxProfit // Return maximum profit +} + +func main() { + prices := []int{7, 1, 5, 3, 6, 4} + fmt.Println(maxProfit(prices)) // Output: 5 +} \ No newline at end of file diff --git a/Dynamic Programming/best_time_to_buy_and_sell_stock.java b/Dynamic Programming/best_time_to_buy_and_sell_stock.java new file mode 100644 index 00000000..c6e9c3a7 --- /dev/null +++ b/Dynamic Programming/best_time_to_buy_and_sell_stock.java @@ -0,0 +1,43 @@ +// Best Time to buy and sell stock +/* + Explanation: + We start by initializing the minimum price to the maximum integer value and the maximum profit to 0. + We loop through the prices array, and for each price: + If the price is less than the current minimum price, we update the minimum price. + Otherwise, if the difference between the price and the minimum price is greater than the current maximum profit, we update the maximum profit. + Finally, we return the maximum profit + + Sample Input [7, 1, 5, 3, 6, 4] + Output: 5 buy at 1 sell at 6 + + Time Complexity: O(n), where n is the length of the prices array. + Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit +*/ +public class Solution { + public int maxProfit(int[] prices) { + // Initialize variables to track the minimum price seen so far and the maximum profit + int minPrice = Integer.MAX_VALUE; + int maxProfit = 0; + + // Loop through the prices array + for (int i = 0; i < prices.length; i++) { + // If the current price is less than the minimum price seen so far, update the minimum price + if (prices[i] < minPrice) { + minPrice = prices[i]; + } + // If the difference between the current price and the minimum price is greater than the maximum profit seen so far, update the maximum profit + else if (prices[i] - minPrice > maxProfit) { + maxProfit = prices[i] - minPrice; + } + } + + return maxProfit; // Return the maximum profit + } + + public static void main(String[] args) { + int[] prices = {7, 1, 5, 3, 6, 4}; + int maxProfit = maxProfit(prices); + System.out.println("Max Profit: " + maxProfit); + } +} + diff --git a/Dynamic Programming/best_time_to_buy_and_sell_stock.js b/Dynamic Programming/best_time_to_buy_and_sell_stock.js new file mode 100644 index 00000000..8e1ee1da --- /dev/null +++ b/Dynamic Programming/best_time_to_buy_and_sell_stock.js @@ -0,0 +1,36 @@ +// Best Time to buy and sell stock +/* + Explanation: + We start by initializing the minimum price to the maximum integer value and the maximum profit to 0. + We loop through the prices array, and for each price: + If the price is less than the current minimum price, we update the minimum price. + Otherwise, if the difference between the price and the minimum price is greater than the current maximum profit, we update the maximum profit. + Finally, we return the maximum profit + + Sample Input [7, 1, 5, 3, 6, 4] + Output: 5 buy at 1 sell at 6 + + Time Complexity: O(n), where n is the length of the prices array. + Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit +*/ +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (prices) { + let minPrice = Infinity; // keep track of minimum price seen so far + let maxProfit = 0; // keep track of maximum profit seen so far + + for (let i = 0; i < prices.length; i++) { + if (prices[i] < minPrice) { + minPrice = prices[i]; // update minimum price seen so far + } else if (prices[i] - minPrice > maxProfit) { + maxProfit = prices[i] - minPrice; // update maximum profit seen so far + } + } + + return maxProfit; +}; + +const prices = [7, 1, 5, 3, 6, 4]; +console.log(maxProfit(prices)); // Output: 5 diff --git a/Dynamic Programming/best_time_to_buy_and_sell_stock.py b/Dynamic Programming/best_time_to_buy_and_sell_stock.py new file mode 100644 index 00000000..3a582401 --- /dev/null +++ b/Dynamic Programming/best_time_to_buy_and_sell_stock.py @@ -0,0 +1,42 @@ +# Best Time to buy and sell stock +''' + The function maxProfit takes a list of integers prices and returns the maximum profit that can be + made from buying and selling the stock represented by prices. The function works by initializing + the minimum price to a very large number (sys.maxsize) and the maximum profit to 0. + + It then loops through the prices, updating the minimum price if the current price is less than the current minimum + price, and updating the maximum profit if the difference between the current price and the minimum + price is greater than the current maximum profit. Finally, it returns the maximum profit. + + + Sample Input [7, 1, 5, 3, 6, 4] + Output: 5 buy at 1 sell at 6 + + Time Complexity: O(n), where n is the length of the prices array. + Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit +''' + + +def maxProfit(prices: List[int]) -> int: + # initialize the minimum price as maximum integer value + min_price = sys.maxsize + + # initialize the maximum profit as 0 + max_profit = 0 + + # loop through the prices + for price in prices: + # if the current price is less than the minimum price, update the minimum price + if price < min_price: + min_price = price + # else if the difference between the current price and the minimum price is greater than the maximum profit, + # update the maximum profit + elif price - min_price > max_profit: + max_profit = price - min_price + + # return the maximum profit + return max_profit + +prices = [7, 1, 5, 3, 6, 4] +profit = maxProfit(prices) +print(profit) # Output: 5 \ No newline at end of file diff --git a/Dynamic Programming/climb_stairs.cpp b/Dynamic Programming/climb_stairs.cpp new file mode 100644 index 00000000..ff79f163 --- /dev/null +++ b/Dynamic Programming/climb_stairs.cpp @@ -0,0 +1,49 @@ +// A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 +// or 2 steps. In how many distinct ways can the child climb to the top? +#include +using namespace std; + +// ClimbStairs: returns the number of ways in which a child can climb stairs +// Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair +// ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) +int climbStairs(int n) { + // Base case + if (n < 3) { + return n; + } + int cache[n]; + // Initialize initial 2 values + cache[0] = 1; + cache[1] = 2; + for (int i = 2; i < n; i++) { + // Add previous 2 values + cache[i] = cache[i - 1] + cache[i - 2]; + } + return cache[n - 1]; +} + +// Variation: A child is climbing up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. +// Implement a method to count how many possible ways the child can jump up the stairs. +// Approach similar to the above problem +int climbStairsVariation(int n) { + // Base case + if (n < 3) { + return n; + } + int cache[n]; + // Initialize initial 3 values + cache[0] = 1; + cache[1] = 2; + cache[2] = 4; + for (int i = 3; i < n; i++) { + // Add previous 3 values + cache[i] = cache[i - 1] + cache[i - 2] + cache[i - 3]; + } + return cache[n - 1]; +} + +int main() { + cout << climbStairs(5) << endl; + cout << climbStairsVariation(5) << endl; + return 0; +} diff --git a/Dynamic_Programming/climb_stairs.go b/Dynamic Programming/climb_stairs.go similarity index 100% rename from Dynamic_Programming/climb_stairs.go rename to Dynamic Programming/climb_stairs.go diff --git a/Dynamic Programming/climb_stairs.java b/Dynamic Programming/climb_stairs.java new file mode 100644 index 00000000..aecec826 --- /dev/null +++ b/Dynamic Programming/climb_stairs.java @@ -0,0 +1,47 @@ +// A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 +// or 2 steps. In how many distinct ways can the child climb to the top? +public class StairClimbing { + // ClimbStairs: returns the number of ways in which a child can climb stairs + // Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair + // ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) + public static int climbStairs(int n) { + // Base case + if (n < 3) { + return n; + } + int[] cache = new int[n]; + // Initialize initial 2 values + cache[0] = 1; + cache[1] = 2; + for (int i = 2; i < n; i++) { + // Add previous 2 values + cache[i] = cache[i - 1] + cache[i - 2]; + } + return cache[n - 1]; + } + + // Variatiom: A child is climbing up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. + // Implement a method to count how many possible ways the child can jump up the stairs. + // Approach similar to the above problem + public static int climbStairsVariation(int n) { + // Base case + if (n < 3) { + return n; + } + int[] cache = new int[n]; + // Initialize initial 3 values + cache[0] = 1; + cache[1] = 2; + cache[2] = 4; + for (int i = 3; i < n; i++) { + // Add previous 3 values + cache[i] = cache[i - 1] + cache[i - 2] + cache[i - 3]; + } + return cache[n - 1]; + } + + public static void main(String[] args) { + System.out.println(climbStairs(5)); + System.out.println(climbStairsVariation(5)); + } +} diff --git a/Dynamic Programming/climb_stairs.js b/Dynamic Programming/climb_stairs.js new file mode 100644 index 00000000..38cdc85c --- /dev/null +++ b/Dynamic Programming/climb_stairs.js @@ -0,0 +1,44 @@ +// A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 +// or 2 steps. In how many distinct ways can the child climb to the top? + +// ClimbStairs: returns the number of ways in which a child can climb stairs +// Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair +// ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) +function climbStairs(n) { + // Base case + if (n < 3) { + return n; + } + const cache = new Array(n); + // Initialize initial 2 values + cache[0] = 1; + cache[1] = 2; + for (let i = 2; i < n; i++) { + // Add previous 2 values + cache[i] = cache[i - 1] + cache[i - 2]; + } + return cache[n - 1]; +} + +// Variation: A child is climbing up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. +// Implement a method to count how many possible ways the child can jump up the stairs. +// Approach similar to the above problem +function climbStairsVariation(n) { + // Base case + if (n < 3) { + return n; + } + const cache = new Array(n); + // Initialize initial 3 values + cache[0] = 1; + cache[1] = 2; + cache[2] = 4; + for (let i = 3; i < n; i++) { + // Add previous 3 values + cache[i] = cache[i - 1] + cache[i - 2] + cache[i - 3]; + } + return cache[n - 1]; +} + +console.log(climbStairs(5)); +console.log(climbStairsVariation(5)); diff --git a/Dynamic Programming/climb_stairs.py b/Dynamic Programming/climb_stairs.py new file mode 100644 index 00000000..047fe829 --- /dev/null +++ b/Dynamic Programming/climb_stairs.py @@ -0,0 +1,37 @@ +''' +A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 \ +or 2 steps. In how many distinct ways can the child climb to the top? +''' +# ClimbStairs: returns the number of ways in which a child can climb stairs +# Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair +# ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) +def climb_stairs(n): + # Base case + if n < 3: + return n + cache = [0] * n + # Initialize initial 2 values + cache[0], cache[1] = 1, 2 + for i in range(2, n): + # Add previous 2 values + cache[i] = cache[i - 1] + cache[i - 2] + return cache[n - 1] + +# Variation: A child is climbing up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. +# Implement a method to count how many possible ways the child can jump up the stairs. +# Approach similar to the above problem +def climb_stairs_variation(n): + # Base case + if n < 3: + return n + cache = [0] * n + # Initialize initial 3 values + cache[0], cache[1], cache[2] = 1, 2, 4 + for i in range(3, n): + # Add previous 3 values + cache[i] = cache[i - 1] + cache[i - 2] + cache[i - 3] + return cache[n - 1] + +if __name__ == "__main__": + print(climb_stairs(5)) + print(climb_stairs_variation(5)) diff --git a/Dynamic Programming/coin_change.cpp b/Dynamic Programming/coin_change.cpp new file mode 100644 index 00000000..fe971c89 --- /dev/null +++ b/Dynamic Programming/coin_change.cpp @@ -0,0 +1,65 @@ +/* + Coin Change Problem + You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount + of money.Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any + combination of the coins, return -1.You may assume that you have an infinite number of each kind of coin. + + The code uses a dynamic programming approach to solve the coin change problem. The dp vector is used to + store the minimum number of coins needed to make each amount from 0 to amount. The minimum number + of coins needed to make an amount of 0 is 0. Then, for each coin, the code iterates through each + amount from the coin value to the target amount. If the current amount minus the coin value is a + valid amount, the code updates the minimum number of coins needed to make that amount by taking + the minimum of the current value and the value of dp[i - coin] + 1. Finally, if the minimum + number of coins needed to make the target amount is still INT_MAX, it is not possible to make + the amount and the code returns -1. Otherwise, the code returns the minimum number of coins + needed to make the target amount. + + Sample Input : [1, 2, 5] target : 11 + Output 3 (5, 5, 1) + + The time complexity is O(n * V), where n is the number of coins and V is the value we want to make change for. + The space complexity is also O(n * V) as we need to store the minimum number of coins required to make + change for every value up to V for every coin. + + In the worst case, when we have a large number of coins and a large value V, the time and space complexity + can become quite large. However, this approach can efficiently handle a wide range of input values and is + guaranteed to give the optimal solution. +*/ +#include +#include +#include +using namespace std; + +int coinChange(vector& coins, int amount) { + // Create a vector to store the minimum number of coins needed to make each amount from 0 to amount + vector dp(amount + 1, INT_MAX); + + // The minimum number of coins needed to make an amount of 0 is 0 + dp[0] = 0; + + // Iterate through each coin + for (int coin : coins) { + // Iterate through each amount from the coin value to the target amount + for (int i = coin; i <= amount; i++) { + // If the current amount minus the coin value is a valid amount, update the minimum number of coins needed + if (dp[i - coin] != INT_MAX) { + dp[i] = min(dp[i], dp[i - coin] + 1); + } + } + } + + // If the minimum number of coins needed to make the target amount is still INT_MAX, it is not possible to make the amount + if (dp[amount] == INT_MAX) { + return -1; + } + + // Return the minimum number of coins needed to make the target amount + return dp[amount]; +} + +int main() { + vector coins = {1, 2, 5}; + int amount = 11; + cout << "Minimum number of coins needed: " << coinChange(coins, amount) << endl; + return 0; +} diff --git a/Dynamic Programming/coin_change.go b/Dynamic Programming/coin_change.go new file mode 100644 index 00000000..3f1412c0 --- /dev/null +++ b/Dynamic Programming/coin_change.go @@ -0,0 +1,82 @@ +/* + Coin Change Problem + You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount + of money.Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any + combination of the coins, return -1.You may assume that you have an infinite number of each kind of coin. + + This implementation uses a bottom-up approach to fill in a 2D table of minimum coin counts for each amount + up to the target amount. The table is initialized with the base cases (0 coins for an amount of 0, infinity + for an amount greater than 0) and then filled in using the recurrence relation: + + dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i-1]]+1) + + where dp[i][j] is the minimum number of coins needed to make an amount of j using the first i coins. + If the current coin value coins[i-1] is greater than the current amount j, then we can't use that coin, + so we take the minimum number of coins we need to make the amount using only the first i-1 coins (dp[i-1][j]). + Otherwise, we can use the current coin, so we take the minimum of the number of coins we need to make the + amount using only the first i-1 coins (dp[i-1][j]) and the number of coins we need to make the amount minus + the value of the current coin, plus one (dp[i][j-coins[i-1]]+1). + + The final result is dp[len(coins)][amount], which gives us the minimum number of coins needed to make the + target amount. If this value is infinity, then it's not possible to make the amount using the given coins, + so we return -1. + + Sample Input : [1, 2, 5] target : 11 + Output 3 (5, 5, 1) + + The time complexity of this implementation is O(nm), where n is the number of coins and m is the target amount. + The space complexity is also O(nm) because we're storing a 2D table of size (n+1) x (m+1). + +*/ +package main + +import ( + "fmt" + "math" +) + +func coinChange(coins []int, amount int) int { + // create a 2D slice to store the minimum number of coins needed for each subproblem + dp := make([][]int, len(coins)+1) + for i := range dp { + dp[i] = make([]int, amount+1) + } + + // initialize the first row to infinity and the first column to 0 + for j := 1; j <= amount; j++ { + dp[0][j] = math.MaxInt32 + } + for i := 0; i <= len(coins); i++ { + dp[i][0] = 0 + } + + // fill in the rest of the table + for i := 1; i <= len(coins); i++ { + for j := 1; j <= amount; j++ { + if j < coins[i-1] { + dp[i][j] = dp[i-1][j] + } else { + dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i-1]]+1) + } + } + } + + // return the result + if dp[len(coins)][amount] == math.MaxInt32 { + return -1 + } + return dp[len(coins)][amount] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func main() { + coins := []int{1, 2, 5} + amount := 11 + fmt.Println(coinChange(coins, amount)) +} diff --git a/Dynamic Programming/coin_change.java b/Dynamic Programming/coin_change.java new file mode 100644 index 00000000..1055d038 --- /dev/null +++ b/Dynamic Programming/coin_change.java @@ -0,0 +1,91 @@ +/* + Coin Change Problem + You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount + of money.Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any + combination of the coins, return -1.You may assume that you have an infinite number of each kind of coin. + + Example 1: + + Input: coins = [1,2,5], amount = 11 + Output: 3 + Explanation: 11 = 5 + 5 + 1 + + Example 2: + Input: coins = [2], amount = 3 + Output: -1 + + Example 3: + Input: coins = [1], amount = 0 + Output: 0 + + Constraints: + + 1 <= coins.length <= 12 + 1 <= coins[i] <= 231 - 1 + 0 <= amount <= 104 */ + +//SOLUTION +//EXPLANATION OF CODE +/*Using unbounded knapsack in Dynamic programming + * The minimal number of coins required to make up a specific sum using a certain number of coins is stored in a 2D array. + * The array's dimensions are n+1 and amount+1, where n is the length of the coins array. + * Initialisation 1 + * t[i][j] is set to Integer when i = 0.MAX_VALUE-1 indicates that the amount j cannot be calculated using 0 coins. For j = 0, + * t[i][j] is set to 0, suggesting that the amount 0 can be made up of any number of coins. + * Initialisation 2 + * If the amount j is a multiple of the first coin denomination, then the minimum number of coins required to make up the amount j is j/coins[0]. + * Otherwise, it is not possible to make up the amount j using only the first coin denomination, so the value of t[1][j] is set to + * Integer.MAX_VALUE-1. + * + * main code + * The third loop fills in the t array for all other cases. If the current coin denomination coins[i-1] is less than or equal to the current amount + * j, then we can either include or exclude the current coin denomination to make up the amount j. If we include the current coin denomination, + * then the minimum number of coins required is 1 + t[i][j-coins[i-1]]. If we exclude the current coin denomination, then the minimum number of + * coins required is t[i-1][j]. We take the minimum of these two values to get the minimum number of coins required to make up the amount j using + * the first i coin denominations. +Finally, if the value of t[n][amount] is still Integer.MAX_VALUE-1, then it is not possible to make up the amount amount using the coin +denominations in coins, so we return -1. Otherwise, we return the value of t[n][amount], which represents the minimum number of coins required to +make up the amount amount using all the coin denominations in coins. +*/ + +//Code: +class Solution { + public int coinChange(int[] coins, int amount) { + int n=coins.length; + + int t[][]= new int[n+1][amount+1]; + for(int i=0; i -1 { + // If the result is already stored, return it to avoid redundant calculations. + return storedResults[numDice][target] + } + + // Recursive calculation: Find the number of ways to reach the target sum using numDice dice. + numWaysToReachTarget := 0 + for currentTarget := max(0, target-numSides); currentTarget < target; currentTarget++ { + // Recursively calculate the number of ways to reach the currentTarget using numDice-1 dice. + numWaysToReachTarget += diceThrowsHelper(numDice-1, numSides, currentTarget, storedResults) + } + + // Store the result in storedResults for future use. + storedResults[numDice][target] = numWaysToReachTarget + + // Return the total number of ways to reach the target sum. + return numWaysToReachTarget +} + +// max is a helper function to find the maximum of two integers. +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/Dynamic Programming/disk_stacking.go b/Dynamic Programming/disk_stacking.go new file mode 100644 index 00000000..1e9414cd --- /dev/null +++ b/Dynamic Programming/disk_stacking.go @@ -0,0 +1,148 @@ +/* + You're given a non-empty array of arrays where each subarray holds three integers and represents a disk. + These integers denote each disk's width, depth, and height, respectively. Your goal is to stack up the + disks and to maximize the total height of the stack. A disk must have a strictly smaller width, depth, + and height than any other disk below it. + + Write a function that returns an array of the disks in the final stack, starting with the top disk and + ending with the bottom disk. Note that you can't rotate disks; in other words, the integers in each subarray must + represent [width, depth, height] at all times + + Sample input : = [[2, 1, 2], [3, 2, 3], [2, 2, 8], [2, 3, 4], [1, 3, 1], [4, 4, 5]] + Output: [[2, 1, 2], [3, 2, 3], [4, 4, 5]] // 10 (2 + 3 + 5) is the tallest height we can get by + + Explanation: + This code snippet implements the "Disk Stacking" problem, which is a classic dynamic programming problem. The goal is to + find the maximum height that can be achieved by stacking disks on top of each other while adhering to certain conditions. + + The problem is defined as follows: + Given a list of disks represented by their dimensions (width, depth, height), you need to find a stack of disks with the + maximum height. You can only stack a disk on top of another if its width, depth, and height are all strictly smaller than + those of the disk below it. + + Now, let's go through the code step by step: + + 1. `Disk` and `Disks` types: These are custom types defined to simplify the code and make it more readable. `Disk` represents + a single disk's dimensions, and `Disks` is a slice of `Disk`, representing a collection of disks. + + 2. Implementing sort interface for `Disks`: The `Disks` type is provided with three methods - `Len`, `Swap`, and `Less`. + These methods are part of the `sort.Interface`, which allows us to sort the disks based on their height (the third dimension). + + 3. `DiskStacking` function: This is the main function that solves the disk stacking problem. It takes a 2D slice `input`, + representing the dimensions of the disks, and returns a 2D slice representing the sequence of disks to stack to achieve + the maximum height. + + 4. Creating `disks` slice and sorting: The function first converts the input to a `Disks` slice and then sorts it based on + the third dimension (height) in increasing order. Sorting will allow us to consider disks in a specific order while building + the stack. + + 5. Initializing `heights` and `sequences` slices: The function initializes two slices, `heights` and `sequences`, both with + the same length as the number of disks. `heights` will keep track of the maximum height achievable with the current disk at + each position, and `sequences` will store the index of the previous disk that contributes to the current disk's maximum height. + + 6. Dynamic programming loop: The function iterates over each disk and calculates the maximum height that can be achieved by + considering the current disk and all the disks before it. It checks if the conditions (`areValidDimensions`) for placing the + current disk on top of another are met, and if so, it updates the maximum height and the contributing disk index in `heights` + and `sequences`. + + 7. Finding the maximum height and the sequence: After the dynamic programming loop, it finds the index with the maximum + height in the `heights` slice. This index represents the topmost disk in the stack, which contributes to the maximum height. + + 8. Building the sequence: The function calls the `buildSequence` function to create the sequence of disks contributing to + the maximum height. It starts from the topmost disk (found in the previous step) and follows the sequence of disks using the + `sequences` slice. + + 9. Reversing the sequence: The sequence is built in reverse order, starting from the topmost disk. Since the problem requires + the disks to be stacked from bottom to top, the `reverse` function is used to reverse the order of elements in the sequence. + + 10. Returning the result: Finally, the function returns the sequence of disks that should be stacked to achieve the maximum height. + + O(n^2) time | O(n) space - where n is the number of disks +*/ +package main + +import "sort" + +// Custom type for representing a single disk's dimensions (width, depth, height) +type Disk []int + +// Custom type for representing a collection of disks +type Disks []Disk + +// Implementing sort.Interface for Disks to allow sorting based on height (third dimension) +func (disks Disks) Len() int { return len(disks) } +func (disks Disks) Swap(i, j int) { disks[i], disks[j] = disks[j], disks[i] } +func (disks Disks) Less(i, j int) bool { return disks[i][2] < disks[j][2] } + +// Main function to solve the Disk Stacking problem +func DiskStacking(input [][]int) [][]int { + // Convert input to Disks slice and sort it based on height in increasing order + disks := make(Disks, len(input)) + for i, disk := range input { + disks[i] = disk + } + sort.Sort(disks) + + // Initialize slices to store the maximum height and the sequence of disks + heights := make([]int, len(disks)) + sequences := make([]int, len(disks)) + for i := range disks { + heights[i] = disks[i][2] + sequences[i] = -1 + } + + // Dynamic programming loop to find the maximum height and contributing disks + for i := 1; i < len(disks); i++ { + currentDisk := disks[i] + for j := 0; j < i; j++ { + other := disks[j] + + // Check if conditions are met to stack currentDisk on top of other + if areValidDimensions(other, currentDisk) { + // Update maximum height and contributing disk index if needed + if heights[i] <= currentDisk[2]+heights[j] { + heights[i] = currentDisk[2] + heights[j] + sequences[i] = j + } + } + } + } + + // Find the index with the maximum height (topmost disk in the stack) + maxIndex := 0 + for i, height := range heights { + if height > heights[maxIndex] { + maxIndex = i + } + } + + // Build the sequence of disks contributing to the maximum height + sequence := buildSequence(disks, sequences, maxIndex) + + // Return the sequence of disks that should be stacked to achieve the maximum height + return sequence +} + +// Function to check if the conditions are met for placing current disk on top of other +func areValidDimensions(o Disk, c Disk) bool { + return o[0] < c[0] && o[1] < c[1] && o[2] < c[2] +} + +// Function to build the sequence of disks contributing to the maximum height +func buildSequence(disks []Disk, sequences []int, index int) [][]int { + sequence := [][]int{} + for index != -1 { + sequence = append(sequence, disks[index]) + index = sequences[index] + } + // Since the sequence is built in reverse order (top to bottom), reverse it to get correct order + reverse(sequence) + return sequence +} + +// Function to reverse the order of elements in a 2D slice +func reverse(numbers [][]int) { + for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 { + numbers[i], numbers[j] = numbers[j], numbers[i] + } +} diff --git a/Dynamic Programming/disk_stacking.py b/Dynamic Programming/disk_stacking.py new file mode 100644 index 00000000..b6523abc --- /dev/null +++ b/Dynamic Programming/disk_stacking.py @@ -0,0 +1,100 @@ +''' + You're given a non-empty array of arrays where each subarray holds three integers and represents a disk. + These integers denote each disk's width, depth, and height, respectively. Your goal is to stack up the + disks and to maximize the total height of the stack. A disk must have a strictly smaller width, depth, + and height than any other disk below it. + + Write a function that returns an array of the disks in the final stack, starting with the top disk and + ending with the bottom disk. Note that you can't rotate disks; in other words, the integers in each subarray must + represent [width, depth, height] at all times + + Sample input : = [[2, 1, 2], [3, 2, 3], [2, 2, 8], [2, 3, 4], [1, 3, 1], [4, 4, 5]] + Output: [[2, 1, 2], [3, 2, 3], [4, 4, 5]] // 10 (2 + 3 + 5) is the tallest height we can get by + + Explanation: + This code snippet implements the "Disk Stacking" problem, which is a classic dynamic programming problem. The goal is to + find the maximum height that can be achieved by stacking disks on top of each other while adhering to certain conditions. + + The problem is defined as follows: + Given a list of disks represented by their dimensions (width, depth, height), you need to find a stack of disks with the + maximum height. You can only stack a disk on top of another if its width, depth, and height are all strictly smaller than + those of the disk below it. + + Now, let's go through the code step by step: + + 1. `Disk` and `Disks` types: These are custom types defined to simplify the code and make it more readable. `Disk` represents + a single disk's dimensions, and `Disks` is a slice of `Disk`, representing a collection of disks. + + 2. Implementing sort interface for `Disks`: The `Disks` type is provided with three methods - `Len`, `Swap`, and `Less`. + These methods are part of the `sort.Interface`, which allows us to sort the disks based on their height (the third dimension). + + 3. `DiskStacking` function: This is the main function that solves the disk stacking problem. It takes a 2D slice `input`, + representing the dimensions of the disks, and returns a 2D slice representing the sequence of disks to stack to achieve + the maximum height. + + 4. Creating `disks` slice and sorting: The function first converts the input to a `Disks` slice and then sorts it based on + the third dimension (height) in increasing order. Sorting will allow us to consider disks in a specific order while building + the stack. + + 5. Initializing `heights` and `sequences` slices: The function initializes two slices, `heights` and `sequences`, both with + the same length as the number of disks. `heights` will keep track of the maximum height achievable with the current disk at + each position, and `sequences` will store the index of the previous disk that contributes to the current disk's maximum height. + + 6. Dynamic programming loop: The function iterates over each disk and calculates the maximum height that can be achieved by + considering the current disk and all the disks before it. It checks if the conditions (`areValidDimensions`) for placing the + current disk on top of another are met, and if so, it updates the maximum height and the contributing disk index in `heights` + and `sequences`. + + 7. Finding the maximum height and the sequence: After the dynamic programming loop, it finds the index with the maximum + height in the `heights` slice. This index represents the topmost disk in the stack, which contributes to the maximum height. + + 8. Building the sequence: The function calls the `buildSequence` function to create the sequence of disks contributing to + the maximum height. It starts from the topmost disk (found in the previous step) and follows the sequence of disks using the + `sequences` slice. + + 9. Reversing the sequence: The sequence is built in reverse order, starting from the topmost disk. Since the problem requires + the disks to be stacked from bottom to top, the `reverse` function is used to reverse the order of elements in the sequence. + + 10. Returning the result: Finally, the function returns the sequence of disks that should be stacked to achieve the maximum height. + + O(n^2) time | O(n) space - where n is the number of disks +''' +def DiskStacking(disks): + # Sort the disks based on their height + disks.sort(key=lambda x: x[2]) + + # Initialize arrays to store heights and sequences + heights = [disk[2] for disk in disks] # Heights of each disk + sequences = [-1 for _ in disks] # Index sequence for each disk + + # Loop through each disk and calculate the maximum height + for i in range(1, len(disks)): + current_disk = disks[i] + for j in range(i): + other_disk = disks[j] + # Check if the dimensions of the other_disk are valid for stacking + if are_valid_dimensions(other_disk, current_disk): + # Update the height and sequence if the condition is met + if heights[i] <= current_disk[2] + heights[j]: + heights[i] = current_disk[2] + heights[j] + sequences[i] = j + + # Find the index of the maximum height + max_index = heights.index(max(heights)) + + # Build and return the sequence of disks for the maximum height + sequence = build_sequence(disks, sequences, max_index) + return sequence + +def are_valid_dimensions(other_disk, current_disk): + # Check if the dimensions of other_disk allow current_disk to be stacked on top + return other_disk[0] < current_disk[0] and other_disk[1] < current_disk[1] and other_disk[2] < current_disk[2] + +def build_sequence(disks, sequences, index): + sequence = [] + # Build the sequence of disks for the maximum height + while index != -1: + sequence.append(disks[index]) + index = sequences[index] + sequence.reverse() # Reverse the sequence to maintain the correct order + return sequence diff --git a/Dynamic Programming/distance_of_nearest_0.cpp b/Dynamic Programming/distance_of_nearest_0.cpp new file mode 100644 index 00000000..0f51b913 --- /dev/null +++ b/Dynamic Programming/distance_of_nearest_0.cpp @@ -0,0 +1,133 @@ +/*Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell. + +The distance between two adjacent cells is 1. + + + +Example 1: + + +Input: mat = [[0,0,0],[0,1,0],[0,0,0]] +Output: [[0,0,0],[0,1,0],[0,0,0]] +Example 2: + + +Input: mat = [[0,0,0],[0,1,0],[1,1,1]] +Output: [[0,0,0],[0,1,0],[1,2,1]] + + +Constraints: + +m == mat.length +n == mat[i].length +1 <= m, n <= 10^4 +1 <= m * n <= 10^4 +mat[i][j] is either 0 or 1. +There is at least one 0 in mat. + + +explanation: + +stepwise explanation of the code: + +1. We start by initializing the dimensions of the input matrix mat as m (number of rows) and n (number of columns). This will be used later in the code. + +2. We create a result matrix called result of size m x n to store the distance of the nearest 0 for each cell. Initially, we set all the values in result to a large value (in this case, INT_MAX) to represent an unreachable distance. + +3. We create a queue called q to store the cell indices that need to be processed during the breadth-first search. + +4. We iterate through the input matrix mat and enqueue the indices of all the cells with value 0 into the queue. Additionally, we mark the distance of these cells in the result matrix as 0. This initialization step ensures that the cells with value 0 are considered as starting points for the breadth-first search. + +5. Now, we perform the breadth-first search using the queue q. While the queue is not empty, we process the cells in a breadth-first manner. + +6. For each cell (row, col) popped from the queue, we check its neighboring cells in four directions: up, down, left, and right. We define the directions as a vector of pairs called directions, where each pair represents the change in row and column indices to move in a specific direction. + +7. If the neighboring cell indices (newRow, newCol) are within the valid range of the matrix and the current distance in the result matrix for (newRow, newCol) is greater than the distance of the current cell plus 1, we update the distance in the result matrix and enqueue the neighboring cell (newRow, newCol) into the queue for further processing. + +8. Once the breadth-first search is completed, the result matrix will contain the distance of the nearest 0 for each cell in the input matrix. + +9. Finally, we return the result matrix. + +*/ + +#include +#include +#include + +using namespace std; + +class Solution +{ +public: + vector> updateMatrix(vector> &mat) + { + int m = mat.size(); + int n = mat[0].size(); + + // Create a result matrix and initialize with large values + vector> result(m, vector(n, INT_MAX)); + + // Create a queue to store cell indices + queue> q; + + // Initialize the queue with 0 cells and mark their distance as 0 + for (int i = 0; i < m; i++) + { + for (int j = 0; j < n; j++) + { + if (mat[i][j] == 0) + { + result[i][j] = 0; + q.push({i, j}); + } + } + } + + // Perform breadth-first search + vector> directions{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + while (!q.empty()) + { + int row = q.front().first; + int col = q.front().second; + q.pop(); + + for (const auto &dir : directions) + { + int newRow = row + dir.first; + int newCol = col + dir.second; + + if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n) + { + if (result[newRow][newCol] > result[row][col] + 1) + { + result[newRow][newCol] = result[row][col] + 1; + q.push({newRow, newCol}); + } + } + } + } + + return result; + } +}; + +int main() +{ + // Example usage + vector> mat = {{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}; + + Solution s; + vector> result = s.updateMatrix(mat); + + // Print the result + for (const auto &row : result) + { + for (int val : row) + { + cout << val << " "; + } + cout << endl; + } + + return 0; +} diff --git a/Dynamic Programming/distance_of_nearest_0.py b/Dynamic Programming/distance_of_nearest_0.py new file mode 100644 index 00000000..71ea55ff --- /dev/null +++ b/Dynamic Programming/distance_of_nearest_0.py @@ -0,0 +1,80 @@ +''' +Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell. +The distance between two adjacent cells is 1. + +Example 1: +Input: mat = [[0,0,0],[0,1,0],[0,0,0]] +Output: [[0,0,0],[0,1,0],[0,0,0]] +Example 2: +Input: mat = [[0,0,0],[0,1,0],[1,1,1]] +Output: [[0,0,0],[0,1,0],[1,2,1]] + +Constraints: +m == mat.length +n == mat[i].length +1 <= m, n <= 104 +1 <= m * n <= 104 +mat[i][j] is either 0 or 1. +There is at least one 0 in mat. + +Intuition +The goal is to get the nearest 0 for each 1. So how can we get the nearest 0 for each 1? Lets use Multisource BFS and Dynamic Programming. +Approach +To get the nearest 0 for each 1, we should treat every 0 as if they are on the same level, make a BFS on them and keep track of the distance as we go further. Since we are finding the nearest zero, we may need to use pre-calculated distances for the current 1 when all of its neighbours are all 1. +Lets see this with example! +Here is the given grid. + 0 0 0 + 0 1 0 + 1 1 1 +Then by taking all zeros and making a BFS on them, the nearest distance becomes 1 for all ones other than the back ticked 1 found on the third row. + 0 0 0 + 0 1 0 + 1 `1` 1 +The back ticked '1' is not adjacent to 0, so it should use the precalculated distance of the nearest 1 it get. +So, in here we are using precalculated values to get the nearest distance. This technique is called Dynamic programming. +Then we will get this grid by using the values we calculated for its adjacent 1 + 0 0 0 + 0 1 0 + 1 2 1 +Complexity +Time complexity: O(N) +Space complexity: O(V + E) +Code +sample input and output: + +1. Input: mat = [[0,0,0],[0,1,0],[0,0,0]] + Output: [[0,0,0],[0,1,0],[0,0,0]] + +''' +class Solution: + def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]: + + # it a Multi-source BFS like 994. Rotting Oranges + # treat every 0 as if they are on the same level and make BFS on them + def isvalid(row, col): + if row >= len(mat) or row < 0 or col >= len(mat[0]) or col < 0: + return False + return True + + # get every zero and add them to a queue + q, visited = deque(), set() + for row in range( len(mat)): + for col in range( len(mat[0])): + if mat[row][col] == 0: + q.append([row, col]) + visited.add((row, col)) + + level = 1 # the distance to the nearest zero starts from 1 + while q: + size = len(q) + for _ in range( size ): + row, col =q.popleft() + for r, c in [ [1, 0], [-1, 0], [0, 1], [0, -1]]: + newRow, newCol = row + r, col + c + if (newRow, newCol) not in visited and isvalid(newRow, newCol): + mat[newRow][newCol] = level + q.append([newRow, newCol]) + visited.add( (newRow, newCol)) + level += 1 + + return mat \ No newline at end of file diff --git a/Dynamic Programming/distane_of_nearest_0.go b/Dynamic Programming/distane_of_nearest_0.go new file mode 100644 index 00000000..4d88dd0c --- /dev/null +++ b/Dynamic Programming/distane_of_nearest_0.go @@ -0,0 +1,82 @@ +// Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell in Java + +// Solution +// // This solution uses a breadth-first search (BFS) approach to calculate the distance of the nearest 0 for each cell in the matrix. +// // The idea is to initialize a distances matrix with all values set to the maximum integer value, except for the cells that contain 0s, +// // which are set to 0 and added to a queue. We then perform a BFS on the queue, updating the distances of neighboring cells as we go. +// // Finally, we return the updated distances matrix. + +// Time Complexity: + +// We traverse the entire matrix in the worst case to fill the distances matrix with initial values, which takes O(m * n) time. +// We use Breadth-First Search (BFS) to update the distances matrix, which in the worst case can visit each cell once, taking O(m * n) time. +// Therefore, the total time complexity of this solution is O(m * n). + +// Space Complexity: + +// We store the distances matrix, which requires O(m * n) space. +// We use a queue to implement the BFS algorithm, which can store at most m * n cells in the worst case, taking O(m * n) space. +// Therefore, the total space complexity of this solution is O(m * n). + +package main + +import ( + "container/list" + "math" +) + +var directions = [][2]int{{-1, 0}, {0, 1}, {1, 0}, {0, -1}} + +func updateMatrix(mat [][]int) [][]int { + m := len(mat) + n := len(mat[0]) + distances := make([][]int, m) // Initialize a distances matrix + + // Initialize distances to math.MaxInt32 except for cells with 0 + for i := range distances { + distances[i] = make([]int, n) + for j := range distances[i] { + distances[i][j] = math.MaxInt32 + if mat[i][j] == 0 { + distances[i][j] = 0 + } + } + } + + queue := list.New() // Initialize a queue for BFS + + // Perform BFS + for i := 0; i < m; i++ { + for j := 0; j < n; j++ { + if mat[i][j] == 0 { + queue.PushBack([2]int{i, j}) // Add the cell to the queue + } + } + } + + for queue.Len() > 0 { + element := queue.Front() + queue.Remove(element) + cell := element.Value.([2]int) + i, j := cell[0], cell[1] + + for _, direction := range directions { + x, y := i+direction[0], j+direction[1] + + // Check if cell is out of bounds + if x < 0 || x >= m || y < 0 || y >= n { + continue + } + + // Check if distance is already smaller + if distances[x][y] <= distances[i][j]+1 { + continue + } + + distances[x][y] = distances[i][j] + 1 // Update the distance + queue.PushBack([2]int{x, y}) // Add the cell to the queue + } + } + + return distances // Return the updated distances matrix +} diff --git a/Dynamic Programming/distane_of_nearest_0.java b/Dynamic Programming/distane_of_nearest_0.java new file mode 100644 index 00000000..6d3f8109 --- /dev/null +++ b/Dynamic Programming/distane_of_nearest_0.java @@ -0,0 +1,71 @@ +// Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell in Java + + +// Solution +// // This solution uses a breadth-first search (BFS) approach to calculate the distance of the nearest 0 for each cell in the matrix. +// // The idea is to initialize a distances matrix with all values set to the maximum integer value, except for the cells that contain 0s, +// // which are set to 0 and added to a queue. We then perform a BFS on the queue, updating the distances of neighboring cells as we go. +// // Finally, we return the updated distances matrix. + + +// Time Complexity: + +// We traverse the entire matrix in the worst case to fill the distances matrix with initial values, which takes O(m * n) time. +// We use Breadth-First Search (BFS) to update the distances matrix, which in the worst case can visit each cell once, taking O(m * n) time. +// Therefore, the total time complexity of this solution is O(m * n). + +// Space Complexity: + +// We store the distances matrix, which requires O(m * n) space. +// We use a queue to implement the BFS algorithm, which can store at most m * n cells in the worst case, taking O(m * n) space. +// Therefore, the total space complexity of this solution is O(m * n). + +class Solution { + private static final int[][] DIRECTIONS = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; + + public int[][] updateMatrix(int[][] mat) { + int m = mat.length; + int n = mat[0].length; + int[][] distances = new int[m][n]; // Initialize a distances matrix + Queue queue = new LinkedList<>(); // Initialize a queue for BFS + + // Loop through the matrix and set distances to MAX_VALUE except for cells with 0 + for (int i = 0; i < m; i++) { + Arrays.fill(distances[i], Integer.MAX_VALUE); + for (int j = 0; j < n; j++) { + if (mat[i][j] == 0) { + distances[i][j] = 0; + queue.offer(new int[]{i, j}); // Add the cell to the queue + } + } + } + + // Perform BFS + while (!queue.isEmpty()) { + int[] cell = queue.poll(); + int i = cell[0]; + int j = cell[1]; + for (int[] direction : DIRECTIONS) { + int x = i + direction[0]; + int y = j + direction[1]; + if (x < 0 || x >= m || y < 0 || y >= n) { // Check if cell is out of bounds + continue; + } + if (distances[x][y] <= distances[i][j] + 1) {// Check if distance is already smaller + continue; + } + distances[x][y] = distances[i][j] + 1; // Update the distance + queue.offer(new int[]{x, y});// Add the cell to the queue + } + } + return distances; // Return the updated distances matrix + } +} + + +//Input +// mat = +// [[0,0,0],[0,1,0],[0,0,0]] + +// Output +// [[0,0,0],[0,1,0],[0,0,0]] \ No newline at end of file diff --git a/Dynamic Programming/distane_of_nearest_0.js b/Dynamic Programming/distane_of_nearest_0.js new file mode 100644 index 00000000..4b0410ab --- /dev/null +++ b/Dynamic Programming/distane_of_nearest_0.js @@ -0,0 +1,66 @@ +// Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell in Java + + +// Solution +// // This solution uses a breadth-first search (BFS) approach to calculate the distance of the nearest 0 for each cell in the matrix. +// // The idea is to initialize a distances matrix with all values set to the maximum integer value, except for the cells that contain 0s, +// // which are set to 0 and added to a queue. We then perform a BFS on the queue, updating the distances of neighboring cells as we go. +// // Finally, we return the updated distances matrix. + + +// Time Complexity: + +// We traverse the entire matrix in the worst case to fill the distances matrix with initial values, which takes O(m * n) time. +// We use Breadth-First Search (BFS) to update the distances matrix, which in the worst case can visit each cell once, taking O(m * n) time. +// Therefore, the total time complexity of this solution is O(m * n). + +// Space Complexity: + +// We store the distances matrix, which requires O(m * n) space. +// We use a queue to implement the BFS algorithm, which can store at most m * n cells in the worst case, taking O(m * n) space. +// Therefore, the total space complexity of this solution is O(m * n). + +function updateMatrix(mat) { + const m = mat.length; + const n = mat[0].length; + const queue = []; + + // Initialize distance matrix with maximum possible values + const dist = new Array(m).fill().map(() => new Array(n).fill(Number.MAX_VALUE)); + + // Initialize the queue with all cells containing 0 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (mat[i][j] === 0) { + dist[i][j] = 0; + queue.push([i, j]); + } + } + } + + // Perform a BFS starting from the cells containing 0 + while (queue.length > 0) { + const [i, j] = queue.shift(); + + // Check the neighbors of the current cell + const neighbors = [[i - 1, j], [i + 1, j], [i, j - 1], [i, j + 1]]; + for (const [ni, nj] of neighbors) { + // Check if the neighbor is within bounds + if (ni >= 0 && ni < m && nj >= 0 && nj < n) { + // If the distance to the neighbor can be updated + if (dist[ni][nj] > dist[i][j] + 1) { + dist[ni][nj] = dist[i][j] + 1; + queue.push([ni, nj]); + } + } + } + } + + return dist; +} + + +The updateMatrix function takes a binary matrix mat as input and returns a matrix dist with the distance of the nearest 0 for each cell. +The algorithm first initializes the dist matrix with maximum possible values and adds all cells containing 0 to a queue. +Then, it performs a BFS starting from the cells in the queue and updates the distances of the neighboring cells if they can be improved. +Finally, it returns the dist matrix with the updated distances. diff --git a/Dynamic_Programming/edit_distance_dp.cpp b/Dynamic Programming/edit_distance_dp.cpp similarity index 51% rename from Dynamic_Programming/edit_distance_dp.cpp rename to Dynamic Programming/edit_distance_dp.cpp index 9016c152..14e70c51 100644 --- a/Dynamic_Programming/edit_distance_dp.cpp +++ b/Dynamic Programming/edit_distance_dp.cpp @@ -1,52 +1,64 @@ -/* - Given two strings str1 and str2 and following three operations that can performed on str1. - 1) Insert - 2) Remove - 3) Replace - Find minimum number of operations required to convert ‘str1’ into ‘str2’. - For example if input strings are CAT AND CAR the edit distance is 1. - - Input : s1 : saturday s2 : sunday - Output : 3 -*/ -// Dynamic Programming Solution : TC O(n^2) -// Porgram Author : Abhisek Kumar Gupta -#include -using namespace std; -int find_edit_distance(string s1, string s2, int l1, int l2){ - int dp[100][100] = {}; - for(int i = 0; i <= l1; i++){ - dp[i][0] = i; - } - for(int i = 0; i <= l2; i++){ - dp[0][i] = i; - } - for(int i = 1; i <= l1; i++){ - for(int j = 1; j <= l2; j++){ - if(s1[i] == s2[j]) - dp[i][j] = dp[i - 1][j - 1]; - else{ - int del = dp[i][j - 1]; - int replace = dp[i - 1][j - 1]; - int insert = dp[i - 1][j]; - dp[i][j] = min(del, min(replace, insert)) + 1; - } - } - } - for(int i = 0; i <= l1; i++){ - for(int j = 0; j <= l2; j++){ - cout << setw(5) < +using namespace std; + +// Function to find the minimum edit distance between two strings +int find_edit_distance(string s1, string s2, int l1, int l2){ + // Create a 2D array dp to store the edit distances + int dp[100][100] = {}; + + // Initialize the base cases for empty strings + for(int i = 0; i <= l1; i++){ + dp[i][0] = i; // Minimum edit distance for transforming s1[0...i] to an empty string + } + for(int i = 0; i <= l2; i++){ + dp[0][i] = i; // Minimum edit distance for transforming an empty string to s2[0...i] + } + + // Calculate the edit distance for the rest of the strings + for(int i = 1; i <= l1; i++){ + for(int j = 1; j <= l2; j++){ + if(s1[i] == s2[j]) + dp[i][j] = dp[i - 1][j - 1]; // No edit required if characters match + else{ + int del = dp[i][j - 1]; // Deletion (from s2) + int replace = dp[i - 1][j - 1]; // Replacement (of s1[i] with s2[j]) + int insert = dp[i - 1][j]; // Insertion (into s1) + dp[i][j] = min(del, min(replace, insert)) + 1; // Choose the minimum of the three operations + } + } + } + + // Print the edit distance matrix (optional) + for(int i = 0; i <= l1; i++){ + for(int j = 0; j <= l2; j++){ + cout << setw(5) << dp[i][j] << " "; + } + cout << "\n"; + } + + return dp[l1][l2]; // Return the minimum edit distance +} + +int main(){ + string s1 = "abhisek"; + string s2 = "tsunade"; + int l1 = s1.length() - 1; + int l2 = s2.length() - 1; + + int result = find_edit_distance(s1, s2, l1, l2); + cout << "Minimum Edit Distance: " << result; + return 0; +} diff --git a/Dynamic Programming/edit_distance_dp.go b/Dynamic Programming/edit_distance_dp.go new file mode 100644 index 00000000..22aea047 --- /dev/null +++ b/Dynamic Programming/edit_distance_dp.go @@ -0,0 +1,68 @@ +/* + Given two strings str1 and str2 and following three operations that can performed on str1. + 1) Insert + 2) Remove + 3) Replace + Find minimum number of operations required to convert ‘str1’ into ‘str2’. + For example if input strings are CAT AND CAR the edit distance is 1. + + Input : s1 : saturday s2 : sunday + Output : 3 +*/ +// Dynamic Programming Solution : TC O(n^2) +package main + +import ( + "fmt" +) + +// findEditDistance finds the minimum edit distance between two strings. +func findEditDistance(s1, s2 string) int { + l1, l2 := len(s1), len(s2) + + // Create a 2D array dp to store the edit distances. + dp := make([][]int, l1+1) + for i := range dp { + dp[i] = make([]int, l2+1) + } + + // Initialize the base cases for empty strings. + for i := 0; i <= l1; i++ { + dp[i][0] = i // Minimum edit distance for transforming s1[0...i] to an empty string. + } + for i := 0; i <= l2; i++ { + dp[0][i] = i // Minimum edit distance for transforming an empty string to s2[0...i]. + } + + // Calculate the edit distance for the rest of the strings. + for i := 1; i <= l1; i++ { + for j := 1; j <= l2; j++ { + if s1[i-1] == s2[j-1] { + dp[i][j] = dp[i-1][j-1] // No edit required if characters match. + } else { + del := dp[i][j-1] // Deletion (from s2). + replace := dp[i-1][j-1] // Replacement (of s1[i] with s2[j]). + insert := dp[i-1][j] // Insertion (into s1). + dp[i][j] = min(del, min(replace, insert)) + 1 // Choose the minimum of the three operations. + } + } + } + + // Print the edit distance matrix (optional). + for i := 0; i <= l1; i++ { + for j := 0; j <= l2; j++ { + fmt.Printf("%5d ", dp[i][j]) + } + fmt.Println() + } + + return dp[l1][l2] // Return the minimum edit distance. +} + +func main() { + s1 := "abhisek" + s2 := "tsunade" + + result := findEditDistance(s1, s2) + fmt.Printf("Minimum Edit Distance: %d\n", result) +} diff --git a/Dynamic Programming/edit_distance_dp.java b/Dynamic Programming/edit_distance_dp.java new file mode 100644 index 00000000..897015e9 --- /dev/null +++ b/Dynamic Programming/edit_distance_dp.java @@ -0,0 +1,63 @@ +/* + Given two strings str1 and str2 and following three operations that can performed on str1. + 1) Insert + 2) Remove + 3) Replace + Find minimum number of operations required to convert ‘str1’ into ‘str2’. + For example if input strings are CAT AND CAR the edit distance is 1. + + Input : s1 : saturday s2 : sunday + Output : 3 +*/ +// Dynamic Programming Solution : TC O(n^2) +public class EditDistance { + + // Function to find the minimum edit distance between two strings + public static int findEditDistance(String s1, String s2) { + int l1 = s1.length(); + int l2 = s2.length(); + + // Create a 2D array dp to store the edit distances + int[][] dp = new int[l1 + 1][l2 + 1]; + + // Initialize the base cases for empty strings + for (int i = 0; i <= l1; i++) { + dp[i][0] = i; // Minimum edit distance for transforming s1[0...i] to an empty string + } + for (int i = 0; i <= l2; i++) { + dp[0][i] = i; // Minimum edit distance for transforming an empty string to s2[0...i] + } + + // Calculate the edit distance for the rest of the strings + for (int i = 1; i <= l1; i++) { + for (int j = 1; j <= l2; j++) { + if (s1.charAt(i - 1) == s2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; // No edit required if characters match + } else { + int del = dp[i][j - 1]; // Deletion (from s2) + int replace = dp[i - 1][j - 1]; // Replacement (of s1[i] with s2[j]) + int insert = dp[i - 1][j]; // Insertion (into s1) + dp[i][j] = Math.min(del, Math.min(replace, insert)) + 1; // Choose the minimum of the three operations + } + } + } + + // Print the edit distance matrix (optional) + for (int i = 0; i <= l1; i++) { + for (int j = 0; j <= l2; j++) { + System.out.printf("%5d ", dp[i][j]); + } + System.out.println(); + } + + return dp[l1][l2]; // Return the minimum edit distance + } + + public static void main(String[] args) { + String s1 = "abhisek"; + String s2 = "tsunade"; + + int result = findEditDistance(s1, s2); + System.out.println("Minimum Edit Distance: " + result); + } +} diff --git a/Dynamic Programming/edit_distance_dp.js b/Dynamic Programming/edit_distance_dp.js new file mode 100644 index 00000000..3718aa57 --- /dev/null +++ b/Dynamic Programming/edit_distance_dp.js @@ -0,0 +1,58 @@ +/* + Given two strings str1 and str2 and following three operations that can performed on str1. + 1) Insert + 2) Remove + 3) Replace + Find minimum number of operations required to convert ‘str1’ into ‘str2’. + For example if input strings are CAT AND CAR the edit distance is 1. + + Input : s1 : saturday s2 : sunday + Output : 3 +*/ +// Dynamic Programming Solution : TC O(n^2) +// Function to find the minimum edit distance between two strings +function findEditDistance(s1, s2) { + const l1 = s1.length; + const l2 = s2.length; + + // Create a 2D array dp to store the edit distances + const dp = Array.from({ length: l1 + 1 }, () => Array(l2 + 1).fill(0)); + + // Initialize the base cases for empty strings + for (let i = 0; i <= l1; i++) { + dp[i][0] = i; // Minimum edit distance for transforming s1[0...i] to an empty string + } + for (let i = 0; i <= l2; i++) { + dp[0][i] = i; // Minimum edit distance for transforming an empty string to s2[0...i] + } + + // Calculate the edit distance for the rest of the strings + for (let i = 1; i <= l1; i++) { + for (let j = 1; j <= l2; j++) { + if (s1[i - 1] === s2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1]; // No edit required if characters match + } else { + const del = dp[i][j - 1]; // Deletion (from s2) + const replace = dp[i - 1][j - 1]; // Replacement (of s1[i] with s2[j]) + const insert = dp[i - 1][j]; // Insertion (into s1) + dp[i][j] = Math.min(del, Math.min(replace, insert)) + 1; // Choose the minimum of the three operations + } + } + } + + // Print the edit distance matrix (optional) + for (let i = 0; i <= l1; i++) { + console.log( + dp[i].map((value) => value.toString().padStart(5, " ")).join(" ") + ); + } + + return dp[l1][l2]; // Return the minimum edit distance +} + +// Example usage +const s1 = "abhisek"; +const s2 = "tsunade"; + +const result = findEditDistance(s1, s2); +console.log("Minimum Edit Distance:", result); diff --git a/Dynamic Programming/edit_distance_dp.py b/Dynamic Programming/edit_distance_dp.py new file mode 100644 index 00000000..435946a5 --- /dev/null +++ b/Dynamic Programming/edit_distance_dp.py @@ -0,0 +1,49 @@ +''' + Given two strings str1 and str2 and following three operations that can performed on str1. + 1) Insert + 2) Remove + 3) Replace + Find minimum number of operations required to convert ‘str1’ into ‘str2’. + For example if input strings are CAT AND CAR the edit distance is 1. + + Input : s1 : saturday s2 : sunday + Output : 3 +*/ +// Dynamic Programming Solution : TC O(n^2) +''' +# Function to find the minimum edit distance between two strings +def find_edit_distance(s1, s2): + l1, l2 = len(s1), len(s2) + + # Create a 2D list dp to store the edit distances + dp = [[0] * (l2 + 1) for _ in range(l1 + 1)] + + # Initialize the base cases for empty strings + for i in range(l1 + 1): + dp[i][0] = i # Minimum edit distance for transforming s1[0...i] to an empty string + for i in range(l2 + 1): + dp[0][i] = i # Minimum edit distance for transforming an empty string to s2[0...i] + + # Calculate the edit distance for the rest of the strings + for i in range(1, l1 + 1): + for j in range(1, l2 + 1): + if s1[i - 1] == s2[j - 1]: + dp[i][j] = dp[i - 1][j - 1] # No edit required if characters match + else: + del_op = dp[i][j - 1] # Deletion (from s2) + replace_op = dp[i - 1][j - 1] # Replacement (of s1[i] with s2[j]) + insert_op = dp[i - 1][j] # Insertion (into s1) + dp[i][j] = min(del_op, min(replace_op, insert_op)) + 1 # Choose the minimum of the three operations + + # Print the edit distance matrix (optional) + for i in range(l1 + 1): + print(" ".join(map(lambda value: f"{value:5}", dp[i]))) + + return dp[l1][l2] # Return the minimum edit distance + +# Example usage +s1 = "abhisek" +s2 = "tsunade" + +result = find_edit_distance(s1, s2) +print("Minimum Edit Distance:", result) diff --git a/Dynamic_Programming/edit_distance_memoized.cpp b/Dynamic Programming/edit_distance_memoized.cpp similarity index 96% rename from Dynamic_Programming/edit_distance_memoized.cpp rename to Dynamic Programming/edit_distance_memoized.cpp index 490890f0..ddfd029d 100644 --- a/Dynamic_Programming/edit_distance_memoized.cpp +++ b/Dynamic Programming/edit_distance_memoized.cpp @@ -1,44 +1,44 @@ -/* - Given two strings str1 and str2 and following three operations that can performed on str1. - 1) Insert - 2) Remove - 3) Replace - Find minimum number of operations required to convert ‘str1’ into ‘str2’. - For example if input strings are CAT AND CAR the edit distance is 1. - - Input : s1 : saturday s2 : sunday - Output : 3 -*/ -// Memoized Solution : TC O(n^2) -// Porgram Author : Abhisek Kumar Gupta -#include -using namespace std; -int calls = 0; -int memoize[1009][1009]; -int find_edit_distance(string s1, string s2, int l1, int l2){ - calls++; - if(l1 == 0) - return l2; - if(l2 == 0) - return l1; - if(memoize[l1][l2] != -1) return memoize[l1][l2]; - if(s1[l1] == s2[l2]){ - return find_edit_distance(s1, s2, l1 - 1, l2 - 1); - } - int del = find_edit_distance(s1, s2, l1, l2 - 1); - int replace = find_edit_distance(s1, s2, l1 - 1, l2 - 1); - int insert = find_edit_distance(s1, s2, l1 - 1, l2); - memoize[l1][l2] = min (del, min(replace, insert)) + 1; - return min (del, min(replace, insert)) + 1; -} -int main(){ - memset(memoize, -1, sizeof(memoize)); - string s1 = "abhisek"; - string s2 = "tsunade"; - int l1 = s1.length() - 1; - int l2 = s2.length() - 1; - int result = find_edit_distance(s1, s2, l1, l2); - cout << result; - cout << "\n" << calls; - return 0; +/* + Given two strings str1 and str2 and following three operations that can performed on str1. + 1) Insert + 2) Remove + 3) Replace + Find minimum number of operations required to convert ‘str1’ into ‘str2’. + For example if input strings are CAT AND CAR the edit distance is 1. + + Input : s1 : saturday s2 : sunday + Output : 3 +*/ +// Memoized Solution : TC O(n^2) +// Porgram Author : Abhisek Kumar Gupta +#include +using namespace std; +int calls = 0; +int memoize[1009][1009]; +int find_edit_distance(string s1, string s2, int l1, int l2){ + calls++; + if(l1 == 0) + return l2; + if(l2 == 0) + return l1; + if(memoize[l1][l2] != -1) return memoize[l1][l2]; + if(s1[l1] == s2[l2]){ + return find_edit_distance(s1, s2, l1 - 1, l2 - 1); + } + int del = find_edit_distance(s1, s2, l1, l2 - 1); + int replace = find_edit_distance(s1, s2, l1 - 1, l2 - 1); + int insert = find_edit_distance(s1, s2, l1 - 1, l2); + memoize[l1][l2] = min (del, min(replace, insert)) + 1; + return min (del, min(replace, insert)) + 1; +} +int main(){ + memset(memoize, -1, sizeof(memoize)); + string s1 = "abhisek"; + string s2 = "tsunade"; + int l1 = s1.length() - 1; + int l2 = s2.length() - 1; + int result = find_edit_distance(s1, s2, l1, l2); + cout << result; + cout << "\n" << calls; + return 0; } \ No newline at end of file diff --git a/Dynamic_Programming/edit_distance_recursive.cpp b/Dynamic Programming/edit_distance_recursive.cpp similarity index 96% rename from Dynamic_Programming/edit_distance_recursive.cpp rename to Dynamic Programming/edit_distance_recursive.cpp index a5c8c62b..260cd7b0 100644 --- a/Dynamic_Programming/edit_distance_recursive.cpp +++ b/Dynamic Programming/edit_distance_recursive.cpp @@ -1,40 +1,40 @@ -/* - Given two strings str1 and str2 and following three operations that can performed on str1. - 1) Insert - 2) Remove - 3) Replace - Find minimum number of operations required to convert ‘str1’ into ‘str2’. - For example if input strings are CAT AND CAR the edit distance is 1. - - Input : s1 : saturday s2 : sunday - Output : 3 -*/ -// Recursive Solution : TC Exponential -// Porgram Author : Abhisek Kumar Gupta -#include -using namespace std; -int calls = 0; -int find_edit_distance(string s1, string s2, int l1, int l2){ - calls++; - if(l1 == 0) - return l2; - if(l2 == 0) - return l1; - if(s1[l1] == s2[l2]){ - return find_edit_distance(s1, s2, l1 - 1, l2 - 1); - } - int del = find_edit_distance(s1, s2, l1, l2 - 1); - int replace = find_edit_distance(s1, s2, l1 - 1, l2 - 1); - int insert = find_edit_distance(s1, s2, l1 - 1, l2); - return min (del, min(replace, insert)) + 1; -} -int main(){ - string s1 = "abhisek"; - string s2 = "tsunade"; - int l1 = s1.length() - 1; - int l2 = s2.length() - 1; - int result = find_edit_distance(s1, s2, l1, l2); - cout << result; - cout << "\n" << calls; - return 0; +/* + Given two strings str1 and str2 and following three operations that can performed on str1. + 1) Insert + 2) Remove + 3) Replace + Find minimum number of operations required to convert ‘str1’ into ‘str2’. + For example if input strings are CAT AND CAR the edit distance is 1. + + Input : s1 : saturday s2 : sunday + Output : 3 +*/ +// Recursive Solution : TC Exponential +// Porgram Author : Abhisek Kumar Gupta +#include +using namespace std; +int calls = 0; +int find_edit_distance(string s1, string s2, int l1, int l2){ + calls++; + if(l1 == 0) + return l2; + if(l2 == 0) + return l1; + if(s1[l1] == s2[l2]){ + return find_edit_distance(s1, s2, l1 - 1, l2 - 1); + } + int del = find_edit_distance(s1, s2, l1, l2 - 1); + int replace = find_edit_distance(s1, s2, l1 - 1, l2 - 1); + int insert = find_edit_distance(s1, s2, l1 - 1, l2); + return min (del, min(replace, insert)) + 1; +} +int main(){ + string s1 = "abhisek"; + string s2 = "tsunade"; + int l1 = s1.length() - 1; + int l2 = s2.length() - 1; + int result = find_edit_distance(s1, s2, l1, l2); + cout << result; + cout << "\n" << calls; + return 0; } \ No newline at end of file diff --git a/Dynamic Programming/house_robber.cpp b/Dynamic Programming/house_robber.cpp new file mode 100644 index 00000000..ddc286ec --- /dev/null +++ b/Dynamic Programming/house_robber.cpp @@ -0,0 +1,54 @@ +/* +You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night. + +Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police. + + + +Example 1: + +Input: nums = [1,2,3,1] +Output: 4 +Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). +Total amount you can rob = 1 + 3 = 4. +Example 2: + +Input: nums = [2,7,9,3,1] +Output: 12 +Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1). +Total amount you can rob = 2 + 9 + 1 = 12. + + +Constraints: + +1 <= nums.length <= 100 +0 <= nums[i] <= 400 +*/ + +#include + +class Solution { +public: + int rob(vector& nums) { + int len = nums.size(); + /* + if(len == 0) return 0; + if(len == 1) return nums[0]; + if(len == 2) return max(nums[0], nums[1]); + int dp[len]; + dp[0] = nums[0]; + dp[1] = max(nums[0], nums[1]); + for(int i = 2; i < len; i++){ + dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); + } + return dp[len-1]; + */ + int prev = 0, curr = 0, temp = 0; + for(int i = 0; i < len; i++){ + temp = max(nums[i] + prev, curr); + prev = curr; + curr = temp; + } + return curr; + } +}; \ No newline at end of file diff --git a/Dynamic Programming/juice_bottling.cpp b/Dynamic Programming/juice_bottling.cpp new file mode 100644 index 00000000..71327506 --- /dev/null +++ b/Dynamic Programming/juice_bottling.cpp @@ -0,0 +1,76 @@ +/* + + +Explanation: + +1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. + +2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). +These arrays will be used to store information about maximum profit and dividing points. + +3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. + +4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size +and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the +price of the bottle at the current dividing point. + +5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` +and `dividingPoints` arrays. + +6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. +It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. + +7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. + +In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. +It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. +The solution is then reconstructed by backtracking from the end using the recorded dividing points. +*/ + +#include +#include + +std::vector juiceBottling(std::vector& prices) { + int numSizes = prices.size(); + std::vector maxProfit(numSizes); // Vector to store the maximum profit for each bottle size + std::vector dividingPoints(numSizes); // Vector to store the dividing points that maximize profit + + // Loop through each bottle size + for (int size = 0; size < numSizes; size++) { + // Loop through possible dividing points for the current size + for (int dividingPoint = 0; dividingPoint < size + 1; dividingPoint++) { + // Calculate the possible profit by combining the previous maximum profit + // with the price at the current dividing point + int possibleProfit = maxProfit[size - dividingPoint] + prices[dividingPoint]; + + // Update maxProfit and dividingPoints if the new possible profit is greater + if (possibleProfit > maxProfit[size]) { + maxProfit[size] = possibleProfit; + dividingPoints[size] = dividingPoint; + } + } + } + + std::vector solution; + int currentDividingPoint = numSizes - 1; + // Reconstruct the solution by tracing back from the end + // using the dividing points information + while (currentDividingPoint > 0) { + solution.push_back(dividingPoints[currentDividingPoint]); + currentDividingPoint -= dividingPoints[currentDividingPoint]; + } + return solution; +} + +int main() { + std::vector prices = {3, 5, 8, 9, 10, 17, 17, 20}; + std::vector result = juiceBottling(prices); + + std::cout << "Dividing Points for Maximum Profit:"; + for (int point : result) { + std::cout << " " << point; + } + std::cout << std::endl; + + return 0; +} diff --git a/Dynamic Programming/juice_bottling.go b/Dynamic Programming/juice_bottling.go new file mode 100644 index 00000000..d8426376 --- /dev/null +++ b/Dynamic Programming/juice_bottling.go @@ -0,0 +1,61 @@ +/* + + +Explanation: + +1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. + +2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). +These arrays will be used to store information about maximum profit and dividing points. + +3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. + +4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size +and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the +price of the bottle at the current dividing point. + +5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` +and `dividingPoints` arrays. + +6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. +It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. + +7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. + +In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. +It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. +The solution is then reconstructed by backtracking from the end using the recorded dividing points. +*/ +package main + +func JuiceBottling(prices []int) []int { + numSizes := len(prices) + maxProfit := make([]int, numSizes) // Array to store the maximum profit for each bottle size + dividingPoints := make([]int, numSizes) // Array to store the dividing points that maximize profit + + // Loop through each bottle size + for size := 0; size < numSizes; size++ { + // Loop through possible dividing points for the current size + for dividingPoint := 0; dividingPoint < size+1; dividingPoint++ { + // Calculate the possible profit by combining the previous maximum profit + // with the price at the current dividing point + possibleProfit := maxProfit[size-dividingPoint] + prices[dividingPoint] + + // Update maxProfit and dividingPoints if the new possible profit is greater + if possibleProfit > maxProfit[size] { + maxProfit[size] = possibleProfit + dividingPoints[size] = dividingPoint + } + } + } + + solution := []int{} + currentDividingPoint := numSizes - 1 + // Reconstruct the solution by tracing back from the end + // using the dividing points information + for currentDividingPoint > 0 { + solution = append(solution, dividingPoints[currentDividingPoint]) + currentDividingPoint -= dividingPoints[currentDividingPoint] + } + return solution +} diff --git a/Dynamic Programming/juice_bottling.java b/Dynamic Programming/juice_bottling.java new file mode 100644 index 00000000..d75b27ff --- /dev/null +++ b/Dynamic Programming/juice_bottling.java @@ -0,0 +1,73 @@ +/* + + +Explanation: + +1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. + +2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). +These arrays will be used to store information about maximum profit and dividing points. + +3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. + +4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size +and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the +price of the bottle at the current dividing point. + +5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` +and `dividingPoints` arrays. + +6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. +It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. + +7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. + +In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. +It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. +The solution is then reconstructed by backtracking from the end using the recorded dividing points. +*/ + +import java.util.ArrayList; +import java.util.List; + +public class JuiceBottling { + + public static List juiceBottling(int[] prices) { + int numSizes = prices.length; + int[] maxProfit = new int[numSizes]; // Array to store the maximum profit for each bottle size + int[] dividingPoints = new int[numSizes]; // Array to store the dividing points that maximize profit + + // Loop through each bottle size + for (int size = 0; size < numSizes; size++) { + // Loop through possible dividing points for the current size + for (int dividingPoint = 0; dividingPoint < size + 1; dividingPoint++) { + // Calculate the possible profit by combining the previous maximum profit + // with the price at the current dividing point + int possibleProfit = maxProfit[size - dividingPoint] + prices[dividingPoint]; + + // Update maxProfit and dividingPoints if the new possible profit is greater + if (possibleProfit > maxProfit[size]) { + maxProfit[size] = possibleProfit; + dividingPoints[size] = dividingPoint; + } + } + } + + List solution = new ArrayList<>(); + int currentDividingPoint = numSizes - 1; + // Reconstruct the solution by tracing back from the end + // using the dividing points information + while (currentDividingPoint > 0) { + solution.add(dividingPoints[currentDividingPoint]); + currentDividingPoint -= dividingPoints[currentDividingPoint]; + } + return solution; + } + + public static void main(String[] args) { + int[] prices = {3, 5, 8, 9, 10, 17, 17, 20}; + List result = juiceBottling(prices); + + System.out.println("Dividing Points for Maximum Profit: " + result); + } +} diff --git a/Dynamic Programming/juice_bottling.js b/Dynamic Programming/juice_bottling.js new file mode 100644 index 00000000..67d8a71d --- /dev/null +++ b/Dynamic Programming/juice_bottling.js @@ -0,0 +1,65 @@ +/* + + +Explanation: + +1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. + +2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). +These arrays will be used to store information about maximum profit and dividing points. + +3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. + +4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size +and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the +price of the bottle at the current dividing point. + +5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` +and `dividingPoints` arrays. + +6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. +It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. + +7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. + +In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. +It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. +The solution is then reconstructed by backtracking from the end using the recorded dividing points. +*/ +function juiceBottling(prices) { + const numSizes = prices.length; + const maxProfit = new Array(numSizes).fill(0); // Array to store the maximum profit for each bottle size + const dividingPoints = new Array(numSizes).fill(0); // Array to store the dividing points that maximize profit + + // Loop through each bottle size + for (let size = 0; size < numSizes; size++) { + // Loop through possible dividing points for the current size + for (let dividingPoint = 0; dividingPoint < size + 1; dividingPoint++) { + // Calculate the possible profit by combining the previous maximum profit + // with the price at the current dividing point + const possibleProfit = + maxProfit[size - dividingPoint] + prices[dividingPoint]; + + // Update maxProfit and dividingPoints if the new possible profit is greater + if (possibleProfit > maxProfit[size]) { + maxProfit[size] = possibleProfit; + dividingPoints[size] = dividingPoint; + } + } + } + + const solution = []; + let currentDividingPoint = numSizes - 1; + // Reconstruct the solution by tracing back from the end + // using the dividing points information + while (currentDividingPoint > 0) { + solution.push(dividingPoints[currentDividingPoint]); + currentDividingPoint -= dividingPoints[currentDividingPoint]; + } + return solution; +} + +// Example usage: +const prices = [3, 5, 8, 9, 10, 17, 17, 20]; +const result = juiceBottling(prices); +console.log("Dividing Points for Maximum Profit:", result); diff --git a/Dynamic Programming/juice_bottling.py b/Dynamic Programming/juice_bottling.py new file mode 100644 index 00000000..ee834b19 --- /dev/null +++ b/Dynamic Programming/juice_bottling.py @@ -0,0 +1,58 @@ +''' +Explanation: + +1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. + +2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). +These arrays will be used to store information about maximum profit and dividing points. + +3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. + +4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size +and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the +price of the bottle at the current dividing point. + +5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` +and `dividingPoints` arrays. + +6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. +It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. + +7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. + +In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. +It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. +The solution is then reconstructed by backtracking from the end using the recorded dividing points. + +''' +def juice_bottling(prices): + num_sizes = len(prices) + max_profit = [0] * num_sizes # List to store the maximum profit for each bottle size + dividing_points = [0] * num_sizes # List to store the dividing points that maximize profit + + # Loop through each bottle size + for size in range(num_sizes): + # Loop through possible dividing points for the current size + for dividing_point in range(size + 1): + # Calculate the possible profit by combining the previous maximum profit + # with the price at the current dividing point + possible_profit = max_profit[size - dividing_point] + prices[dividing_point] + + # Update max_profit and dividing_points if the new possible profit is greater + if possible_profit > max_profit[size]: + max_profit[size] = possible_profit + dividing_points[size] = dividing_point + + solution = [] + current_dividing_point = num_sizes - 1 + # Reconstruct the solution by tracing back from the end + # using the dividing points information + while current_dividing_point > 0: + solution.append(dividing_points[current_dividing_point]) + current_dividing_point -= dividing_points[current_dividing_point] + return solution + +# Example usage: +prices = [3, 5, 8, 9, 10, 17, 17, 20] +result = juice_bottling(prices) +print("Dividing Points for Maximum Profit:", result) diff --git a/Dynamic Programming/knapsack.cpp b/Dynamic Programming/knapsack.cpp new file mode 100644 index 00000000..bd4b437b --- /dev/null +++ b/Dynamic Programming/knapsack.cpp @@ -0,0 +1,140 @@ +/* + You're given an array of arrays where each subarray holds two integer values and represents an item; + the first integer is the item's value, and the second integer is the item's weight. + You're also given an integer representing the maximum capacity of a knapsack that you have. + + Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's + capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. + + Write a function that returns the maximized combined value of the items that you should pick as well as an array of + the indices of each item picked. + + Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] + Output:= [10, [1, 3]] // items [4, 3] and [6, 7] + + Explanation: + + Sure! Let's break down the code step by step: + + 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the + list of items with their values and weights, and `capacity`, an integer representing the maximum weight + capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved + and the sequence of items included in the knapsack to achieve that maximum value. + + 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum + achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, + where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. + The `values` array will be filled during the dynamic programming process. + + 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` + array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable + value for all possible capacities from `0` to `capacity`. + + 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for + the current item at index `i` and the current capacity `c`. + + 5. Updating the `values` array: There are two possibilities for each item: + a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot + include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` + for the current cell `values[i][c]`. + b. If we can include the current item, we have two choices: + i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. + ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. + We choose the maximum of these two options and update the current cell `values[i][c]`. + + 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the + bottom-right cell `values[len(items)][capacity]`. + + 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in + the knapsack to achieve the maximum value. + + 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing + the indices of the items included in the knapsack. + + 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the + items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above + `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, + it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. + + 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to + reverse the order of elements in the `sequence` slice. + + 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. + + 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` + function is used to reverse the order of elements in a slice. + + Time and Space complexity: + O(nc) time | O(nc) space - where n is the number of items and c is the capacity +*/ +#include +#include + +using namespace std; + +vector knapsackProblem(vector>& items, int capacity) { + // Create a 2D vector to store the values of different knapsack configurations. + vector> values(items.size() + 1, vector(capacity + 1, 0)); + + // Iterate through the items and fill the values vector. + for (int i = 1; i <= items.size(); i++) { + int currentValue = items[i - 1][0]; + int currentWeight = items[i - 1][1]; + + for (int c = 0; c <= capacity; c++) { + // If the current item's weight is more than the current capacity (c), + // then we cannot include it, so we use the value from the previous row (i - 1). + if (currentWeight > c) { + values[i][c] = values[i - 1][c]; + } else { + // If we can include the current item, we have two choices: + // 1. Not include the current item, so the value remains the same as the previous row. + // 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - currentWeight). + // We choose the maximum of these two options. + values[i][c] = max(values[i - 1][c], values[i - 1][c - currentWeight] + currentValue); + } + } + } + + // The value at the bottom-right corner of the values vector represents the maximum achievable value for the knapsack problem. + int value = values[items.size()][capacity]; + + // Call the getKnapSackItems function to find the items that were included in the knapsack to achieve the maximum value. + vector sequence = getKnapSackItems(values, items); + + // Return the maximum value and the sequence of items included in the knapsack. + vector result = {value}; + result.insert(result.end(), sequence.begin(), sequence.end()); + return result; +} + +// getKnapSackItems is a helper function to find the sequence of items included in the knapsack. +vector getKnapSackItems(vector>& values, vector>& items) { + vector sequence; + int i = values.size() - 1; + int c = values[0].size() - 1; + + // Starting from the bottom-right corner of the values vector, + // we traverse back to find the items included in the knapsack. + while (i > 0) { + if (values[i][c] == values[i - 1][c]) { + // If the value is the same as in the previous row, it means the current item was not included. + // So, we move to the previous row without adding the item to the sequence. + i--; + } else { + // If the value is greater than the value in the previous row, it means the current item was included. + // So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. + sequence.push_back(i - 1); + c -= items[i - 1][1]; + i--; + } + // If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. + if (c == 0) { + break; + } + } + + // The sequence of items is built in reverse order, so we need to reverse it to get the correct order. + reverse(sequence.begin(), sequence.end()); + return sequence; +} diff --git a/Dynamic Programming/knapsack.go b/Dynamic Programming/knapsack.go new file mode 100644 index 00000000..9eadad72 --- /dev/null +++ b/Dynamic Programming/knapsack.go @@ -0,0 +1,152 @@ +/* + You're given an array of arrays where each subarray holds two integer values and represents an item; + the first integer is the item's value, and the second integer is the item's weight. + You're also given an integer representing the maximum capacity of a knapsack that you have. + + Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's + capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. + + Write a function that returns the maximized combined value of the items that you should pick as well as an array of + the indices of each item picked. + + Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] + Output:= [10, [1, 3]] // items [4, 3] and [6, 7] + + Explanation: + + Sure! Let's break down the code step by step: + + 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the + list of items with their values and weights, and `capacity`, an integer representing the maximum weight + capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved + and the sequence of items included in the knapsack to achieve that maximum value. + + 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum + achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, + where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. + The `values` array will be filled during the dynamic programming process. + + 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` + array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable + value for all possible capacities from `0` to `capacity`. + + 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for + the current item at index `i` and the current capacity `c`. + + 5. Updating the `values` array: There are two possibilities for each item: + a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot + include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` + for the current cell `values[i][c]`. + b. If we can include the current item, we have two choices: + i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. + ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. + We choose the maximum of these two options and update the current cell `values[i][c]`. + + 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the + bottom-right cell `values[len(items)][capacity]`. + + 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in + the knapsack to achieve the maximum value. + + 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing + the indices of the items included in the knapsack. + + 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the + items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above + `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, + it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. + + 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to + reverse the order of elements in the `sequence` slice. + + 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. + + 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` + function is used to reverse the order of elements in a slice. + + Time and Space complexity: + O(nc) time | O(nc) space - where n is the number of items and c is the capacity +*/ +package main + +func KnapsackProblem(items [][]int, capacity int) []interface{} { + // Create a 2D array to store the values of different knapsack configurations. + values := make([][]int, len(items)+1) + for i := range values { + values[i] = make([]int, capacity+1) + } + + // Iterate through the items and fill the values array. + for i := 1; i < len(items)+1; i++ { + currentValue := items[i-1][0] + currentWeight := items[i-1][1] + + for c := 0; c < capacity+1; c++ { + // If the current item's weight is more than the current capacity (c), + // then we cannot include it, so we use the value from the previous row (i - 1). + if currentWeight > c { + values[i][c] = values[i-1][c] + } else { + // If we can include the current item, we have two choices: + // 1. Not include the current item, so the value remains the same as the previous row. + // 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - currentWeight). + // We choose the maximum of these two options. + values[i][c] = max(values[i-1][c], values[i-1][c-currentWeight]+currentValue) + } + } + } + + // The value at the bottom-right corner of the values array represents the maximum achievable value for the knapsack problem. + value := values[len(items)][capacity] + + // Call the getKnapSackItems function to find the items that were included in the knapsack to achieve the maximum value. + sequence := getKnapSackItems(values, items) + + // Return the maximum value and the sequence of items included in the knapsack as an interface slice. + return []interface{}{value, sequence} +} + +// getKnapSackItems is a helper function to find the sequence of items included in the knapsack. +func getKnapSackItems(values [][]int, items [][]int) []int { + sequence := []int{} + i, c := len(values)-1, len(values[0])-1 + + // Starting from the bottom-right corner of the values array, + // we traverse back to find the items included in the knapsack. + for i > 0 { + if values[i][c] == values[i-1][c] { + // If the value is the same as in the previous row, it means the current item was not included. + // So, we move to the previous row without adding the item to the sequence. + i-- + } else { + // If the value is greater than the value in the previous row, it means the current item was included. + // So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. + sequence = append(sequence, i-1) + c -= items[i-1][1] + i-- + } + // If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. + if c == 0 { + break + } + } + + // The sequence of items is built in reverse order, so we need to reverse it to get the correct order. + reverse(sequence) + return sequence +} + +// max returns the maximum of two integers. +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// reverse reverses the order of elements in the given slice. +func reverse(numbers []int) { + for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 { + numbers[i], numbers[j] = numbers[j], numbers[i] + } +} diff --git a/Dynamic Programming/knapsack.java b/Dynamic Programming/knapsack.java new file mode 100644 index 00000000..799db62e --- /dev/null +++ b/Dynamic Programming/knapsack.java @@ -0,0 +1,159 @@ +/* + You're given an array of arrays where each subarray holds two integer values and represents an item; + the first integer is the item's value, and the second integer is the item's weight. + You're also given an integer representing the maximum capacity of a knapsack that you have. + + Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's + capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. + + Write a function that returns the maximized combined value of the items that you should pick as well as an array of + the indices of each item picked. + + Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] + Output:= [10, [1, 3]] // items [4, 3] and [6, 7] + + Explanation: + + Sure! Let's break down the code step by step: + + 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the + list of items with their values and weights, and `capacity`, an integer representing the maximum weight + capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved + and the sequence of items included in the knapsack to achieve that maximum value. + + 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum + achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, + where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. + The `values` array will be filled during the dynamic programming process. + + 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` + array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable + value for all possible capacities from `0` to `capacity`. + + 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for + the current item at index `i` and the current capacity `c`. + + 5. Updating the `values` array: There are two possibilities for each item: + a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot + include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` + for the current cell `values[i][c]`. + b. If we can include the current item, we have two choices: + i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. + ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. + We choose the maximum of these two options and update the current cell `values[i][c]`. + + 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the + bottom-right cell `values[len(items)][capacity]`. + + 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in + the knapsack to achieve the maximum value. + + 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing + the indices of the items included in the knapsack. + + 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the + items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above + `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, + it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. + + 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to + reverse the order of elements in the `sequence` slice. + + 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. + + 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` + function is used to reverse the order of elements in a slice. + + Time and Space complexity: + O(nc) time | O(nc) space - where n is the number of items and c is the capacity +*/ +import java.util.ArrayList; +import java.util.List; + +public class KnapsackProblem { + public static List knapsackProblem(int[][] items, int capacity) { + // Create a 2D array to store the values of different knapsack configurations. + int[][] values = new int[items.length + 1][capacity + 1]; + + // Iterate through the items and fill the values array. + for (int i = 1; i <= items.length; i++) { + int currentValue = items[i - 1][0]; + int currentWeight = items[i - 1][1]; + + for (int c = 0; c <= capacity; c++) { + // If the current item's weight is more than the current capacity (c), + // then we cannot include it, so we use the value from the previous row (i - 1). + if (currentWeight > c) { + values[i][c] = values[i - 1][c]; + } else { + // If we can include the current item, we have two choices: + // 1. Not include the current item, so the value remains the same as the previous row. + // 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - currentWeight). + // We choose the maximum of these two options. + values[i][c] = Math.max(values[i - 1][c], values[i - 1][c - currentWeight] + currentValue); + } + } + } + + // The value at the bottom-right corner of the values array represents the maximum achievable value for the knapsack problem. + int value = values[items.length][capacity]; + + // Call the getKnapSackItems function to find the items that were included in the knapsack to achieve the maximum value. + List sequence = getKnapSackItems(values, items); + + // Return the maximum value and the sequence of items included in the knapsack as a list of objects. + List result = new ArrayList<>(); + result.add(value); + result.add(sequence); + return result; + } + + // getKnapSackItems is a helper function to find the sequence of items included in the knapsack. + private static List getKnapSackItems(int[][] values, int[][] items) { + List sequence = new ArrayList<>(); + int i = values.length - 1; + int c = values[0].length - 1; + + // Starting from the bottom-right corner of the values array, + // we traverse back to find the items included in the knapsack. + while (i > 0) { + if (values[i][c] == values[i - 1][c]) { + // If the value is the same as in the previous row, it means the current item was not included. + // So, we move to the previous row without adding the item to the sequence. + i--; + } else { + // If the value is greater than the value in the previous row, it means the current item was included. + // So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. + sequence.add(i - 1); + c -= items[i - 1][1]; + i--; + } + // If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. + if (c == 0) { + break; + } + } + + // The sequence of items is built in reverse order, so we need to reverse it to get the correct order. + reverseList(sequence); + return sequence; + } + + // max returns the maximum of two integers. + private static int max(int a, int b) { + return Math.max(a, b); + } + + // reverseList reverses the order of elements in the given list. + private static void reverseList(List list) { + int left = 0; + int right = list.size() - 1; + while (left < right) { + int temp = list.get(left); + list.set(left, list.get(right)); + list.set(right, temp); + left++; + right--; + } + } +} diff --git a/Dynamic Programming/knapsack.js b/Dynamic Programming/knapsack.js new file mode 100644 index 00000000..39653d22 --- /dev/null +++ b/Dynamic Programming/knapsack.js @@ -0,0 +1,143 @@ +/* + You're given an array of arrays where each subarray holds two integer values and represents an item; + the first integer is the item's value, and the second integer is the item's weight. + You're also given an integer representing the maximum capacity of a knapsack that you have. + + Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's + capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. + + Write a function that returns the maximized combined value of the items that you should pick as well as an array of + the indices of each item picked. + + Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] + Output:= [10, [1, 3]] // items [4, 3] and [6, 7] + + Explanation: + + Sure! Let's break down the code step by step: + + 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the + list of items with their values and weights, and `capacity`, an integer representing the maximum weight + capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved + and the sequence of items included in the knapsack to achieve that maximum value. + + 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum + achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, + where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. + The `values` array will be filled during the dynamic programming process. + + 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` + array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable + value for all possible capacities from `0` to `capacity`. + + 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for + the current item at index `i` and the current capacity `c`. + + 5. Updating the `values` array: There are two possibilities for each item: + a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot + include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` + for the current cell `values[i][c]`. + b. If we can include the current item, we have two choices: + i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. + ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. + We choose the maximum of these two options and update the current cell `values[i][c]`. + + 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the + bottom-right cell `values[len(items)][capacity]`. + + 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in + the knapsack to achieve the maximum value. + + 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing + the indices of the items included in the knapsack. + + 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the + items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above + `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, + it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. + + 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to + reverse the order of elements in the `sequence` slice. + + 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. + + 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` + function is used to reverse the order of elements in a slice. + + Time and Space complexity: + O(nc) time | O(nc) space - where n is the number of items and c is the capacity +*/ +function KnapsackProblem(items, capacity) { + // Create a 2D array to store the values of different knapsack configurations. + const values = new Array(items.length + 1) + .fill(0) + .map(() => new Array(capacity + 1).fill(0)); + + // Iterate through the items and fill the values array. + for (let i = 1; i < items.length + 1; i++) { + const currentValue = items[i - 1][0]; + const currentWeight = items[i - 1][1]; + + for (let c = 0; c < capacity + 1; c++) { + // If the current item's weight is more than the current capacity (c), + // then we cannot include it, so we use the value from the previous row (i - 1). + if (currentWeight > c) { + values[i][c] = values[i - 1][c]; + } else { + // If we can include the current item, we have two choices: + // 1. Not include the current item, so the value remains the same as the previous row. + // 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - currentWeight). + // We choose the maximum of these two options. + values[i][c] = Math.max( + values[i - 1][c], + values[i - 1][c - currentWeight] + currentValue + ); + } + } + } + + // The value at the bottom-right corner of the values array represents the maximum achievable value for the knapsack problem. + const value = values[items.length][capacity]; + + // Call the getKnapSackItems function to find the items that were included in the knapsack to achieve the maximum value. + const sequence = getKnapSackItems(values, items); + + // Return the maximum value and the sequence of items included in the knapsack as an array. + return [value, sequence]; +} + +// getKnapSackItems is a helper function to find the sequence of items included in the knapsack. +function getKnapSackItems(values, items) { + const sequence = []; + let i = values.length - 1; + let c = values[0].length - 1; + + // Starting from the bottom-right corner of the values array, + // we traverse back to find the items included in the knapsack. + while (i > 0) { + if (values[i][c] == values[i - 1][c]) { + // If the value is the same as in the previous row, it means the current item was not included. + // So, we move to the previous row without adding the item to the sequence. + i--; + } else { + // If the value is greater than the value in the previous row, it means the current item was included. + // So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. + sequence.push(i - 1); + c -= items[i - 1][1]; + i--; + } + // If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. + if (c == 0) { + break; + } + } + + // Reverse the sequence of items to get the correct order. + sequence.reverse(); + return sequence; +} + +// max returns the maximum of two integers. +function max(a, b) { + return a > b ? a : b; +} diff --git a/Dynamic Programming/knapsack.py b/Dynamic Programming/knapsack.py new file mode 100644 index 00000000..f1715316 --- /dev/null +++ b/Dynamic Programming/knapsack.py @@ -0,0 +1,125 @@ +''' + You're given an array of arrays where each subarray holds two integer values and represents an item; + the first integer is the item's value, and the second integer is the item's weight. + You're also given an integer representing the maximum capacity of a knapsack that you have. + + Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's + capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. + + Write a function that returns the maximized combined value of the items that you should pick as well as an array of + the indices of each item picked. + + Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] + Output:= [10, [1, 3]] // items [4, 3] and [6, 7] + + Explanation: + + Sure! Let's break down the code step by step: + + 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the + list of items with their values and weights, and `capacity`, an integer representing the maximum weight + capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved + and the sequence of items included in the knapsack to achieve that maximum value. + + 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum + achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, + where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. + The `values` array will be filled during the dynamic programming process. + + 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` + array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable + value for all possible capacities from `0` to `capacity`. + + 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for + the current item at index `i` and the current capacity `c`. + + 5. Updating the `values` array: There are two possibilities for each item: + a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot + include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` + for the current cell `values[i][c]`. + b. If we can include the current item, we have two choices: + i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. + ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. + We choose the maximum of these two options and update the current cell `values[i][c]`. + + 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the + bottom-right cell `values[len(items)][capacity]`. + + 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in + the knapsack to achieve the maximum value. + + 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing + the indices of the items included in the knapsack. + + 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the + items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above + `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, + it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. + + 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to + reverse the order of elements in the `sequence` slice. + + 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. + + 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` + function is used to reverse the order of elements in a slice. + + Time and Space complexity: + O(nc) time | O(nc) space - where n is the number of items and c is the capacity +''' +def knapsack_problem(items, capacity): + # Create a 2D list to store the values of different knapsack configurations. + values = [[0] * (capacity + 1) for _ in range(len(items) + 1)] + + # Iterate through the items and fill the values list. + for i in range(1, len(items) + 1): + current_value = items[i - 1][0] + current_weight = items[i - 1][1] + + for c in range(capacity + 1): + # If the current item's weight is more than the current capacity (c), + # then we cannot include it, so we use the value from the previous row (i - 1). + if current_weight > c: + values[i][c] = values[i - 1][c] + else: + # If we can include the current item, we have two choices: + # 1. Not include the current item, so the value remains the same as the previous row. + # 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - current_weight). + # We choose the maximum of these two options. + values[i][c] = max(values[i - 1][c], values[i - 1][c - current_weight] + current_value) + + # The value at the bottom-right corner of the values list represents the maximum achievable value for the knapsack problem. + value = values[len(items)][capacity] + + # Call the get_knapsack_items function to find the items that were included in the knapsack to achieve the maximum value. + sequence = get_knapsack_items(values, items) + + # Return the maximum value and the sequence of items included in the knapsack. + return [value] + sequence + +# get_knapsack_items is a helper function to find the sequence of items included in the knapsack. +def get_knapsack_items(values, items): + sequence = [] + i = len(values) - 1 + c = len(values[0]) - 1 + + # Starting from the bottom-right corner of the values list, + # we traverse back to find the items included in the knapsack. + while i > 0: + if values[i][c] == values[i - 1][c]: + # If the value is the same as in the previous row, it means the current item was not included. + # So, we move to the previous row without adding the item to the sequence. + i -= 1 + else: + # If the value is greater than the value in the previous row, it means the current item was included. + # So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. + sequence.append(i - 1) + c -= items[i - 1][1] + i -= 1 + # If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. + if c == 0: + break + + # The sequence of items is built in reverse order, so we need to reverse it to get the correct order. + sequence.reverse() + return sequence diff --git a/Dynamic Programming/knight_probability_chessboard.cpp b/Dynamic Programming/knight_probability_chessboard.cpp new file mode 100644 index 00000000..e90faaa2 --- /dev/null +++ b/Dynamic Programming/knight_probability_chessboard.cpp @@ -0,0 +1,87 @@ +/* +On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). + +A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. + + +Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. + +The knight continues moving until it has made exactly k moves or has moved off the chessboard. + +Return the probability that the knight remains on the board after it has stopped moving. + + + +Example 1: + +Input: n = 3, k = 2, row = 0, column = 0 +Output: 0.06250 +Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. +From each of those positions, there are also two moves that will keep the knight on the board. +The total probability the knight stays on the board is 0.0625. +Example 2: + +Input: n = 1, k = 0, row = 0, column = 0 +Output: 1.00000 +*/ +#include +#include + +using namespace std; + +class Solution { +public: + double knightProbability(int n, int k, int row, int column) { + // Create a 3D vector for memoization + vector>> dp(k + 1, vector>(n, vector(n, -1.0))); + + // Call the DFS function to compute the probability + return dfs(n, k, row, column, dp); + } + + double dfs(int n, int k, int row, int column, vector>>& dp) { + // Base case: Check if the knight goes off the board + if (row < 0 || row >= n || column < 0 || column >= n) { + return 0.0; + } + + // Base case: If no more moves left, knight remains on the board + if (k == 0) { + return 1.0; + } + + // If result is already computed, return it + if (dp[k][row][column] != -1.0) { + return dp[k][row][column]; + } + + double probability = 0.0; + int directions[8][2] = {{-2, -1}, {-1, -2}, {-2, 1}, {-1, 2}, {2, -1}, {1, -2}, {2, 1}, {1, 2}}; + + // Try all 8 possible knight moves + for (int i = 0; i < 8; ++i) { + int newRow = row + directions[i][0]; + int newColumn = column + directions[i][1]; + probability += 0.125 * dfs(n, k - 1, newRow, newColumn, dp); + } + + // Memoize the result and return it + dp[k][row][column] = probability; + return probability; + } +}; + +int main() { + Solution solution; + + // Example 1 + int n1 = 3, k1 = 2, row1 = 0, column1 = 0; + cout << "Output 1: " << solution.knightProbability(n1, k1, row1, column1) << endl; + + // Example 2 + int n2 = 1, k2 = 0, row2 = 0, column2 = 0; + cout << "Output 2: " << solution.knightProbability(n2, k2, row2, column2) << endl; + + return 0; +} + diff --git a/Dynamic Programming/knight_probability_chessboard.go b/Dynamic Programming/knight_probability_chessboard.go new file mode 100644 index 00000000..9b132c04 --- /dev/null +++ b/Dynamic Programming/knight_probability_chessboard.go @@ -0,0 +1,70 @@ +/* +On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). + +A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. + + +Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. + +The knight continues moving until it has made exactly k moves or has moved off the chessboard. + +Return the probability that the knight remains on the board after it has stopped moving. + + + +Example 1: + +Input: n = 3, k = 2, row = 0, column = 0 +Output: 0.06250 +Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. +From each of those positions, there are also two moves that will keep the knight on the board. +The total probability the knight stays on the board is 0.0625. +Example 2: + +Input: n = 1, k = 0, row = 0, column = 0 +Output: 1.00000 +*/ +func knightProbability(n int, k int, row int, column int) float64 { + // Create a 3D grid to store the probabilities + dp := make([][][]float64, n) + for i := 0; i < n; i++ { + dp[i] = make([][]float64, n) + for j := 0; j < n; j++ { + dp[i][j] = make([]float64, k+1) + } + } + + // Define the eight possible knight moves + moves := [][]int{{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -1}, {2, 1}} + + // Set the initial probability of the knight being on the starting cell to 1 + dp[row][column][0] = 1.0 + + // Calculate the probabilities for each move + for s := 1; s <= k; s++ { + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + for _, move := range moves { + x := i + move[0] + y := j + move[1] + + // Check if the move is within the chessboard + if x >= 0 && x < n && y >= 0 && y < n { + // Accumulate the probability for the current cell + dp[i][j][s] += dp[x][y][s-1] / 8.0 + } + } + } + } + } + + // Calculate the total probability of the knight remaining on the board + probability := 0.0 + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + probability += dp[i][j][k] + } + } + + return probability +} diff --git a/Dynamic Programming/knight_probability_chessboard.java b/Dynamic Programming/knight_probability_chessboard.java new file mode 100644 index 00000000..588159a2 --- /dev/null +++ b/Dynamic Programming/knight_probability_chessboard.java @@ -0,0 +1,78 @@ +/* +On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). + +A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. + + +Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. + +The knight continues moving until it has made exactly k moves or has moved off the chessboard. + +Return the probability that the knight remains on the board after it has stopped moving. + + + +Example 1: + +Input: n = 3, k = 2, row = 0, column = 0 +Output: 0.06250 +Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. +From each of those positions, there are also two moves that will keep the knight on the board. +The total probability the knight stays on the board is 0.0625. +Example 2: + +Input: n = 1, k = 0, row = 0, column = 0 +Output: 1.00000 +*/ +public class KnightProbability { + public double knightProbability(int n, int k, int row, int column) { + // Create a 3D array for memoization + double[][][] dp = new double[k + 1][n][n]; + + // Call the recursive function to compute the probability + return dfs(n, k, row, column, dp); + } + + private double dfs(int n, int k, int row, int column, double[][][] dp) { + // Base case: Check if the knight goes off the board + if (row < 0 || row >= n || column < 0 || column >= n) { + return 0.0; + } + + // Base case: If no more moves left, knight remains on the board + if (k == 0) { + return 1.0; + } + + // If result is already computed, return it + if (dp[k][row][column] != 0.0) { + return dp[k][row][column]; + } + + double probability = 0.0; + int[][] directions = {{-2, -1}, {-1, -2}, {-2, 1}, {-1, 2}, {2, -1}, {1, -2}, {2, 1}, {1, 2}}; + + // Try all 8 possible knight moves + for (int i = 0; i < 8; ++i) { + int newRow = row + directions[i][0]; + int newColumn = column + directions[i][1]; + probability += 0.125 * dfs(n, k - 1, newRow, newColumn, dp); + } + + // Memoize the result and return it + dp[k][row][column] = probability; + return probability; + } + + public static void main(String[] args) { + KnightProbability solution = new KnightProbability(); + + // Example 1 + int n1 = 3, k1 = 2, row1 = 0, column1 = 0; + System.out.println("Output 1: " + solution.knightProbability(n1, k1, row1, column1)); + + // Example 2 + int n2 = 1, k2 = 0, row2 = 0, column2 = 0; + System.out.println("Output 2: " + solution.knightProbability(n2, k2, row2, column2)); + } +} diff --git a/Dynamic Programming/knight_probability_chessboard.js b/Dynamic Programming/knight_probability_chessboard.js new file mode 100644 index 00000000..461e6097 --- /dev/null +++ b/Dynamic Programming/knight_probability_chessboard.js @@ -0,0 +1,75 @@ +/* +On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). + +A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. + + +Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. + +The knight continues moving until it has made exactly k moves or has moved off the chessboard. + +Return the probability that the knight remains on the board after it has stopped moving. + + + +Example 1: + +Input: n = 3, k = 2, row = 0, column = 0 +Output: 0.06250 +Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. +From each of those positions, there are also two moves that will keep the knight on the board. +The total probability the knight stays on the board is 0.0625. +Example 2: + +Input: n = 1, k = 0, row = 0, column = 0 +Output: 1.00000 +*/ +var knightProbability = function (n, k, row, column) { + // Create a 3D grid to store the probabilities + let dp = new Array(n) + .fill(0) + .map(() => new Array(n).fill(0).map(() => new Array(k + 1).fill(0))); + + // Define the eight possible knight moves + const moves = [ + [-2, -1], + [-2, 1], + [-1, -2], + [-1, 2], + [1, -2], + [1, 2], + [2, -1], + [2, 1], + ]; + + // Set the initial probability of the knight being on the starting cell to 1 + dp[row][column][0] = 1.0; + + // Calculate the probabilities for each move + for (let s = 1; s <= k; s++) { + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + for (const move of moves) { + const x = i + move[0]; + const y = j + move[1]; + + // Check if the move is within the chessboard + if (x >= 0 && x < n && y >= 0 && y < n) { + // Accumulate the probability for the current cell + dp[i][j][s] += dp[x][y][s - 1] / 8.0; + } + } + } + } + } + + // Calculate the total probability of the knight remaining on the board + let probability = 0.0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + probability += dp[i][j][k]; + } + } + + return probability; +}; diff --git a/Dynamic Programming/knight_probability_chessboard.py b/Dynamic Programming/knight_probability_chessboard.py new file mode 100644 index 00000000..52d29482 --- /dev/null +++ b/Dynamic Programming/knight_probability_chessboard.py @@ -0,0 +1,57 @@ +''' + On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). + + A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. + + + Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. + + The knight continues moving until it has made exactly k moves or has moved off the chessboard. + + Return the probability that the knight remains on the board after it has stopped moving. + + + + Example 1: + + Input: n = 3, k = 2, row = 0, column = 0 + Output: 0.06250 + Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. + From each of those positions, there are also two moves that will keep the knight on the board. + The total probability the knight stays on the board is 0.0625. + Example 2: + + Input: n = 1, k = 0, row = 0, column = 0 + Output: 1.00000 +''' +class Solution: + def knightProbability(self, n: int, k: int, row: int, column: int) -> float: + # Create a 3D grid to store the probabilities + dp = [[[0 for _ in range(k + 1)] for _ in range(n)] for _ in range(n)] + + # Define the eight possible knight moves + moves = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)] + + # Set the initial probability of the knight being on the starting cell to 1 + dp[row][column][0] = 1.0 + + # Calculate the probabilities for each move + for s in range(1, k + 1): + for i in range(n): + for j in range(n): + for move in moves: + x = i + move[0] + y = j + move[1] + + # Check if the move is within the chessboard + if 0 <= x < n and 0 <= y < n: + # Accumulate the probability for the current cell + dp[i][j][s] += dp[x][y][s - 1] / 8.0 + + # Calculate the total probability of the knight remaining on the board + probability = 0.0 + for i in range(n): + for j in range(n): + probability += dp[i][j][k] + + return probability diff --git a/Dynamic Programming/kth_closest_point_to origin.java b/Dynamic Programming/kth_closest_point_to origin.java new file mode 100644 index 00000000..3b48a346 --- /dev/null +++ b/Dynamic Programming/kth_closest_point_to origin.java @@ -0,0 +1,73 @@ +/* + K Closest Points to Origin + +Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). +The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). +You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). + + + +Example 1: +Input: points = [[1,3],[-2,2]], k = 1 +Output: [[-2,2]] +Explanation: +The distance between (1, 3) and the origin is sqrt(10). +The distance between (-2, 2) and the origin is sqrt(8). +Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. +We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. + +Example 2: +Input: points = [[3,3],[5,-1],[-2,4]], k = 2 +Output: [[3,3],[-2,4]] +Explanation: The answer [[-2,4],[3,3]] would also be accepted. + +Constraints: + +1 <= k <= points.length <= 104 +-104 < xi, yi < 104 + */ + + /* + SOLUTION: + + Explanation: + Solved this question using priority Queue. + Created a priority queue with an Integer.comapre() function. + The solution uses a max-heap, implemented as a PriorityQueue, to keep track of the k closest points seen so far. + We use the Euclidean distance formula to calculate the distance between each point and the origin, + which is given by the square root of the sum of the squares of the coordinates. However, to avoid the expensive square root operation, + we square the distance formula and compare the squared distances instead. + + The PriorityQueue is initialized with a lambda function that compares the squared distances of two points, p1 and p2, + using the Integer.compare() method. This lambda function sorts the points in descending order of their squared distances so that the + largest squared distance is at the top of the queue. + + + + + */ + + //code: + class Solution { + + public int[][] kClosest(int[][] points, int k) { + + Queue q = new PriorityQueue((p1, p2) -> Integer.compare((p2[0] * p2[0] + p2[1] * p2[1]),(p1[0] * p1[0] + p1[1] * p1[1]))); // Created a priority queue (Implementing max-heap) with a comparison function. + + for(int i=0; i k){ + q.poll(); + } + + } + int[][] arr = new int[k][2]; + while(k>0){ + arr[--k] = q.poll(); /* Creating a 2D array of size K and storing points in non decreasing order of their distance (from ,least-most). */ + } + return arr; //answer + + } +} \ No newline at end of file diff --git a/Dynamic Programming/largest_rectangle.java b/Dynamic Programming/largest_rectangle.java new file mode 100644 index 00000000..899e0787 --- /dev/null +++ b/Dynamic Programming/largest_rectangle.java @@ -0,0 +1,89 @@ +//Program Author : TheCodeVenturer [Niraj Modi] +/* + Problem Definition: + Dynamic Programming: Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area in Python + Approach: + This Problem can be termed as updated version of Largest Rectangle in Histogram + If you are given a binary matrix then you can use no.of rows together with one as height + Like + 0 1 1 0 + 1 1 1 1 + 1 1 1 1 + 1 1 0 0 + If you can Visualise then it will be clear that for + first row the histogram row is like [0,1,1,0] + second row the histogram row is like [1,2,2,1] + third row the histogram row is like [2,3,3,2] + fourth row the histogram row is like [3,4,0,0] + then by using a monotonic stack for each row we can get The Largest Rectangle in the Binary Matrix + + we are taking here a row list which keeps track of current height of a particular column . Here, ShiftRow + we are also using a solution variable to keep track of largest rectangle + then first we will iterate through each row + and inside each iteration we will go and look for the particular element of that row matrix[i][j] + if it is 1 then will increase size of jth entry in the shiftRow else will convert it to zero + next will initialize an empty Stack [Monotonic] + next we will iterate through the shiftRow and will first check for the list is not empty and (it's top element is greater than or equal to current element or value of current column is equal to row size) + then will store it's height from the current row array and will update width of the rectangle with stack's top element and will finally update the sol + and will insert the element to the stack + Complexity: + Time Complexity: O(rows * col) for for traversing through each elements of the array + Here in each iteration we are doint three times O(n) => O(3n) ~ O(n) + Space Complexity: O(n) for the shiftRow and O(n) for the stack we are using => O(2n) ~ O(n) + Sample input/outputs: + Example 1: + Input: [[0,1,1,0],[1,1,1,1],[1,1,1,1],[1,1,0,0]] + Output: 8 + + Example 2: + Input: [[0,1,1],[1,1,1],[0,1,1]] + Output: 6 +*/ +import java.util.*; + +public class Solution { + public static int maxArea(int[][] matrix, int rows, int cols) { + int[] shiftRow = new int[cols]; // initializing the row which updates after each iteration + int sol = 0; + + for (int[] row : matrix) { + for (int i = 0; i < row.length; i++) { + // Updating the shiftRow if the value of ele is 1 => shiftRow[i] <- shiftRow[i] + 1 + // else shiftRow[i] = 0 + int ele = row[i]; + if (ele == 1) shiftRow[i]++; + else shiftRow[i] = 0; + } + + Deque stack = new ArrayDeque<>(); + + for (int i = 0; i < cols + 1; i++) { + while (!stack.isEmpty() && (i == cols || shiftRow[stack.peek()] >= shiftRow[i])) { + // checking TOS (top of stack) stack.length - 1 + int height = shiftRow[stack.peek()]; // for getting the height of the current index + stack.pop(); + int width = i; // setting width to i as it is only smallest from the beginning + if (!stack.isEmpty()) width = i - stack.peek() - 1; // updating width if the stack is not empty as it is not the smallest element + sol = Math.max(height * width, sol); // updating the sol + } + + stack.push(i); // pushing the element's index to the stack + } + } + + return sol; + } + + public static void main(String[] args) { + int[][] matrix = { + {0, 1, 1, 0}, + {1, 1, 1, 1}, + {1, 1, 1, 1}, + {1, 1, 0, 0} + }; + + System.out.println(maxArea(matrix, 4, 4)); + } +} + + \ No newline at end of file diff --git a/Dynamic Programming/largest_rectangle.js b/Dynamic Programming/largest_rectangle.js new file mode 100644 index 00000000..bd359139 --- /dev/null +++ b/Dynamic Programming/largest_rectangle.js @@ -0,0 +1,75 @@ +//Program Author : TheCodeVenturer [Niraj Modi] +/* + Problem Definition: + Dynamic Programming: Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area in Python + Approach: + This Problem can be termed as updated version of Largest Rectangle in Histogram + If you are given a binary matrix then you can use no.of rows together with one as height + Like + 0 1 1 0 + 1 1 1 1 + 1 1 1 1 + 1 1 0 0 + If you can Visualise then it will be clear that for + first row the histogram row is like [0,1,1,0] + second row the histogram row is like [1,2,2,1] + third row the histogram row is like [2,3,3,2] + fourth row the histogram row is like [3,4,0,0] + then by using a monotonic stack for each row we can get The Largest Rectangle in the Binary Matrix + + we are taking here a row list which keeps track of current height of a particular column . Here, ShiftRow + we are also using a solution variable to keep track of largest rectangle + then first we will iterate through each row + and inside each iteration we will go and look for the particular element of that row matrix[i][j] + if it is 1 then will increase size of jth entry in the shiftRow else will convert it to zero + next will initialize an empty Stack [Monotonic] + next we will iterate through the shiftRow and will first check for the list is not empty and (it's top element is greater than or equal to current element or value of current column is equal to row size) + then will store it's height from the current row array and will update width of the rectangle with stack's top element and will finally update the sol + and will insert the element to the stack + Complexity: + Time Complexity: O(rows * col) for for traversing through each elements of the array + Here in each iteration we are doint three times O(n) => O(3n) ~ O(n) + Space Complexity: O(n) for the shiftRow and O(n) for the stack we are using => O(2n) ~ O(n) + Sample input/outputs: + Example 1: + Input: [[0,1,1,0],[1,1,1,1],[1,1,1,1],[1,1,0,0]] + Output: 8 + + Example 2: + Input: [[0,1,1],[1,1,1],[0,1,1]] + Output: 6 +*/ +var maxArea = function (matrix, rows, cols) { + let shiftRow = new Array(cols).fill(0); //initialising the row which update after each iteration + var sol = 0; + for (let row of matrix) { + for (let i = 0; i < row.length; i++) { + // Updating the shiftRow if value of ele is 1 => ShiftRow[i] <- shiftRow[i]+1 + // else shiftRow[i]=0 + var ele = row[i]; + if (ele == 1) shiftRow[i]++; + else shiftRow[i] = 0; + } + st = []; + for (let i = 0; i < cols + 1; i++) { + while (st.length > 0 &&(i == cols || shiftRow[st[st.length - 1]] >= shiftRow[i])) { + //checking TOS st.length-1 + height = shiftRow[st[st.length - 1]]; //for getting height of Current index + st.pop(); + width = i; // setting width to i as it is only smallest from beginning + if (st.length > 0) width = i - st[st.length - 1] - 1; // updating width is stack is not empty as it is not the smallest element + sol = Math.max(height * width, sol); // Updating the sol + } + st.push(i); // Pushing the Element's index to the stack + } + } + return sol; +}; + +var matrix = [ + [0, 1, 1, 0], + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 0, 0], +]; +console.log(maxArea(matrix, 4, 4)); diff --git a/Dynamic Programming/largest_rectangle.py b/Dynamic Programming/largest_rectangle.py new file mode 100644 index 00000000..37355c00 --- /dev/null +++ b/Dynamic Programming/largest_rectangle.py @@ -0,0 +1,69 @@ +#Program Author : TheCodeVenturer [Niraj Modi] +''' + Problem Definition: + Dynamic Programming: Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area in Python + Approach: + This Problem can be termed as updated version of Largest Rectangle in Histogram + If you are given a binary matrix then you can use no.of rows together with one as height + Like + 0 1 1 0 + 1 1 1 1 + 1 1 1 1 + 1 1 0 0 + If you can Visualise then it will be clear that for + first row the histogram row is like [0,1,1,0] + second row the histogram row is like [1,2,2,1] + third row the histogram row is like [2,3,3,2] + fourth row the histogram row is like [3,4,0,0] + then by using a monotonic stack for each row we can get The Largest Rectangle in the Binary Matrix + + we are taking here a row list which keeps track of current height of a particular column . Here, ShiftRow + we are also using a solution variable to keep track of largest rectangle + then first we will iterate through each row + and inside each iteration we will go and look for the particular element of that row matrix[i][j] + if it is 1 then will increase size of jth entry in the shiftRow else will convert it to zero + next will initialize an empty Stack [Monotonic] + next we will iterate through the shiftRow and will first check for the list is not empty and (it's top element is greater than or equal to current element or value of current column is equal to row size) + then will store it's height from the current row array and will update width of the rectangle with stack's top element and will finally update the sol + and will insert the element to the stack + Complexity: + Time Complexity: O(rows * col) for for traversing through each elements of the array + Here in each iteration we are doint three times O(n) => O(3n) ~ O(n) + Space Complexity: O(n) for the shiftRow and O(n) for the stack we are using => O(2n) ~ O(n) + Sample input/outputs: + Example 1: + Input: [[0,1,1,0],[1,1,1,1],[1,1,1,1],[1,1,0,0]] + Output: 8 + + Example 2: + Input: [[0,1,1],[1,1,1],[0,1,1]] + Output: 6 +''' +class Solution: + def maxArea(self,matrix, rows, cols): + shiftRow = [0]*cols #initialising the row which update after each iteration + sol=0 + for row in matrix: + for i,ele in enumerate(row): #used enumerate as it will give index as well as element + # Updating the shiftRow if value of ele is 1 => ShiftRow[i] <- shiftRow[i]+1 + # else shiftRow[i]=0 + if ele==1: + shiftRow[i]+=1 + else: + shiftRow[i]=0 + st = [] + for i in range(cols+1): + while(len(st)>0 and(i==cols or shiftRow[st[-1]]>=shiftRow[i])): + height = shiftRow[st[-1]] #for getting height of Current index + st.pop() + width = i # setting width to i as it is only smallest from beginning + if(len(st)>0): + width = i - st[-1] - 1 # updating width is stack is not empty as it is not the smallest element + sol = max(height*width,sol) # Updating the sol + st.append(i) # Pushing the Element's index to the stack + return sol + +if __name__ == '__main__': + matrix = [[0,1,1,0],[1,1,1,1],[1,1,1,1],[1,1,0,0]] + print(Solution().maxArea(matrix, 4, 4)) + diff --git a/Dynamic_Programming/longest_common_subsequence_dp.cpp b/Dynamic Programming/longest_common_subsequence_dp.cpp similarity index 96% rename from Dynamic_Programming/longest_common_subsequence_dp.cpp rename to Dynamic Programming/longest_common_subsequence_dp.cpp index 800d39bb..94bc986a 100644 --- a/Dynamic_Programming/longest_common_subsequence_dp.cpp +++ b/Dynamic Programming/longest_common_subsequence_dp.cpp @@ -1,43 +1,43 @@ -// DP : Find Longest Common Subsequence of two string -// Program Author : Abhisek Kumar Gupta -#include - -using namespace std; -const int maxi = 10000; -int find_longest_common_subsequence(string a, string b, int n, int m, int dp[][maxi]){ - for(int i = 0; i <= n; i++){ - dp[i][0] = 0; - } - for(int i = 0; i <= m; i++){ - dp[0][i] = 0; - } - // dp solution builds the table of LCS of substrings and starts - // computing the length building on the final solution - // we place one string along row and one along the column - for(int i = 1; i <= n; i++){ - for(int j = 1; j <= m; j++){ - // populating table in row-wise order - if(a[i - 1] == b[j - 1]){ - dp[i][j] = dp[i - 1][j - 1] + 1; - } - else{ - dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); - } - } - } - return dp[m][n]; -} -int main(){ - string a = "DABCEFIHKST"; - string b = "DCEFKAAAQST"; - int n = a.length(); - int m = b.length(); - int dp[n+1][maxi]; - memset(dp, -1, sizeof(dp)); - int result = find_longest_common_subsequence(a, b, n, m, dp); - cout << result; - return 0; -} -// Time complexity O(N * M) improvement over both -// Recursion and memoization +// DP : Find Longest Common Subsequence of two string +// Program Author : Abhisek Kumar Gupta +#include + +using namespace std; +const int maxi = 10000; +int find_longest_common_subsequence(string a, string b, int n, int m, int dp[][maxi]){ + for(int i = 0; i <= n; i++){ + dp[i][0] = 0; + } + for(int i = 0; i <= m; i++){ + dp[0][i] = 0; + } + // dp solution builds the table of LCS of substrings and starts + // computing the length building on the final solution + // we place one string along row and one along the column + for(int i = 1; i <= n; i++){ + for(int j = 1; j <= m; j++){ + // populating table in row-wise order + if(a[i - 1] == b[j - 1]){ + dp[i][j] = dp[i - 1][j - 1] + 1; + } + else{ + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[m][n]; +} +int main(){ + string a = "DABCEFIHKST"; + string b = "DCEFKAAAQST"; + int n = a.length(); + int m = b.length(); + int dp[n+1][maxi]; + memset(dp, -1, sizeof(dp)); + int result = find_longest_common_subsequence(a, b, n, m, dp); + cout << result; + return 0; +} +// Time complexity O(N * M) improvement over both +// Recursion and memoization // Space complexity O(N * M) \ No newline at end of file diff --git a/Dynamic_Programming/longest_common_subsequence_memoization.cpp b/Dynamic Programming/longest_common_subsequence_memoization.cpp similarity index 97% rename from Dynamic_Programming/longest_common_subsequence_memoization.cpp rename to Dynamic Programming/longest_common_subsequence_memoization.cpp index dbfc4c1d..39bd7f76 100644 --- a/Dynamic_Programming/longest_common_subsequence_memoization.cpp +++ b/Dynamic Programming/longest_common_subsequence_memoization.cpp @@ -1,47 +1,47 @@ -// Memoization : Find Longest Common Subsequence of two string -// Program Author : Abhisek Kumar Gupta -/* - To avoid computation of subproblem many times we use memoization - here we take 2D array of size n*m memoization[n][m] - when length of lcs of first i characters of a and j characters of b is - computed for first time it is stored in cell memoization[i][j] - if function is called with n = i and m = j then LCS is not computed from scratch - and stored value is returned from table -*/ -#include -using namespace std; -const int maxi = 10000; -int get_max(int x, int y){ - return (x > y) ? x : y; -} -int find_longest_common_subsequence(string a, string b, int n, int m, int memoization[][maxi]){ - if(m == 0 || n == 0) - return 0; - // if value is already computed then return the value - if(memoization[n][m] != -1){ - return memoization[n][m]; - } - if(a[n - 1] == b[m - 1]){ - // memoize the solution in order to avoid recomputation - memoization[n][m] = 1 + find_longest_common_subsequence(a, b, n - 1, m - 1, memoization); - } - else{ - // memoize the solution in order to avoid recomputation - memoization[n][m] = get_max(find_longest_common_subsequence(a, b, n - 1, m, memoization), - find_longest_common_subsequence(a, b, n, m -1, memoization)); - } - return memoization[n][m]; -} -int main(){ - string a = "DABCEFIHKST"; - string b = "DCEFKAAAQST"; - int n = a.length(); - int m = b.length(); - int memoization[n+1][maxi]; - memset(memoization, -1, sizeof(memoization)); - int result = find_longest_common_subsequence(a, b, n, m, memoization); - cout << result; - return 0; -} -// Time complexity from exponential to polynomial O(N * M) +// Memoization : Find Longest Common Subsequence of two string +// Program Author : Abhisek Kumar Gupta +/* + To avoid computation of subproblem many times we use memoization + here we take 2D array of size n*m memoization[n][m] + when length of lcs of first i characters of a and j characters of b is + computed for first time it is stored in cell memoization[i][j] + if function is called with n = i and m = j then LCS is not computed from scratch + and stored value is returned from table +*/ +#include +using namespace std; +const int maxi = 10000; +int get_max(int x, int y){ + return (x > y) ? x : y; +} +int find_longest_common_subsequence(string a, string b, int n, int m, int memoization[][maxi]){ + if(m == 0 || n == 0) + return 0; + // if value is already computed then return the value + if(memoization[n][m] != -1){ + return memoization[n][m]; + } + if(a[n - 1] == b[m - 1]){ + // memoize the solution in order to avoid recomputation + memoization[n][m] = 1 + find_longest_common_subsequence(a, b, n - 1, m - 1, memoization); + } + else{ + // memoize the solution in order to avoid recomputation + memoization[n][m] = get_max(find_longest_common_subsequence(a, b, n - 1, m, memoization), + find_longest_common_subsequence(a, b, n, m -1, memoization)); + } + return memoization[n][m]; +} +int main(){ + string a = "DABCEFIHKST"; + string b = "DCEFKAAAQST"; + int n = a.length(); + int m = b.length(); + int memoization[n+1][maxi]; + memset(memoization, -1, sizeof(memoization)); + int result = find_longest_common_subsequence(a, b, n, m, memoization); + cout << result; + return 0; +} +// Time complexity from exponential to polynomial O(N * M) // Space complexity O(N * M) \ No newline at end of file diff --git a/Dynamic_Programming/longest_common_subsequence_recursive.cpp b/Dynamic Programming/longest_common_subsequence_recursive.cpp similarity index 97% rename from Dynamic_Programming/longest_common_subsequence_recursive.cpp rename to Dynamic Programming/longest_common_subsequence_recursive.cpp index a7097908..4991affa 100644 --- a/Dynamic_Programming/longest_common_subsequence_recursive.cpp +++ b/Dynamic Programming/longest_common_subsequence_recursive.cpp @@ -1,49 +1,49 @@ -// Recursion : Find Longest Common Subsequence of two string -// Program Author : Abhisek Kumar Gupta -/* - This problem exemplify optimal substructure property and the - bigger problem can be defined in terms of mini subprobs of same type - hence recursion -*/ - -#include -using namespace std; -int get_max(int x, int y){ - return (x > y) ? x : y; -} -int find_longest_common_subsequence(string a, string b, int n, int m){ - if(m == 0 || n == 0) - return 0; - // we start by comparing last two characters of strings and there are two - // possibilities - // 1) both are same [means we already have found one character in LCS so - // add 1 and make recursive call with modified strings] - if(a[n - 1] == b[m - 1]){ - return 1 + find_longest_common_subsequence(a, b, n-1, m-1); - } - // 2) both are different [find length of tow lcs, first haveing n-1 character from first string - // and m characters from second string, and another with m characters from first string and - // m - 1 characters from second string and return the max of two] - else{ - return get_max(find_longest_common_subsequence(a, b, n-1,m), - find_longest_common_subsequence(a, b, n, m-1)); - } -} -int main(){ - string a = "ABCEFIHST"; - string b = "DCEFKAAAQST"; - int n = a.length(); - int m = b.length(); - int result = find_longest_common_subsequence(a, b, n, m); - cout << result; -} -// Code takes exponential time 2^n in the worst case i.e when all -// two characters are different -// ACB | ABC -// ACB | AB AC | ABC -// AC | A A | AB -// A | A AC | A | A | AB -// BECAUSE WE ARE SOLVING ONE SUB PROBLEM MULTIPLE TIMES SO LCS PROBLEM -// DEMONSTRATES OPTIMAL SUBSTRUCTURE PROPERTY AND THERE ARE OVERLAPPING -// SUBPROBLEMS TOO, SO WE WILL USE MEMOIZATION / DP TO SOLVE THIS PROBLEM +// Recursion : Find Longest Common Subsequence of two string +// Program Author : Abhisek Kumar Gupta +/* + This problem exemplify optimal substructure property and the + bigger problem can be defined in terms of mini subprobs of same type + hence recursion +*/ + +#include +using namespace std; +int get_max(int x, int y){ + return (x > y) ? x : y; +} +int find_longest_common_subsequence(string a, string b, int n, int m){ + if(m == 0 || n == 0) + return 0; + // we start by comparing last two characters of strings and there are two + // possibilities + // 1) both are same [means we already have found one character in LCS so + // add 1 and make recursive call with modified strings] + if(a[n - 1] == b[m - 1]){ + return 1 + find_longest_common_subsequence(a, b, n-1, m-1); + } + // 2) both are different [find length of tow lcs, first haveing n-1 character from first string + // and m characters from second string, and another with m characters from first string and + // m - 1 characters from second string and return the max of two] + else{ + return get_max(find_longest_common_subsequence(a, b, n-1,m), + find_longest_common_subsequence(a, b, n, m-1)); + } +} +int main(){ + string a = "ABCEFIHST"; + string b = "DCEFKAAAQST"; + int n = a.length(); + int m = b.length(); + int result = find_longest_common_subsequence(a, b, n, m); + cout << result; +} +// Code takes exponential time 2^n in the worst case i.e when all +// two characters are different +// ACB | ABC +// ACB | AB AC | ABC +// AC | A A | AB +// A | A AC | A | A | AB +// BECAUSE WE ARE SOLVING ONE SUB PROBLEM MULTIPLE TIMES SO LCS PROBLEM +// DEMONSTRATES OPTIMAL SUBSTRUCTURE PROPERTY AND THERE ARE OVERLAPPING +// SUBPROBLEMS TOO, SO WE WILL USE MEMOIZATION / DP TO SOLVE THIS PROBLEM // OPTIMALLY \ No newline at end of file diff --git a/Dynamic_Programming/longest_increasing_subsequence.cpp b/Dynamic Programming/longest_increasing_subsequence.cpp similarity index 96% rename from Dynamic_Programming/longest_increasing_subsequence.cpp rename to Dynamic Programming/longest_increasing_subsequence.cpp index 3d806881..fa7bd5f1 100644 --- a/Dynamic_Programming/longest_increasing_subsequence.cpp +++ b/Dynamic Programming/longest_increasing_subsequence.cpp @@ -1,36 +1,36 @@ -/* - Longest Increasing Subsequence - Input : 1 2 1 3 1 4 - Output : 4 -*/ -// Dynamic Programming Approach : TC O(n^2) -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; - -int find_longest_increasing_subsequence(vector V, int n){ - int dp[1004]; - for(int i = 0; i < 1000; i++) dp[i] = 1; - int best = INT_MIN; - for(int i = 1; i < n; i++){ - for(int j = 0; j < i; j++){ - if(V[j] <= V[i]){ // this means its in increasing order and we can take value stored at dp[j] and add 1 to it - int curr_len = 1 + dp[j]; - dp[i] = max(curr_len, dp[i]); - } - } - best = max(dp[i], best); - } - return best; -} -int main(){ - int n; - cout << "Enter a number"; - cin >> n; - vector V(n); - for(int i = 0; i < n; i++){ - cin >> V[i]; - } - int result = find_longest_increasing_subsequence(V, n); - cout << result; -} +/* + Longest Increasing Subsequence + Input : 1 2 1 3 1 4 + Output : 4 +*/ +// Dynamic Programming Approach : TC O(n^2) +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; + +int find_longest_increasing_subsequence(vector V, int n){ + int dp[1004]; + for(int i = 0; i < 1000; i++) dp[i] = 1; + int best = INT_MIN; + for(int i = 1; i < n; i++){ + for(int j = 0; j < i; j++){ + if(V[j] <= V[i]){ // this means its in increasing order and we can take value stored at dp[j] and add 1 to it + int curr_len = 1 + dp[j]; + dp[i] = max(curr_len, dp[i]); + } + } + best = max(dp[i], best); + } + return best; +} +int main(){ + int n; + cout << "Enter a number"; + cin >> n; + vector V(n); + for(int i = 0; i < n; i++){ + cin >> V[i]; + } + int result = find_longest_increasing_subsequence(V, n); + cout << result; +} diff --git a/Dynamic Programming/longest_pallindromic_substring.cpp b/Dynamic Programming/longest_pallindromic_substring.cpp new file mode 100644 index 00000000..8b82976a --- /dev/null +++ b/Dynamic Programming/longest_pallindromic_substring.cpp @@ -0,0 +1,63 @@ +/* + Given a string s, return the longest palindromic substring in s. + + Example 1: + Input: s = "babad" + Output: "bab" + Explanation: "aba" is also a valid answer. + + Example 2: + Input: s = "cbbd" + Output: "bb" + + + Constraints: + 1 <= s.length <= 1000 + s consist of only digits and English letters. +*/ +#include +#include +#include + +// Function to find the length of the longest palindromic substring +int longestPalindromicSubstring(const std::string& Array) { + int n = Array.length(); + + // Create a 2D boolean array L to store whether substrings are palindromes. + std::vector> L(n, std::vector(n, false)); + + int max_length = 1; // Initialize the maximum palindrome length to 1 (single characters are palindromes) + + // Initialize the base cases for substrings of length 1 and 2. + for (int i = 0; i < n - 1; i++) { + L[i][i] = true; // Single characters are palindromes + if (Array[i] == Array[i + 1]) { + L[i][i + 1] = true; // Check for palindromes of length 2 + max_length = 2; // Update the maximum palindrome length + } + } + + // Check for palindromes of length 3 and greater. + for (int k = 3; k <= n; k++) { + for (int i = 0; i < n - k + 1; i++) { + int j = i + k - 1; + + // Check if the characters at the ends of the current substring match + // and if the substring inside is a palindrome. + if (Array[i] == Array[j] && L[i + 1][j - 1]) { + L[i][j] = true; // Mark the current substring as a palindrome + max_length = k; // Update the maximum palindrome length + } + } + } + + return max_length; // Return the length of the longest palindromic substring +} + +int main() { + // Example usage: + std::string input = "babad"; + int result = longestPalindromicSubstring(input); + std::cout << result << std::endl; // Output: 3 ("bab" or "aba" is the longest palindromic substring) + return 0; +} diff --git a/Dynamic Programming/longest_pallindromic_substring.go b/Dynamic Programming/longest_pallindromic_substring.go new file mode 100644 index 00000000..6995afc6 --- /dev/null +++ b/Dynamic Programming/longest_pallindromic_substring.go @@ -0,0 +1,66 @@ +/* + Given a string s, return the longest palindromic substring in s. + + Example 1: + Input: s = "babad" + Output: "bab" + Explanation: "aba" is also a valid answer. + + Example 2: + Input: s = "cbbd" + Output: "bb" + + + Constraints: + 1 <= s.length <= 1000 + s consist of only digits and English letters. +*/ +package main + +import ( + "fmt" +) + +// LongestPalindromicSubstring finds the length of the longest palindromic substring in a given string. +func LongestPalindromicSubstring(Array string) int { + n := len(Array) + + // Create a 2D boolean array L to store whether substrings are palindromes. + L := make([][]bool, n) + for i := range L { + L[i] = make([]bool, n) // Defaults to false + } + + max := 1 // Initialize the maximum palindrome length to 1 (single characters are palindromes) + + // Initialize the base cases for substrings of length 1 and 2. + for i := 0; i < n-1; i++ { + L[i][i] = true // Single characters are palindromes + if Array[i] == Array[i+1] { + L[i][i+1] = true // Check for palindromes of length 2 + max = 2 // Update the maximum palindrome length + } + } + + // Check for palindromes of length 3 and greater. + for k := 3; k <= n; k++ { + for i := 0; i < n-k+1; i++ { + j := i + k - 1 + + // Check if the characters at the ends of the current substring match and if the substring inside is a palindrome. + if Array[i] == Array[j] && L[i+1][j-1] { + L[i][j] = true // Mark the current substring as a palindrome + max = k // Update the maximum palindrome length + } else { + L[i][j] = false // Mark the current substring as not a palindrome + } + } + } + + return max // Return the length of the longest palindromic substring +} + +func main() { + // Example usage: + fmt.Print(LongestPalindromicSubstring("babad")) +} diff --git a/Dynamic Programming/longest_pallindromic_substring.java b/Dynamic Programming/longest_pallindromic_substring.java new file mode 100644 index 00000000..5f0e61d3 --- /dev/null +++ b/Dynamic Programming/longest_pallindromic_substring.java @@ -0,0 +1,60 @@ +/* + Given a string s, return the longest palindromic substring in s. + + Example 1: + Input: s = "babad" + Output: "bab" + Explanation: "aba" is also a valid answer. + + Example 2: + Input: s = "cbbd" + Output: "bb" + + + Constraints: + 1 <= s.length <= 1000 + s consist of only digits and English letters. +*/ +public class LongestPalindromicSubstring { + // Function to find the length of the longest palindromic substring + public static int longestPalindromicSubstring(String Array) { + int n = Array.length(); + + // Create a 2D boolean array L to store whether substrings are palindromes. + boolean[][] L = new boolean[n][n]; + + int max_length = 1; // Initialize the maximum palindrome length to 1 (single characters are palindromes) + + // Initialize the base cases for substrings of length 1 and 2. + for (int i = 0; i < n - 1; i++) { + L[i][i] = true; // Single characters are palindromes + if (Array.charAt(i) == Array.charAt(i + 1)) { + L[i][i + 1] = true; // Check for palindromes of length 2 + max_length = 2; // Update the maximum palindrome length + } + } + + // Check for palindromes of length 3 and greater. + for (int k = 3; k <= n; k++) { + for (int i = 0; i < n - k + 1; i++) { + int j = i + k - 1; + + // Check if the characters at the ends of the current substring match + // and if the substring inside is a palindrome. + if (Array.charAt(i) == Array.charAt(j) && L[i + 1][j - 1]) { + L[i][j] = true; // Mark the current substring as a palindrome + max_length = k; // Update the maximum palindrome length + } + } + } + + return max_length; // Return the length of the longest palindromic substring + } + + public static void main(String[] args) { + // Example usage: + String input = "babad"; + int result = longestPalindromicSubstring(input); + System.out.println(result); // Output: 3 ("bab" or "aba" is the longest palindromic substring) + } +} diff --git a/Dynamic Programming/longest_pallindromic_substring.js b/Dynamic Programming/longest_pallindromic_substring.js new file mode 100644 index 00000000..6e4d7310 --- /dev/null +++ b/Dynamic Programming/longest_pallindromic_substring.js @@ -0,0 +1,54 @@ +/* + Given a string s, return the longest palindromic substring in s. + + Example 1: + Input: s = "babad" + Output: "bab" + Explanation: "aba" is also a valid answer. + + Example 2: + Input: s = "cbbd" + Output: "bb" + + + Constraints: + 1 <= s.length <= 1000 + s consist of only digits and English letters. +*/ +function longestPalindromicSubstring(Array) { + const n = Array.length; + + // Create a 2D boolean array L to store whether substrings are palindromes. + const L = Array.from({ length: n }, () => Array(n).fill(false)); + + let max_length = 1; // Initialize the maximum palindrome length to 1 (single characters are palindromes) + + // Initialize the base cases for substrings of length 1 and 2. + for (let i = 0; i < n - 1; i++) { + L[i][i] = true; // Single characters are palindromes + if (Array[i] === Array[i + 1]) { + L[i][i + 1] = true; // Check for palindromes of length 2 + max_length = 2; // Update the maximum palindrome length + } + } + + // Check for palindromes of length 3 and greater. + for (let k = 3; k <= n; k++) { + for (let i = 0; i < n - k + 1; i++) { + const j = i + k - 1; + + // Check if the characters at the ends of the current substring match + // and if the substring inside is a palindrome. + if (Array[i] === Array[j] && L[i + 1][j - 1]) { + L[i][j] = true; // Mark the current substring as a palindrome + max_length = k; // Update the maximum palindrome length + } + } + } + + return max_length; // Return the length of the longest palindromic substring +} + +// Example usage: +const result = longestPalindromicSubstring("babad"); +console.log(result); // Output: 3 ("bab" or "aba" is the longest palindromic substring) diff --git a/Dynamic Programming/longest_pallindromic_substring.py b/Dynamic Programming/longest_pallindromic_substring.py new file mode 100644 index 00000000..ea203c50 --- /dev/null +++ b/Dynamic Programming/longest_pallindromic_substring.py @@ -0,0 +1,48 @@ +''' + Given a string s, return the longest palindromic substring in s. + + Example 1: + Input: s = "babad" + Output: "bab" + Explanation: "aba" is also a valid answer. + + Example 2: + Input: s = "cbbd" + Output: "bb" + + + Constraints: + 1 <= s.length <= 1000 + s consist of only digits and English letters. +''' +def longest_palindromic_substring(Array): + n = len(Array) + + # Create a 2D boolean array L to store whether substrings are palindromes. + L = [[False] * n for _ in range(n)] + + max_length = 1 # Initialize the maximum palindrome length to 1 (single characters are palindromes) + + # Initialize the base cases for substrings of length 1 and 2. + for i in range(n - 1): + L[i][i] = True # Single characters are palindromes + if Array[i] == Array[i + 1]: + L[i][i + 1] = True # Check for palindromes of length 2 + max_length = 2 # Update the maximum palindrome length + + # Check for palindromes of length 3 and greater. + for k in range(3, n + 1): + for i in range(n - k + 1): + j = i + k - 1 + + # Check if the characters at the ends of the current substring match + # and if the substring inside is a palindrome. + if Array[i] == Array[j] and L[i + 1][j - 1]: + L[i][j] = True # Mark the current substring as a palindrome + max_length = k # Update the maximum palindrome length + + return max_length # Return the length of the longest palindromic substring + +# Example usage: +result = longest_palindromic_substring("babad") +print(result) # Output: 3 ("bab" or "aba" is the longest palindromic substring) diff --git a/Dynamic Programming/max_path_sum.go b/Dynamic Programming/max_path_sum.go new file mode 100644 index 00000000..a8029b6f --- /dev/null +++ b/Dynamic Programming/max_path_sum.go @@ -0,0 +1,83 @@ +/// How would you approach finding the maximum path sum in a binary tree using dynamic programming in Go? +// Provide a sample input and output, explain your approach using comments, +// and analyze the time and space complexity of your solution. + +// sample input: 1 +// / \ +// 2 3 +// sample output: Maximum Path Sum: 6 + +/// Explanation : +// 1. Implement a function that calculates the maximum path sum in a binary tree using dynamic programming. +// 2. Create a struct treeNode to represent each node in the binary tree, including a Val field for the node's value, Left field for the left child, and Right field for the right child. +// 3. Define a helper function pathMaxSum that takes the root node as input and returns the maximum path sum. +// 4 Within maxPathSum, initialize a variable maxSum with the minimum integer value to track the maximum path sum. +// 5. Call the recursive helper function findMaxSum and pass the root node and the maxSum variable as arguments. +// 6. In the findMaxSum function, handle the base case: if the node is nil, return 0. +// 7. Recursively find the maximum path sum for the left and right subtrees by calling findMaxSum on the left and right children of the current node. +// 8. Calculate the maximum path sum that includes the current node: + // Check if the left sum is negative (less than 0), assign it as 0. + // Check if the right sum is negative (less than 0), assign it as 0. + // Update the maxSum by comparing it with the sum of the current node's value, left sum, and right sum.// +// 9. Return the maximum path sum including the current node (either the current node's value plus the maximum of left or right sum). +// 10. Finally, return the maxSum from the pathMaxSum function. +// 11. In the main function, create a sample binary tree and call pathMaxSum to find the maximum path sum. Print the result. + +// Time Complexity: The time complexity of this solution is O(N), where N is the number of nodes in the binary tree, as we need to traverse each node once. + +// Space Complexity: The space complexity is O(H), where H is the height of the binary tree. This is due to the recursive calls on the stack, which can go up to the height of the tree. + + +package main + +import ( + "fmt" + "math" +) + +type treenode struct { + Val int + Left *treenode + Right *treenode +} + +// Function to find the maximum path sum in a binary tree +func pathMaxSum(root *treenode) int{ + maxSum := math.MinInt32 + findMaxSum(root, &maxSum) + return maxSum +} + +// finding the maximum path sum starting from a given node +func findMaxSum(node *treenode, maxSum *int) int { + if node == nil { + return 0 + } + + leftSum := max(0, findMaxSum(node.Left,maxSum)) + rightSum := max(0,findMaxSum(node.Right,maxSum)) + + *maxSum = max(*maxSum,node.Val+leftSum+rightSum) + + // returning the max sum plus the current node + return node.Val + max(leftSum,rightSum) +} + +// function to find the maximum of two numbers +func max(a,b int) int { + if a >b { + return a + } + return b +} + +func main() { + // creating sample binary tree + root := &treenode{Val: 1} + root.Left = &treenode{Val: 2} + root.Right = &treenode{Val: 3} + + // Calculate the maximum path sum + maxSum := pathMaxSum(root) + fmt.Println("Maximum Path Sum:", maxSum) +} \ No newline at end of file diff --git a/Dynamic Programming/max_sum_increasing_subsequence.cpp b/Dynamic Programming/max_sum_increasing_subsequence.cpp new file mode 100644 index 00000000..3a093b0f --- /dev/null +++ b/Dynamic Programming/max_sum_increasing_subsequence.cpp @@ -0,0 +1,63 @@ +/* + Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated + from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. + + Sample Input: = [10, 70, 20, 30, 50, 11, 30] + Output : [110, [10, 20, 30, 50]] + + Explanation: + The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. + + Here's a step-by-step explanation of the code: + + 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. + + 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. + + 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. + + 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. + + 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. + + 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. + + 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. + + 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). + + O(n^2) time | O(n) space - where n is the length of the input array +*/ + +#include +#include + +std::pair> MaxSumIncreasingSubsequence(std::vector& array) { + int n = array.size(); + std::vector sums(n); // Store the maximum increasing sum up to index i. + std::vector sequences(n, -1); // Store the previous index of the increasing subsequence. + + for (int i = 0; i < n; i++) { + sums[i] = array[i]; + for (int j = 0; j < i; j++) { + if (array[i] > array[j] && sums[j] + array[i] > sums[i]) { + sums[i] = sums[j] + array[i]; + sequences[i] = j; + } + } + } + + // Find the index of the maximum sum in 'sums'. + int maxSumIndex = std::max_element(sums.begin(), sums.end()) - sums.begin(); + int maxSum = sums[maxSumIndex]; + + // Build the increasing subsequence using the 'sequences' array. + std::vector sequence; + while (maxSumIndex != -1) { + sequence.push_back(array[maxSumIndex]); + maxSumIndex = sequences[maxSumIndex]; + } + std::reverse(sequence.begin(), sequence.end()); + + return std::make_pair(maxSum, sequence); +} diff --git a/Dynamic Programming/max_sum_increasing_subsequence.go b/Dynamic Programming/max_sum_increasing_subsequence.go new file mode 100644 index 00000000..6e0cb129 --- /dev/null +++ b/Dynamic Programming/max_sum_increasing_subsequence.go @@ -0,0 +1,98 @@ +/* + Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated + from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. + + Sample Input: = [10, 70, 20, 30, 50, 11, 30] + Output : [110, [10, 20, 30, 50]] + + Explanation: + The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. + + Here's a step-by-step explanation of the code: + + 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. + + 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. + + 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. + + 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. + + 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. + + 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. + + 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. + + 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). + + O(n^2) time | O(n) space - where n is the length of the input array +*/ +package main + +import "math" + +// MaxSumIncreasingSubsequence finds the maximum sum increasing subsequence in the given array of integers. +func MaxSumIncreasingSubsequence(array []int) (int, []int) { + // Initialize two arrays to store maximum sums and the previous elements contributing to the sums. + sums := make([]int, len(array)) + sequences := make([]int, len(array)) + + // Initialize each element in 'sums' array to its corresponding element in the input array. + // Also, initialize each element in 'sequences' array to a sentinel value 'math.MinInt32'. + for i := range sequences { + sequences[i] = math.MinInt32 + sums[i] = array[i] + } + + // Variable to keep track of the index with the maximum sum. + maxSumIndex := 0 + + // Iterate through the input array and calculate the maximum sum increasing subsequences. + for i, currNum := range array { + for j := 0; j < i; j++ { + otherNum := array[j] + if otherNum < currNum && currNum+sums[j] >= sums[i] { + // If the current element can extend the increasing subsequence with a better sum, + // update the 'sums' and 'sequences' arrays accordingly. + sums[i] = currNum + sums[j] + sequences[i] = j + } + } + + // Update the index with the maximum sum if the current sum is greater. + if sums[i] > sums[maxSumIndex] { + maxSumIndex = i + } + } + + // Get the maximum sum from the 'sums' array and the increasing subsequence from the 'sequences' array. + sum := sums[maxSumIndex] + sequence := buildSequence(array, sequences, maxSumIndex) + return sum, sequence +} + +// buildSequence reconstructs the increasing subsequence from the 'sequences' array. +func buildSequence(array []int, sequences []int, index int) []int { + sequence := []int{} + + // Traverse the 'sequences' array starting from the 'index' until reaching the sentinel value 'math.MinInt32'. + for index != math.MinInt32 { + // Add the element at the current index to the 'sequence' array. + sequence = append(sequence, array[index]) + + // Move to the previous index using 'sequences' array. + index = sequences[index] + } + + // Reverse the 'sequence' array since it was built backward. + reverse(sequence) + return sequence +} + +// reverse is a helper function used to reverse the elements in the 'sequence' array. +func reverse(numbers []int) { + for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 { + numbers[i], numbers[j] = numbers[j], numbers[i] + } +} diff --git a/Dynamic Programming/max_sum_increasing_subsequence.java b/Dynamic Programming/max_sum_increasing_subsequence.java new file mode 100644 index 00000000..bbdeaedc --- /dev/null +++ b/Dynamic Programming/max_sum_increasing_subsequence.java @@ -0,0 +1,82 @@ +/* + Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated + from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. + + Sample Input: = [10, 70, 20, 30, 50, 11, 30] + Output : [110, [10, 20, 30, 50]] + + Explanation: + The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. + + Here's a step-by-step explanation of the code: + + 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. + + 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. + + 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. + + 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. + + 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. + + 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. + + 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. + + 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). + + O(n^2) time | O(n) space - where n is the length of the input array +*/ +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; + +public class Main { + public static int[] maxSumIncreasingSubsequence(int[] array) { + int n = array.length; + int[] sums = Arrays.copyOf(array, n); // Store the maximum increasing sum up to index i. + int[] sequences = new int[n]; + Arrays.fill(sequences, -1); // Store the previous index of the increasing subsequence. + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (array[i] > array[j] && sums[j] + array[i] > sums[i]) { + sums[i] = sums[j] + array[i]; + sequences[i] = j; + } + } + } + + // Find the index of the maximum sum in 'sums'. + int maxSumIndex = 0; + for (int i = 1; i < n; i++) { + if (sums[i] > sums[maxSumIndex]) { + maxSumIndex = i; + } + } + int maxSum = sums[maxSumIndex]; + + // Build the increasing subsequence using the 'sequences' array. + List sequenceList = new ArrayList<>(); + while (maxSumIndex != -1) { + sequenceList.add(array[maxSumIndex]); + maxSumIndex = sequences[maxSumIndex]; + } + + // Convert the list to an array and reverse it to get the correct order. + int[] sequence = new int[sequenceList.size()]; + for (int i = 0; i < sequence.length; i++) { + sequence[i] = sequenceList.get(sequenceList.size() - i - 1); + } + + return new int[]{maxSum, sequence}; + } + + public static void main(String[] args) { + int[] array = {4, 6, 1, 3, 8, 4, 6}; + int[] result = maxSumIncreasingSubsequence(array); + System.out.println("Max Sum: " + result[0]); + System.out.println("Increasing Subsequence: " + Arrays.toString(result[1])); + } +} diff --git a/Dynamic Programming/max_sum_increasing_subsequence.js b/Dynamic Programming/max_sum_increasing_subsequence.js new file mode 100644 index 00000000..2ffb0342 --- /dev/null +++ b/Dynamic Programming/max_sum_increasing_subsequence.js @@ -0,0 +1,64 @@ +/* + Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated + from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. + + Sample Input: = [10, 70, 20, 30, 50, 11, 30] + Output : [110, [10, 20, 30, 50]] + + Explanation: + The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. + + Here's a step-by-step explanation of the code: + + 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. + + 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. + + 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. + + 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. + + 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. + + 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. + + 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. + + 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). + + O(n^2) time | O(n) space - where n is the length of the input array +*/ + +function maxSumIncreasingSubsequence(array) { + const n = array.length; + const sums = [...array]; // Store the maximum increasing sum up to index i. + const sequences = new Array(n).fill(-1); // Store the previous index of the increasing subsequence. + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (array[i] > array[j] && sums[j] + array[i] > sums[i]) { + sums[i] = sums[j] + array[i]; + sequences[i] = j; + } + } + } + + // Find the index of the maximum sum in 'sums'. + let maxSumIndex = 0; + for (let i = 1; i < n; i++) { + if (sums[i] > sums[maxSumIndex]) { + maxSumIndex = i; + } + } + const maxSum = sums[maxSumIndex]; + + // Build the increasing subsequence using the 'sequences' array. + const sequence = []; + while (maxSumIndex !== -1) { + sequence.push(array[maxSumIndex]); + maxSumIndex = sequences[maxSumIndex]; + } + sequence.reverse(); + + return [maxSum, sequence]; +} diff --git a/Dynamic Programming/max_sum_increasing_subsequence.py b/Dynamic Programming/max_sum_increasing_subsequence.py new file mode 100644 index 00000000..bc3868c0 --- /dev/null +++ b/Dynamic Programming/max_sum_increasing_subsequence.py @@ -0,0 +1,53 @@ +''' + Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated + from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. + + Sample Input: = [10, 70, 20, 30, 50, 11, 30] + Output : [110, [10, 20, 30, 50]] + + Explanation: + The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. + + Here's a step-by-step explanation of the code: + + 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. + + 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. + + 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. + + 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. + + 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. + + 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. + + 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. + + 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). + + O(n^2) time | O(n) space - where n is the length of the input array +''' +def max_sum_increasing_subsequence(array): + n = len(array) + sums = [num for num in array] # Store the maximum increasing sum up to index i. + sequences = [-1] * n # Store the previous index of the increasing subsequence. + + for i in range(n): + for j in range(i): + if array[i] > array[j] and sums[j] + array[i] > sums[i]: + sums[i] = sums[j] + array[i] + sequences[i] = j + + # Find the index of the maximum sum in 'sums'. + max_sum_index = max(range(n), key=lambda i: sums[i]) + max_sum = sums[max_sum_index] + + # Build the increasing subsequence using the 'sequences' array. + sequence = [] + while max_sum_index != -1: + sequence.append(array[max_sum_index]) + max_sum_index = sequences[max_sum_index] + sequence.reverse() + + return max_sum, sequence diff --git a/Dynamic Programming/maximal_sqaure.cpp b/Dynamic Programming/maximal_sqaure.cpp new file mode 100644 index 00000000..51cc44fc --- /dev/null +++ b/Dynamic Programming/maximal_sqaure.cpp @@ -0,0 +1,120 @@ +/* + -> The maximalSquare function takes a matrix as input and returns the area of the maximal square found in the matrix. + -> The function first checks if the matrix is empty (no rows or columns). In such cases, there can't be any squares, + so it returns 0. + -> Next, it initializes some variables. rows stores the number of rows in the matrix, cols stores the number of + columns, and maxSide keeps track of the maximum side length of the square encountered so far. + -> It creates a new matrix called dp using the Array.from method. This matrix will store the side lengths of squares. + -> The next step is to initialize the first row and the first column of dp with the values from the input matrix. It + iterates over each element in the first row and sets the corresponding element in dp to either 0 or 1, depending on + whether the element in the input matrix is '0' or '1'. It also updates maxSide accordingly. + -> Similarly, it iterates over each element in the first column and sets the corresponding element in dp to either + 0 or 1, based on the input matrix. Again, maxSide is updated. + -> Now, the function enters a nested loop to iterate over the remaining elements of the matrix, starting from the + second row and the second column. For each element at position (i, j), it checks if the corresponding element in the + input matrix is '1'. + -> If the element is '1', it calculates the value of dp[i][j] by taking the minimum of the three adjacent elements: + dp[i-1][j] (above), dp[i][j-1] (left), and dp[i-1][j-1] (diagonally above-left). It adds 1 to the minimum value and + assigns it to dp[i][j]. Additionally, it updates maxSide if the current dp[i][j] value is greater. + -> After the nested loop completes, the function has calculated the side lengths of squares for all positions in the + matrix. It returns the area of the maximal square by squaring the value of maxSide. + -> Finally, outside the function, an example usage is shown. The matrix variable represents a 2D array with 0s and 1s. + The maximalSquare function is called with this matrix, and the returned result is logged to the console. In this + example, the maximal square in the matrix has a side length of 2, so the output is 4 (2 * 2). + +The code utilizes dynamic programming to efficiently calculate the maximal square in the given matrix by storing the +side lengths of squares in a separate matrix. By using previously calculated values, the algorithm avoids redundant +calculations and improves performance. + +//Time complexity + +The time complexity of the provided maximal square algorithm is O(m * n), where m is the number of rows +in the matrix and n is the number of columns. This is because we iterate over each element in the matrix +once to calculate the side lengths of squares. The nested loops contribute to the linear time complexity. + +The space complexity is O(m * n) as well. We create an additional matrix, dp, with the same dimensions as +the input matrix to store the side lengths of squares. Hence, the space required is proportional to the +number of elements in the matrix. + +In summary, the time complexity and space complexity of the maximal square algorithm are both O(m * n), +where m is the number of rows and n is the number of columns in the matrix. +*/ + + +/* + +Example 1: +Input: + +1 0 1 0 0 +1 0 1 1 1 +1 1 1 1 1 +1 0 0 1 0 + +Output: 4 +The maximal square in the matrix is a 2x2 square with the top-left corner at position (1, 2) and the bottom-right corner at position (2, 3). The area of this square is 4. + +Example 2: +Input: +0 1 1 1 1 +1 1 0 1 0 +0 1 1 1 1 +1 1 1 1 0 + + +Output: 9 +The maximal square in the matrix is a 3x3 square with the top-left corner at position (0, 1) and the bottom-right corner at position (2, 3). The area of this square is 9. + +Example 3: +Input: + +0 0 0 +0 0 0 +0 0 0 + +Output: 0 +There are no squares with side length greater than 0 in the matrix. Therefore, the output is 0. + +*/ +#include +#include +#include + +int maximalSquare(std::vector>& matrix) { + if (matrix.empty() || matrix[0].empty()) { + return 0; + } + + int rows = matrix.size(); + int cols = matrix[0].size(); + int maxSide = 0; + + // Create a new matrix to store the side lengths of squares + std::vector> dp(rows, std::vector(cols, 0)); + + // Initialize the first row of the dp matrix + for (int i = 0; i < rows; i++) { + dp[i][0] = matrix[i][0] - '0'; + maxSide = std::max(maxSide, dp[i][0]); + } + + // Initialize the first column of the dp matrix + for (int j = 0; j < cols; j++) { + dp[0][j] = matrix[0][j] - '0'; + maxSide = std::max(maxSide, dp[0][j]); + } + + // Iterate over the remaining elements of the matrix + for (int i = 1; i < rows; i++) { + for (int j = 1; j < cols; j++) { + if (matrix[i][j] == '1') { + // Calculate the minimum of the three adjacent squares and add 1 + dp[i][j] = std::min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}) + 1; + maxSide = std::max(maxSide, dp[i][j]); + } + } + } + + // Return the area of the maximal square + return maxSide * maxSide; +} \ No newline at end of file diff --git a/Dynamic Programming/maximal_sqaure.go b/Dynamic Programming/maximal_sqaure.go new file mode 100644 index 00000000..c744e63e --- /dev/null +++ b/Dynamic Programming/maximal_sqaure.go @@ -0,0 +1,141 @@ +/* + -> The maximalSquare function takes a matrix as input and returns the area of the maximal square found in the matrix. + -> The function first checks if the matrix is empty (no rows or columns). In such cases, there can't be any squares, + so it returns 0. + -> Next, it initializes some variables. rows stores the number of rows in the matrix, cols stores the number of + columns, and maxSide keeps track of the maximum side length of the square encountered so far. + -> It creates a new matrix called dp using the Array.from method. This matrix will store the side lengths of squares. + -> The next step is to initialize the first row and the first column of dp with the values from the input matrix. It + iterates over each element in the first row and sets the corresponding element in dp to either 0 or 1, depending on + whether the element in the input matrix is '0' or '1'. It also updates maxSide accordingly. + -> Similarly, it iterates over each element in the first column and sets the corresponding element in dp to either + 0 or 1, based on the input matrix. Again, maxSide is updated. + -> Now, the function enters a nested loop to iterate over the remaining elements of the matrix, starting from the + second row and the second column. For each element at position (i, j), it checks if the corresponding element in the + input matrix is '1'. + -> If the element is '1', it calculates the value of dp[i][j] by taking the minimum of the three adjacent elements: + dp[i-1][j] (above), dp[i][j-1] (left), and dp[i-1][j-1] (diagonally above-left). It adds 1 to the minimum value and + assigns it to dp[i][j]. Additionally, it updates maxSide if the current dp[i][j] value is greater. + -> After the nested loop completes, the function has calculated the side lengths of squares for all positions in the + matrix. It returns the area of the maximal square by squaring the value of maxSide. + -> Finally, outside the function, an example usage is shown. The matrix variable represents a 2D array with 0s and 1s. + The maximalSquare function is called with this matrix, and the returned result is logged to the console. In this + example, the maximal square in the matrix has a side length of 2, so the output is 4 (2 * 2). + +The code utilizes dynamic programming to efficiently calculate the maximal square in the given matrix by storing the +side lengths of squares in a separate matrix. By using previously calculated values, the algorithm avoids redundant +calculations and improves performance. + +//Time complexity + +The time complexity of the provided maximal square algorithm is O(m * n), where m is the number of rows +in the matrix and n is the number of columns. This is because we iterate over each element in the matrix +once to calculate the side lengths of squares. The nested loops contribute to the linear time complexity. + +The space complexity is O(m * n) as well. We create an additional matrix, dp, with the same dimensions as +the input matrix to store the side lengths of squares. Hence, the space required is proportional to the +number of elements in the matrix. + +In summary, the time complexity and space complexity of the maximal square algorithm are both O(m * n), +where m is the number of rows and n is the number of columns in the matrix. +*/ + + +/* + +Example 1: +Input: + +1 0 1 0 0 +1 0 1 1 1 +1 1 1 1 1 +1 0 0 1 0 + +Output: 4 +The maximal square in the matrix is a 2x2 square with the top-left corner at position (1, 2) and the bottom-right corner at position (2, 3). The area of this square is 4. + +Example 2: +Input: +0 1 1 1 1 +1 1 0 1 0 +0 1 1 1 1 +1 1 1 1 0 + + +Output: 9 +The maximal square in the matrix is a 3x3 square with the top-left corner at position (0, 1) and the bottom-right corner at position (2, 3). The area of this square is 9. + +Example 3: +Input: + +0 0 0 +0 0 0 +0 0 0 + +Output: 0 +There are no squares with side length greater than 0 in the matrix. Therefore, the output is 0. + +*/ + + +package main + +import ( + "fmt" +) + +func maximalSquare(matrix [][]byte) int { + if len(matrix) == 0 || len(matrix[0]) == 0 { + return 0 + } + + rows := len(matrix) + cols := len(matrix[0]) + maxSide := 0 + + // Create a new matrix to store the side lengths of squares + dp := make([][]int, rows) + for i := range dp { + dp[i] = make([]int, cols) + } + + // Initialize the first row of the dp matrix + for i := 0; i < rows; i++ { + dp[i][0] = int(matrix[i][0] - '0') + maxSide = max(maxSide, dp[i][0]) + } + + // Initialize the first column of the dp matrix + for j := 0; j < cols; j++ { + dp[0][j] = int(matrix[0][j] - '0') + maxSide = max(maxSide, dp[0][j]) + } + + // Iterate over the remaining elements of the matrix + for i := 1; i < rows; i++ { + for j := 1; j < cols; j++ { + if matrix[i][j] == '1' { + // Calculate the minimum of the three adjacent squares and add 1 + dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1 + maxSide = max(maxSide, dp[i][j]) + } + } + } + + // Return the area of the maximal square + return maxSide * maxSide +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/Dynamic Programming/maximal_sqaure.js b/Dynamic Programming/maximal_sqaure.js new file mode 100644 index 00000000..70835f46 --- /dev/null +++ b/Dynamic Programming/maximal_sqaure.js @@ -0,0 +1,117 @@ +/* + -> The maximalSquare function takes a matrix as input and returns the area of the maximal square found in the matrix. + -> The function first checks if the matrix is empty (no rows or columns). In such cases, there can't be any squares, + so it returns 0. + -> Next, it initializes some variables. rows stores the number of rows in the matrix, cols stores the number of + columns, and maxSide keeps track of the maximum side length of the square encountered so far. + -> It creates a new matrix called dp using the Array.from method. This matrix will store the side lengths of squares. + -> The next step is to initialize the first row and the first column of dp with the values from the input matrix. It + iterates over each element in the first row and sets the corresponding element in dp to either 0 or 1, depending on + whether the element in the input matrix is '0' or '1'. It also updates maxSide accordingly. + -> Similarly, it iterates over each element in the first column and sets the corresponding element in dp to either + 0 or 1, based on the input matrix. Again, maxSide is updated. + -> Now, the function enters a nested loop to iterate over the remaining elements of the matrix, starting from the + second row and the second column. For each element at position (i, j), it checks if the corresponding element in the + input matrix is '1'. + -> If the element is '1', it calculates the value of dp[i][j] by taking the minimum of the three adjacent elements: + dp[i-1][j] (above), dp[i][j-1] (left), and dp[i-1][j-1] (diagonally above-left). It adds 1 to the minimum value and + assigns it to dp[i][j]. Additionally, it updates maxSide if the current dp[i][j] value is greater. + -> After the nested loop completes, the function has calculated the side lengths of squares for all positions in the + matrix. It returns the area of the maximal square by squaring the value of maxSide. + -> Finally, outside the function, an example usage is shown. The matrix variable represents a 2D array with 0s and 1s. + The maximalSquare function is called with this matrix, and the returned result is logged to the console. In this + example, the maximal square in the matrix has a side length of 2, so the output is 4 (2 * 2). + +The code utilizes dynamic programming to efficiently calculate the maximal square in the given matrix by storing the +side lengths of squares in a separate matrix. By using previously calculated values, the algorithm avoids redundant +calculations and improves performance. + +//Time complexity + +The time complexity of the provided maximal square algorithm is O(m * n), where m is the number of rows +in the matrix and n is the number of columns. This is because we iterate over each element in the matrix +once to calculate the side lengths of squares. The nested loops contribute to the linear time complexity. + +The space complexity is O(m * n) as well. We create an additional matrix, dp, with the same dimensions as +the input matrix to store the side lengths of squares. Hence, the space required is proportional to the +number of elements in the matrix. + +In summary, the time complexity and space complexity of the maximal square algorithm are both O(m * n), +where m is the number of rows and n is the number of columns in the matrix. +*/ + +/* + +Example 1: +Input: + +1 0 1 0 0 +1 0 1 1 1 +1 1 1 1 1 +1 0 0 1 0 + +Output: 4 +The maximal square in the matrix is a 2x2 square with the top-left corner at position (1, 2) and the bottom-right corner at position (2, 3). The area of this square is 4. + +Example 2: +Input: +0 1 1 1 1 +1 1 0 1 0 +0 1 1 1 1 +1 1 1 1 0 + + +Output: 9 +The maximal square in the matrix is a 3x3 square with the top-left corner at position (0, 1) and the bottom-right corner at position (2, 3). The area of this square is 9. + +Example 3: +Input: + +0 0 0 +0 0 0 +0 0 0 + +Output: 0 +There are no squares with side length greater than 0 in the matrix. Therefore, the output is 0. + +*/ + +function maximalSquare(matrix) { + if (matrix.length === 0 || matrix[0].length === 0) { + return 0; + } + + const rows = matrix.length; + const cols = matrix[0].length; + let maxSide = 0; + + // Create a new matrix to store the side lengths of squares + const dp = Array.from({ length: rows }, () => Array(cols).fill(0)); + + // Initialize the first row of the dp matrix + for (let i = 0; i < rows; i++) { + dp[i][0] = Number(matrix[i][0]); + maxSide = Math.max(maxSide, dp[i][0]); + } + + // Initialize the first column of the dp matrix + for (let j = 0; j < cols; j++) { + dp[0][j] = Number(matrix[0][j]); + maxSide = Math.max(maxSide, dp[0][j]); + } + + // Iterate over the remaining elements of the matrix + for (let i = 1; i < rows; i++) { + for (let j = 1; j < cols; j++) { + if (matrix[i][j] === '1') { + // Calculate the minimum of the three adjacent squares and add 1 + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1; + maxSide = Math.max(maxSide, dp[i][j]); + } + } + } + + // Return the area of the maximal square + return maxSide * maxSide; +} + diff --git a/Dynamic Programming/maximal_sqaure.py b/Dynamic Programming/maximal_sqaure.py new file mode 100644 index 00000000..3c079957 --- /dev/null +++ b/Dynamic Programming/maximal_sqaure.py @@ -0,0 +1,110 @@ +''' + -> The maximalSquare function takes a matrix as input and returns the area of the maximal square found in the matrix. + -> The function first checks if the matrix is empty (no rows or columns). In such cases, there can't be any squares, + so it returns 0. + -> Next, it initializes some variables. rows stores the number of rows in the matrix, cols stores the number of + columns, and maxSide keeps track of the maximum side length of the square encountered so far. + -> It creates a new matrix called dp using the Array.from method. This matrix will store the side lengths of squares. + -> The next step is to initialize the first row and the first column of dp with the values from the input matrix. It + iterates over each element in the first row and sets the corresponding element in dp to either 0 or 1, depending on + whether the element in the input matrix is '0' or '1'. It also updates maxSide accordingly. + -> Similarly, it iterates over each element in the first column and sets the corresponding element in dp to either + 0 or 1, based on the input matrix. Again, maxSide is updated. + -> Now, the function enters a nested loop to iterate over the remaining elements of the matrix, starting from the + second row and the second column. For each element at position (i, j), it checks if the corresponding element in the + input matrix is '1'. + -> If the element is '1', it calculates the value of dp[i][j] by taking the minimum of the three adjacent elements: + dp[i-1][j] (above), dp[i][j-1] (left), and dp[i-1][j-1] (diagonally above-left). It adds 1 to the minimum value and + assigns it to dp[i][j]. Additionally, it updates maxSide if the current dp[i][j] value is greater. + -> After the nested loop completes, the function has calculated the side lengths of squares for all positions in the + matrix. It returns the area of the maximal square by squaring the value of maxSide. + -> Finally, outside the function, an example usage is shown. The matrix variable represents a 2D array with 0s and 1s. + The maximalSquare function is called with this matrix, and the returned result is logged to the console. In this + example, the maximal square in the matrix has a side length of 2, so the output is 4 (2 * 2). + +The code utilizes dynamic programming to efficiently calculate the maximal square in the given matrix by storing the +side lengths of squares in a separate matrix. By using previously calculated values, the algorithm avoids redundant +calculations and improves performance. + +//Time complexity + +The time complexity of the provided maximal square algorithm is O(m * n), where m is the number of rows +in the matrix and n is the number of columns. This is because we iterate over each element in the matrix +once to calculate the side lengths of squares. The nested loops contribute to the linear time complexity. + +The space complexity is O(m * n) as well. We create an additional matrix, dp, with the same dimensions as +the input matrix to store the side lengths of squares. Hence, the space required is proportional to the +number of elements in the matrix. + +In summary, the time complexity and space complexity of the maximal square algorithm are both O(m * n), +where m is the number of rows and n is the number of columns in the matrix. +''' + + +''' + +Example 1: +Input: + +1 0 1 0 0 +1 0 1 1 1 +1 1 1 1 1 +1 0 0 1 0 + +Output: 4 +The maximal square in the matrix is a 2x2 square with the top-left corner at position (1, 2) and the bottom-right corner at position (2, 3). The area of this square is 4. + +Example 2: +Input: +0 1 1 1 1 +1 1 0 1 0 +0 1 1 1 1 +1 1 1 1 0 + + +Output: 9 +The maximal square in the matrix is a 3x3 square with the top-left corner at position (0, 1) and the bottom-right corner at position (2, 3). The area of this square is 9. + +Example 3: +Input: + +0 0 0 +0 0 0 +0 0 0 + +Output: 0 +There are no squares with side length greater than 0 in the matrix. Therefore, the output is 0. + +''' + +def maximalSquare(matrix): + if len(matrix) == 0 or len(matrix[0]) == 0: + return 0 + + rows = len(matrix) + cols = len(matrix[0]) + maxSide = 0 + + # Create a new matrix to store the side lengths of squares + dp = [[0] * cols for _ in range(rows)] + + # Initialize the first row of the dp matrix + for i in range(rows): + dp[i][0] = int(matrix[i][0]) + maxSide = max(maxSide, dp[i][0]) + + # Initialize the first column of the dp matrix + for j in range(cols): + dp[0][j] = int(matrix[0][j]) + maxSide = max(maxSide, dp[0][j]) + + # Iterate over the remaining elements of the matrix + for i in range(1, rows): + for j in range(1, cols): + if matrix[i][j] == '1': + # Calculate the minimum of the three adjacent squares and add 1 + dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1 + maxSide = max(maxSide, dp[i][j]) + + # Return the area of the maximal square + return maxSide * maxSide diff --git a/Dynamic Programming/maximal_square.java b/Dynamic Programming/maximal_square.java new file mode 100644 index 00000000..dd36c4ae --- /dev/null +++ b/Dynamic Programming/maximal_square.java @@ -0,0 +1,57 @@ +/* +Problem: +Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area. + +Approach: +1. Initialize a variable `maxSide` to 0 to store the maximum side length of the square. +2. Check if the given matrix is empty, if so, return 0. +3. Create a dynamic programming array `dp` with dimensions (rows + 1) x (cols + 1), where `rows` and `cols` are the dimensions of the matrix. +4. Traverse through the matrix starting from index (1, 1) to (rows, cols). +5. For each cell, if the value is '1', calculate the side length of the square by taking the minimum of the values from the top, left, and diagonal cells in the `dp` array, and add 1. +6. Update the `maxSide` variable with the maximum side length found so far. +7. Finally, return the area of the largest square by multiplying `maxSide` with itself. + +Time Complexity: O(m*n), where m is the number of rows and n is the number of columns in the matrix. + +Space Complexity: O(m*n), as we use an additional dp array of the same dimensions as the matrix. + +Sample Input: +char[][] matrix = { + {'1', '0', '1', '0', '0'}, + {'1', '0', '1', '1', '1'}, + {'1', '1', '1', '1', '1'}, + {'1', '0', '0', '1', '0'} +}; + +Sample Output: +4 + +Note: In the given sample input, the largest square containing only 1's has a side length of 2, so the area is 4. +*/ + + +public class LargestSquare { + public int maximalSquare(char[][] matrix) { + int maxSide = 0; // variable to store the maximum side length of the square + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return maxSide; // return 0 if the matrix is empty + } + + int rows = matrix.length; + int cols = matrix[0].length; + int[][] dp = new int[rows + 1][cols + 1]; // create a dynamic programming array to store the side length of the square + + for (int i = 1; i <= rows; i++) { + for (int j = 1; j <= cols; j++) { + if (matrix[i-1][j-1] == '1') { // if current cell is 1 + dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1; // calculate the side length of the square based on top, left, and diagonal cells + maxSide = Math.max(maxSide, dp[i][j]); // update the maximum side length + } + } + } + + return maxSide * maxSide; // return the area of the largest square + } +} + + diff --git a/Dynamic_Programming/min_cost_travel_in_a_grid.cpp b/Dynamic Programming/min_cost_travel_in_a_grid.cpp similarity index 96% rename from Dynamic_Programming/min_cost_travel_in_a_grid.cpp rename to Dynamic Programming/min_cost_travel_in_a_grid.cpp index 1e64e4af..2a3038b9 100644 --- a/Dynamic_Programming/min_cost_travel_in_a_grid.cpp +++ b/Dynamic Programming/min_cost_travel_in_a_grid.cpp @@ -1,53 +1,53 @@ -/* - Given a cost matrix cost[][] and a position (m, n) in cost[][], - write a function that returns cost of minimum cost path to reach (m, n) from (0, 0). - Each cell of the matrix represents a cost to traverse through that cell. - The total cost of a path to reach (m, n) is the sum of all the costs on that path - (including both source and destination). You can only traverse down and right - from a given cell, i.e., from a given cell (i, j), cells - (i+1, j), (i, j+1) can be traversed. You may assume that all - costs are positive integers. - Input : {1, 2, 3, 4}, - {5, 6, 7, 8}, - {9, 10, 11, 12} - Output : 30 [Path : 1->2->3->4->8->12] -*/ -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -int dp[100][100] = {}; -int min_cost_path_in_a_grid(int grid[][100], int m, int n){ - - dp[0][0] = grid[0][0]; - //row - for(int i = 1; i < m; i++){ - dp[i][0] = grid[i][0] + dp[i - 1][0]; - } - //col - for(int i = 1; i < n; i++){ - dp[0][i] = grid[0][i] + dp[0][i - 1]; - } - - for(int i = 1; i < m; i++){ - for(int j = 1; j < n; j++){ - dp[i][j] = grid[i][j] + min(dp[i - 1][j], dp[i][j - 1]); - } - } - return dp[m-1][n-1]; -} -int main(){ - int grid[100][100] = { - {1, 2, 3, 4}, - {5, 6, 7, 8}, - {9, 10, 11, 12} - }; - int m = 3, n = 4; - int result = min_cost_path_in_a_grid(grid, m, n); - for(int i = 0; i < m; i++){ - for(int j = 0; j < n; j++){ - cout << setw(5) <2->3->4->8->12] +*/ +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +int dp[100][100] = {}; +int min_cost_path_in_a_grid(int grid[][100], int m, int n){ + + dp[0][0] = grid[0][0]; + //row + for(int i = 1; i < m; i++){ + dp[i][0] = grid[i][0] + dp[i - 1][0]; + } + //col + for(int i = 1; i < n; i++){ + dp[0][i] = grid[0][i] + dp[0][i - 1]; + } + + for(int i = 1; i < m; i++){ + for(int j = 1; j < n; j++){ + dp[i][j] = grid[i][j] + min(dp[i - 1][j], dp[i][j - 1]); + } + } + return dp[m-1][n-1]; +} +int main(){ + int grid[100][100] = { + {1, 2, 3, 4}, + {5, 6, 7, 8}, + {9, 10, 11, 12} + }; + int m = 3, n = 4; + int result = min_cost_path_in_a_grid(grid, m, n); + for(int i = 0; i < m; i++){ + for(int j = 0; j < n; j++){ + cout << setw(5) < "abcdecb", resulting in a palindrome. + + Approach: + We can solve this problem using dynamic programming. + Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. + If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. + Otherwise, we have two options: + 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. + 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. + We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. + Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. + + Time complexity: O(n^2) + Space complexity: O(n^2) +*/ + +public class PalindromeInsertion { + public static int findMinInsertions(String s) { + int n = s.length(); + int[][] dp = new int[n][n]; + + for (int len = 2; len <= n; len++) { + for (int i = 0; i <= n - len; i++) { + int j = i + len - 1; + if (s.charAt(i) == s.charAt(j)) + dp[i][j] = dp[i + 1][j - 1]; + else + dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1; + } + } + + return dp[0][n - 1]; + } + + public static void main(String[] args) { + String input = "abcde"; + int minInsertions = findMinInsertions(input); + System.out.println("Minimum insertions required: " + minInsertions); + } +} diff --git a/Dynamic Programming/min_number_of_jumps.cpp b/Dynamic Programming/min_number_of_jumps.cpp new file mode 100644 index 00000000..1fd064b6 --- /dev/null +++ b/Dynamic Programming/min_number_of_jumps.cpp @@ -0,0 +1,77 @@ +/* + The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. + Each element in the array represents the maximum distance you can jump from that position. + + Here's a step-by-step explanation of the code: + + 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps + required to reach the last element. + + 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't + need any jumps to reach the end. In this case, the function returns 0. + + 3. If the array has more than one element, the code proceeds with the jump calculation. + + 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps + taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the + remaining steps until a new jump is required. + + 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). + The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as + we are already at the end. + + 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater + than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that + can be reached in a single jump. + + 7. It then decrements `steps` by 1, representing the steps taken in the current jump. + + 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` + by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). + + 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the + last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. + + In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array + by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result + is the total number of jumps taken to reach the end. +*/ +#include +#include + +int MinNumberOfJumpsOptimal(std::vector& array) { + // If the array has only one element, no jumps are needed. + if (array.size() == 1) { + return 0; + } + + // Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. + int jumps = 0; + int maxreach = array[0]; + int steps = array[0]; + + // Iterate through the array to calculate the minimum number of jumps. + // We stop at the second-to-last element as we don't need an additional jump from there. + for (int i = 1; i < array.size() - 1; i++) { + // Update the maximum reachable position if the current position plus the value at that index is greater. + if (i + array[i] > maxreach) { + maxreach = i + array[i]; + } + + // Decrement the remaining steps in the current jump. + steps--; + + // If the current jump is completed (steps becomes 0), calculate the new jump. + if (steps == 0) { + // Increment jumps to count the completed jump. + jumps++; + + // Update steps to the number of steps required to reach the farthest position. + steps = maxreach - i; + } + } + + // The minimum number of jumps to reach the last element is the total number of jumps taken plus one + // because the loop doesn't consider the last element (as we don't need an additional jump from there). + return jumps + 1; +} diff --git a/Dynamic Programming/min_number_of_jumps.go b/Dynamic Programming/min_number_of_jumps.go new file mode 100644 index 00000000..4eed365e --- /dev/null +++ b/Dynamic Programming/min_number_of_jumps.go @@ -0,0 +1,146 @@ +/* + This Go code snippet implements the `MinNumberOfJumps` function, which calculates the minimum number of jumps required to reach the + last element of the `array` by starting from the first element. Each element of the array represents the maximum number of steps that + can be taken from that position. + + Here's a step-by-step explanation of the code: + + 1. The function `MinNumberOfJumps` takes an array `array` as input and returns the minimum number of jumps required. + + 2. The `ways` array is initialized to store the minimum number of jumps required to reach each position in the input array. The length of + the `ways` array is the same as the input array, and all values are initialized to `math.MaxInt32`, which represents an unreachable state. + + 3. The base case is set for the first element of the `ways` array. Since we are already at the first element, the minimum number of jumps + required is 0. So, `ways[0]` is set to 0. + + 4. Starting from the second element (i = 1) to the last element (i = len(array) - 1), the function iterates through the `array`. + + 5. For each element, it iterates through all previous elements (j) up to the current position (i) to check if it is possible to jump from j to i. + + 6. If `array[j] + j >= i`, it means we can jump from position `j` to position `i`. + + 7. The `ways[i]` value is then updated using the minimum between the current `ways[i]` value and `ways[j] + 1`. The `ways[j] + 1` represents + the minimum number of jumps required to reach position `j`, and then from position `j` to position `i`. + + 8. After all iterations, `ways[len(ways) - 1]` will contain the minimum number of jumps required to reach the last element of the array. + + 9. The `min` function is a helper function that returns the minimum of two integers. + + The `MinNumberOfJumps` function uses dynamic programming to find the minimum number of jumps efficiently by keeping track of the minimum number + of jumps required to reach each position from the previous positions. The time complexity of this function is O(n^2), where n is the length of + the input array. The space complexity is O(n), as the `ways` array is used to store the minimum jumps for each position in the array. +*/ +package main + +import "math" + +// MinNumberOfJumps calculates the minimum number of jumps required to reach the last element of the `array`. +func MinNumberOfJumps(array []int) int { + // Create an array to store the minimum number of jumps required to reach each position in the `array`. + ways := make([]int, len(array)) + + // Initialize the `ways` array with maximum integer values representing an unreachable state. + for i := range ways { + ways[i] = math.MaxInt32 + } + + // Base case: The first element requires 0 jumps to reach itself. + ways[0] = 0 + + // Iterate through the array starting from the second element. + for i := 1; i < len(array); i++ { + // Check all previous elements to see if a jump from j to i is possible. + for j := 0; j < i; j++ { + // If it is possible to jump from j to i, update the `ways[i]` value. + if array[j] + j >= i { + ways[i] = min(ways[i], ways[j]+1) + } + } + } + + // The value at `ways[len(ways) - 1]` will contain the minimum number of jumps required to reach the last element. + return ways[len(ways)-1] +} + +// min returns the minimum of two integers. +func min(a, b int) int { + if a < b { + return a + } + return b +} + + +/* + The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. + Each element in the array represents the maximum distance you can jump from that position. + + Here's a step-by-step explanation of the code: + + 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps + required to reach the last element. + + 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't + need any jumps to reach the end. In this case, the function returns 0. + + 3. If the array has more than one element, the code proceeds with the jump calculation. + + 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps + taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the + remaining steps until a new jump is required. + + 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). + The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as + we are already at the end. + + 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater + than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that + can be reached in a single jump. + + 7. It then decrements `steps` by 1, representing the steps taken in the current jump. + + 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` + by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). + + 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the + last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. + + In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array + by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result + is the total number of jumps taken to reach the end. +*/ +func MinNumberOfJumpsOptimal(array []int) int { + // If the array has only one element, no jumps are needed. + if len(array) == 1 { + return 0 + } + + // Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. + jumps := 0 + maxreach, steps := array[0], array[0] + + // Iterate through the array to calculate the minimum number of jumps. + // We stop at the second-to-last element as we don't need an additional jump from there. + for i := 1; i < len(array)-1; i++ { + // Update the maximum reachable position if the current position plus the value at that index is greater. + if i+array[i] > maxreach { + maxreach = i + array[i] + } + + // Decrement the remaining steps in the current jump. + steps-- + + // If the current jump is completed (steps becomes 0), calculate the new jump. + if steps == 0 { + // Increment jumps to count the completed jump. + jumps++ + + // Update steps to the number of steps required to reach the farthest position. + steps = maxreach - i + } + } + + // The minimum number of jumps to reach the last element is the total number of jumps taken plus one + // because the loop doesn't consider the last element (as we don't need an additional jump from there). + return jumps + 1 +} diff --git a/Dynamic Programming/min_number_of_jumps.java b/Dynamic Programming/min_number_of_jumps.java new file mode 100644 index 00000000..d2ab7a74 --- /dev/null +++ b/Dynamic Programming/min_number_of_jumps.java @@ -0,0 +1,77 @@ +public class min_number_of_jumps { + +} +/* + The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. + Each element in the array represents the maximum distance you can jump from that position. + + Here's a step-by-step explanation of the code: + + 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps + required to reach the last element. + + 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't + need any jumps to reach the end. In this case, the function returns 0. + + 3. If the array has more than one element, the code proceeds with the jump calculation. + + 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps + taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the + remaining steps until a new jump is required. + + 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). + The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as + we are already at the end. + + 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater + than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that + can be reached in a single jump. + + 7. It then decrements `steps` by 1, representing the steps taken in the current jump. + + 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` + by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). + + 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the + last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. + + In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array + by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result + is the total number of jumps taken to reach the end. +*/ +public static int minNumberOfJumpsOptimal(int[] array) { + // If the array has only one element, no jumps are needed. + if (array.length == 1) { + return 0; + } + + // Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. + int jumps = 0; + int maxReach = array[0]; + int steps = array[0]; + + // Iterate through the array to calculate the minimum number of jumps. + // We stop at the second-to-last element as we don't need an additional jump from there. + for (int i = 1; i < array.length - 1; i++) { + // Update the maximum reachable position if the current position plus the value at that index is greater. + if (i + array[i] > maxReach) { + maxReach = i + array[i]; + } + + // Decrement the remaining steps in the current jump. + steps--; + + // If the current jump is completed (steps becomes 0), calculate the new jump. + if (steps == 0) { + // Increment jumps to count the completed jump. + jumps++; + + // Update steps to the number of steps required to reach the farthest position. + steps = maxReach - i; + } + } + + // The minimum number of jumps to reach the last element is the total number of jumps taken plus one + // because the loop doesn't consider the last element (as we don't need an additional jump from there). + return jumps + 1; +} diff --git a/Dynamic Programming/min_number_of_jumps.js b/Dynamic Programming/min_number_of_jumps.js new file mode 100644 index 00000000..a19ec60b --- /dev/null +++ b/Dynamic Programming/min_number_of_jumps.js @@ -0,0 +1,74 @@ +/* + The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. + Each element in the array represents the maximum distance you can jump from that position. + + Here's a step-by-step explanation of the code: + + 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps + required to reach the last element. + + 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't + need any jumps to reach the end. In this case, the function returns 0. + + 3. If the array has more than one element, the code proceeds with the jump calculation. + + 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps + taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the + remaining steps until a new jump is required. + + 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). + The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as + we are already at the end. + + 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater + than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that + can be reached in a single jump. + + 7. It then decrements `steps` by 1, representing the steps taken in the current jump. + + 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` + by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). + + 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the + last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. + + In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array + by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result + is the total number of jumps taken to reach the end. +*/ +function minNumberOfJumpsOptimal(array) { + // If the array has only one element, no jumps are needed. + if (array.length === 1) { + return 0; + } + + // Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. + let jumps = 0; + let maxReach = array[0]; + let steps = array[0]; + + // Iterate through the array to calculate the minimum number of jumps. + // We stop at the second-to-last element as we don't need an additional jump from there. + for (let i = 1; i < array.length - 1; i++) { + // Update the maximum reachable position if the current position plus the value at that index is greater. + if (i + array[i] > maxReach) { + maxReach = i + array[i]; + } + + // Decrement the remaining steps in the current jump. + steps--; + + // If the current jump is completed (steps becomes 0), calculate the new jump. + if (steps === 0) { + // Increment jumps to count the completed jump. + jumps++; + + // Update steps to the number of steps required to reach the farthest position. + steps = maxReach - i; + } + } + + // The minimum number of jumps to reach the last element is the total number of jumps taken plus one + // because the loop doesn't consider the last element (as we don't need an additional jump from there). + return jumps + 1; +} diff --git a/Dynamic Programming/min_number_of_jumps.py b/Dynamic Programming/min_number_of_jumps.py new file mode 100644 index 00000000..c6a83340 --- /dev/null +++ b/Dynamic Programming/min_number_of_jumps.py @@ -0,0 +1,69 @@ +''' + The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. + Each element in the array represents the maximum distance you can jump from that position. + + Here's a step-by-step explanation of the code: + + 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps + required to reach the last element. + + 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't + need any jumps to reach the end. In this case, the function returns 0. + + 3. If the array has more than one element, the code proceeds with the jump calculation. + + 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps + taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the + remaining steps until a new jump is required. + + 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). + The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as + we are already at the end. + + 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater + than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that + can be reached in a single jump. + + 7. It then decrements `steps` by 1, representing the steps taken in the current jump. + + 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` + by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). + + 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the + last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. + + In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array + by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result + is the total number of jumps taken to reach the end. +''' +def min_number_of_jumps_optimal(array): + # If the array has only one element, no jumps are needed. + if len(array) == 1: + return 0 + + # Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. + jumps = 0 + max_reach = array[0] + steps = array[0] + + # Iterate through the array to calculate the minimum number of jumps. + # We stop at the second-to-last element as we don't need an additional jump from there. + for i in range(1, len(array) - 1): + # Update the maximum reachable position if the current position plus the value at that index is greater. + if i + array[i] > max_reach: + max_reach = i + array[i] + + # Decrement the remaining steps in the current jump. + steps -= 1 + + # If the current jump is completed (steps becomes 0), calculate the new jump. + if steps == 0: + # Increment jumps to count the completed jump. + jumps += 1 + + # Update steps to the number of steps required to reach the farthest position. + steps = max_reach - i + + # The minimum number of jumps to reach the last element is the total number of jumps taken plus one + # because the loop doesn't consider the last element (as we don't need an additional jump from there). + return jumps + 1 diff --git a/Dynamic Programming/min_steps_to_make_string_palindrome.go b/Dynamic Programming/min_steps_to_make_string_palindrome.go new file mode 100644 index 00000000..040086c5 --- /dev/null +++ b/Dynamic Programming/min_steps_to_make_string_palindrome.go @@ -0,0 +1,70 @@ +/* + Given a string, find the minimum number of insertions needed to make it a palindrome. + + Sample Input: "abcde" + Sample Output: 4 + Explanation: The minimum insertions required are 'edcb' -> "abcdecb", resulting in a palindrome. + + Approach: + We can solve this problem using dynamic programming. + Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. + If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. + Otherwise, we have two options: + 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. + 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. + We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. + Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. + + Time complexity: O(n^2) + Space complexity: O(n^2) +*/ + +package main + +import ( + "fmt" +) + +func lcs(s1, s2 string) int { + n, m := len(s1), len(s2) + dp := make([][]int, n+1) // Create a 2D matrix dp to store the lengths of LCS + for i := 0; i <= n; i++ { + dp[i] = make([]int, m+1) + } + + for i := 1; i <= n; i++ { + for j := 1; j <= m; j++ { + if s1[i-1] == s2[j-1] { // If characters at the current positions match + dp[i][j] = 1 + dp[i-1][j-1] // Increase the length of LCS by 1 + } else { + dp[i][j] = max(dp[i-1][j], dp[i][j-1]) // Take the maximum length from the previous positions + } + } + } + + return dp[n][m] // Return the length of the LCS of s1 and s2 +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func minInsertions(s string) int { + reverseS := "" + for i := len(s) - 1; i >= 0; i-- { + reverseS += string(s[i]) // Reverse the string s + } + + lcsLength := lcs(s, reverseS) // Find the length of the LCS of s and its reverse + return len(s) - lcsLength // Return the number of insertions required to make s a palindrome +} + +func main() { + var s string + fmt.Scanln(&s) // Read input string + ans := minInsertions(s) // Calculate the minimum number of insertions required + fmt.Println(ans) // Print the result +} diff --git a/Dynamic Programming/min_steps_to_make_string_palindrome.java b/Dynamic Programming/min_steps_to_make_string_palindrome.java new file mode 100644 index 00000000..7ab3e585 --- /dev/null +++ b/Dynamic Programming/min_steps_to_make_string_palindrome.java @@ -0,0 +1,75 @@ +/* + Given a string, find the minimum number of insertions needed to make it a palindrome. + + Sample Input: "abcde" + Sample Output: 4 + Explanation: The minimum insertions required are 'edcb' -> "abcdecb", resulting in a palindrome. + + Approach: + We can solve this problem using dynamic programming. + Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. + If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. + Otherwise, we have two options: + 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. + 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. + We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. + Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. + + Time complexity: O(n^2) + Space complexity: O(n^2) +*/ +import java.util.Scanner; + +public class Main { + + // Function to calculate the length of the Longest Common Subsequence (LCS) of two strings + public static int lcs(String s1, String s2) { + int n = s1.length(); + int m = s2.length(); + + // Create a 2D array dp to store the lengths of LCS + int[][] dp = new int[n+1][m+1]; + + // Initialize the first row and first column to 0 + for (int i = 0; i <= n; i++) { + dp[i][0] = 0; + } + for (int j = 0; j <= m; j++) { + dp[0][j] = 0; + } + + // Fill the dp array using dynamic programming + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (s1.charAt(i-1) == s2.charAt(j-1)) { + // If characters at the current positions match, increase the length of LCS by 1 + dp[i][j] = 1 + dp[i-1][j-1]; + } else { + // Take the maximum length from the previous positions + dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); + } + } + } + + // Return the length of the LCS of s1 and s2 + return dp[n][m]; + } + + // Function to calculate the minimum number of insertions required to make a string palindrome + public static int minInsertions(String s) { + StringBuilder reverseSb = new StringBuilder(s); + reverseSb.reverse(); // Reverse the string s + String reverseS = reverseSb.toString(); + + int lcsLength = lcs(s, reverseS); // Find the length of the LCS of s and its reverse + return s.length() - lcsLength; // Return the number of insertions required to make s a palindrome + } + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + String s = scanner.nextLine(); // Read input string + int ans = minInsertions(s); // Calculate the minimum number of insertions required + System.out.println(ans); // Print the result + scanner.close(); + } +} diff --git a/Dynamic Programming/min_steps_to_make_string_palindrome.py b/Dynamic Programming/min_steps_to_make_string_palindrome.py new file mode 100644 index 00000000..e79d1cde --- /dev/null +++ b/Dynamic Programming/min_steps_to_make_string_palindrome.py @@ -0,0 +1,51 @@ +''' + Given a string, find the minimum number of insertions needed to make it a palindrome. + + Sample Input: "abcde" + Sample Output: 4 + Explanation: The minimum insertions required are 'edcb' -> "abcdecb", resulting in a palindrome. + + Approach: + We can solve this problem using dynamic programming. + Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. + If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. + Otherwise, we have two options: + 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. + 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. + We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. + Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. + + Time complexity: O(n^2) + Space complexity: O(n^2) +''' +# Function to calculate the length of the Longest Common Subsequence (LCS) of two strings +def lcs(s1, s2): + n, m = len(s1), len(s2) + + # Create a 2D list dp to store the lengths of LCS + dp = [[0] * (m + 1) for _ in range(n + 1)] + + # Fill the dp array using dynamic programming + for i in range(1, n + 1): + for j in range(1, m + 1): + if s1[i - 1] == s2[j - 1]: + # If characters at the current positions match, increase the length of LCS by 1 + dp[i][j] = 1 + dp[i - 1][j - 1] + else: + # Take the maximum length from the previous positions + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + + # Return the length of the LCS of s1 and s2 + return dp[n][m] + +# Function to calculate the minimum number of insertions required to make a string palindrome +def min_insertions(s): + reverse_s = s[::-1] # Reverse the string s + + lcs_length = lcs(s, reverse_s) # Find the length of the LCS of s and its reverse + return len(s) - lcs_length # Return the number of insertions required to make s a palindrome + +if __name__ == "__main__": + s = input() # Read input string + ans = min_insertions(s) # Calculate the minimum number of insertions required + print(ans) # Print the result diff --git a/Dynamic Programming/min_steps_to_make_string_pallindrome.cpp b/Dynamic Programming/min_steps_to_make_string_pallindrome.cpp new file mode 100644 index 00000000..7bbbb49d --- /dev/null +++ b/Dynamic Programming/min_steps_to_make_string_pallindrome.cpp @@ -0,0 +1,66 @@ +/* + Given a string, find the minimum number of insertions needed to make it a palindrome. + + Sample Input: "abcde" + Sample Output: 4 + Explanation: The minimum insertions required are 'edcb' -> "abcdecb", resulting in a palindrome. + + Approach: + We can solve this problem using dynamic programming. + Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. + If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. + Otherwise, we have two options: + 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. + 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. + We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. + Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. + + Time complexity: O(n^2) + Space complexity: O(n^2) +*/ + +#include +using namespace std; + +int lcs(string s1,string s2){ + int n=s1.size(),m=s2.size(); + vector> dp(n+1,vector(m+1,-1)); + + // bottom up approach to calculate lcs + // base conditions + for(int i=0;i<=n;i++){ + dp[i][0]=0; + } + + for(int i=0;i<=m;i++){ + dp[0][i]=0; + } + + // filling the table to calculate the longest common subsequence + for(int i=1;i<=n;i++){ + for(int j=1;j<=m;j++){ + if(s1[i-1]==s2[j-1]){ // checking if the characters at both the string are same + dp[i][j]=1+dp[i-1][j-1]; + } + else{ + dp[i][j]=0+max(dp[i-1][j],dp[i][j-1]); + } + } + } + + return dp[n][m]; +} + +int main(){ + string s; + cin>>s; + string reverse_s; + for(int i=s.size()-1;i>=0;i--){ + reverse_s.push_back(s[i]); + } + // the minimum steps to make a string pallindrome will require to calculate the + // longest common subsequence in the given string and the reverse of the string + int ans=lcs(s,reverse_s); + ans=s.size()-ans; + cout< "abcdecb", resulting in a palindrome. + + Approach: + We can solve this problem using dynamic programming. + Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. + If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. + Otherwise, we have two options: + 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. + 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. + We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. + Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. + + Time complexity: O(n^2) + Space complexity: O(n^2) +*/ +// Function to calculate the length of the Longest Common Subsequence (LCS) of two strings +function lcs(s1, s2) { + const n = s1.length; + const m = s2.length; + + // Create a 2D array dp to store the lengths of LCS + const dp = new Array(n + 1).fill(0).map(() => new Array(m + 1).fill(0)); + + // Fill the dp array using dynamic programming + for (let i = 1; i <= n; i++) { + for (let j = 1; j <= m; j++) { + if (s1[i - 1] === s2[j - 1]) { + // If characters at the current positions match, increase the length of LCS by 1 + dp[i][j] = 1 + dp[i - 1][j - 1]; + } else { + // Take the maximum length from the previous positions + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + // Return the length of the LCS of s1 and s2 + return dp[n][m]; +} + +// Function to calculate the minimum number of insertions required to make a string palindrome +function minInsertions(s) { + const reverseS = s.split("").reverse().join(""); // Reverse the string s + + const lcsLength = lcs(s, reverseS); // Find the length of the LCS of s and its reverse + return s.length - lcsLength; // Return the number of insertions required to make s a palindrome +} + +// Example usage: +const input = "abcde"; +const result = minInsertions(input); // Calculate the minimum number of insertions required +console.log(result); // Print the result diff --git a/Dynamic_Programming/min_steps_to_reduce_a_number_to_one_dp.cpp b/Dynamic Programming/min_steps_to_reduce_a_number_to_one.cpp similarity index 69% rename from Dynamic_Programming/min_steps_to_reduce_a_number_to_one_dp.cpp rename to Dynamic Programming/min_steps_to_reduce_a_number_to_one.cpp index 52a795b7..bb2f2ca3 100644 --- a/Dynamic_Programming/min_steps_to_reduce_a_number_to_one_dp.cpp +++ b/Dynamic Programming/min_steps_to_reduce_a_number_to_one.cpp @@ -1,43 +1,60 @@ -// Minimum steps to reduce a number to one conditions are as follows -// a) subtract 1 [one operation] -// b) divide by 2 [one operation] -// c) divide by 3 [one operation] - -// Dynamic Programming solution -// Program Author : Abhisek Kumar Gupta -/* - Input : 10 - Output : 3 - Explanation : 10 reduced to 9 reduced to 3 reduced to 1 [total 3 operations] - Input : 15 - Output : 4 -*/ -#include -using namespace std; -int dp[10000]; -int find_min_steps(int number){ - int r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; - dp[0] = 0; - dp[1] = 0; - dp[2] = 1; - dp[3] = 1; - for(int i = 4; i <= number; i++){ - r1 = 1 + dp[i - 1]; - if(i % 2 == 0) - r2 = 1 + dp[i / 2]; - if(i % 3 == 0) - r3 = 1 + dp[i / 3]; - dp[i] = min(r1, min(r2, r3)); - r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; - } - - return dp[number]; -} -int main(){ - int number; - cin >> number; - memset(dp, 0, sizeof(dp)); - int result = find_min_steps(number); - cout << result; - return 0; -} +// Minimum steps to reduce a number to one conditions are as follows +// a) subtract 1 [one operation] +// b) divide by 2 [one operation] +// c) divide by 3 [one operation] + +// Dynamic Programming solution +// Program Author : Abhisek Kumar Gupta +/* + Input : 10 + Output : 3 + Explanation : 10 reduced to 9 reduced to 3 reduced to 1 [total 3 operations] + Input : 15 + Output : 4 +*/ +#include +using namespace std; +int dp[10000]; +int find_min_steps(int number){ + int r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; + dp[0] = 0; + dp[1] = 0; + dp[2] = 1; + dp[3] = 1; + for(int i = 4; i <= number; i++){ + r1 = 1 + dp[i - 1]; + if(i % 2 == 0) + r2 = 1 + dp[i / 2]; + if(i % 3 == 0) + r3 = 1 + dp[i / 3]; + dp[i] = min(r1, min(r2, r3)); + r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; + } + + return dp[number]; +} + +int memoized[10004]; +int find_min_steps(int number){ + if(number == 1) + return 0; + int r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; + if(memoized[number] != -1) return memoized[number]; + r1 = 1 + find_min_steps(number - 1); + if(number % 2 == 0) + r2 = 1 + find_min_steps(number / 2); + if(number % 3 == 0) + r3 = 1 + find_min_steps(number / 3); + memoized[number] = min(r1, min(r2, r3)); + return memoized[number]; +} + + +int main(){ + int number; + cin >> number; + memset(dp, 0, sizeof(dp)); + int result = find_min_steps(number); + cout << result; + return 0; +} diff --git a/Dynamic Programming/num_ways_to_make_change.cpp b/Dynamic Programming/num_ways_to_make_change.cpp new file mode 100644 index 00000000..4d03a4a2 --- /dev/null +++ b/Dynamic Programming/num_ways_to_make_change.cpp @@ -0,0 +1,68 @@ +/* + Given an array of distinct positive integers representing coin denominations and a single non-negative integer n + representing a target amount of money, write a function that returns the number of ways to make change for + that target amount using the given coin denominations. + + Sample Input: n = 6 denominations : [1, 5] + Output: 2 (1 * 1 + 1 * 5 and 6 * 1) + + Explanation: + + The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. + This algorithm calculates the number of ways to make change for a given amount using a set of denominations. + + Here's how the algorithm works: + + 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" + (an array of coin denominations). + + 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount + from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make + change for zero. + + 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. + + 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each + amount. + + 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, + it means that the current denomination can contribute to the change for the current amount. + + 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus + the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. + + 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total + number of ways to make change for the target amount "n" using the given denominations. + + In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given + amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. + + Time complexity : O(nd) + Space complexity : O(n) where n is the target amount and d is the number of coin denominations + +*/ +#include + +int NumberOfWaysToMakeChange(int n, const std::vector& denoms) { + // Create an array to store the number of ways to make change for each amount from 0 to n. + std::vector ways(n + 1, 0); + + // Initialize the base case: There is one way to make change for amount 0 (using no coins). + ways[0] = 1; + + // Iterate over each denomination. + for (int denom : denoms) { + // For each denomination, iterate over each amount from 1 to n. + for (int amount = 1; amount < n + 1; amount++) { + // Check if the denomination can be used to make change for the current amount. + if (denom <= amount) { + // Add the number of ways to make change for the current amount + // by considering the current denomination. + ways[amount] += ways[amount - denom]; + } + } + } + + // Return the number of ways to make change for the target amount n. + return ways[n]; +} diff --git a/Dynamic Programming/num_ways_to_make_change.go b/Dynamic Programming/num_ways_to_make_change.go new file mode 100644 index 00000000..b837a678 --- /dev/null +++ b/Dynamic Programming/num_ways_to_make_change.go @@ -0,0 +1,68 @@ +/* + Given an array of distinct positive integers representing coin denominations and a single non-negative integer n + representing a target amount of money, write a function that returns the number of ways to make change for + that target amount using the given coin denominations. + + Sample Input: n = 6 denominations : [1, 5] + Output: 2 (1 * 1 + 1 * 5 and 6 * 1) + + Explanation: + + The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. + This algorithm calculates the number of ways to make change for a given amount using a set of denominations. + + Here's how the algorithm works: + + 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" + (an array of coin denominations). + + 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount + from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make + change for zero. + + 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. + + 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each + amount. + + 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, + it means that the current denomination can contribute to the change for the current amount. + + 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus + the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. + + 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total + number of ways to make change for the target amount "n" using the given denominations. + + In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given + amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. + + Time complexity : O(nd) + Space complexity : O(n) where n is the target amount and d is the number of coin denominations + +*/ +package main + +func NumberOfWaysToMakeChange(n int, denoms []int) int { + // Create an array to store the number of ways to make change for each amount from 0 to n. + ways := make([]int, n+1) + + // Initialize the base case: There is one way to make change for amount 0 (using no coins). + ways[0] = 1 + + // Iterate over each denomination. + for _, denom := range denoms { + // For each denomination, iterate over each amount from 1 to n. + for amount := 1; amount < n+1; amount++ { + // Check if the denomination can be used to make change for the current amount. + if denom <= amount { + // Add the number of ways to make change for the current amount + // by considering the current denomination. + ways[amount] += ways[amount-denom] + } + } + } + + // Return the number of ways to make change for the target amount n. + return ways[n] +} diff --git a/Dynamic Programming/num_ways_to_make_change.java b/Dynamic Programming/num_ways_to_make_change.java new file mode 100644 index 00000000..6efdb50b --- /dev/null +++ b/Dynamic Programming/num_ways_to_make_change.java @@ -0,0 +1,79 @@ +/* + Given an array of distinct positive integers representing coin denominations and a single non-negative integer n + representing a target amount of money, write a function that returns the number of ways to make change for + that target amount using the given coin denominations. + + Sample Input: n = 6 denominations : [1, 5] + Output: 2 (1 * 1 + 1 * 5 and 6 * 1) + + Explanation: + + The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. + This algorithm calculates the number of ways to make change for a given amount using a set of denominations. + + Here's how the algorithm works: + + 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" + (an array of coin denominations). + + 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount + from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make + change for zero. + + 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. + + 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each + amount. + + 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, + it means that the current denomination can contribute to the change for the current amount. + + 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus + the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. + + 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total + number of ways to make change for the target amount "n" using the given denominations. + + In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given + amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. + + Time complexity : O(nd) + Space complexity : O(n) where n is the target amount and d is the number of coin denominations + +*/ +import java.util.Arrays; + +public class CoinChange { + + public static int numberOfWaysToMakeChange(int n, int[] denoms) { + // Create an array to store the number of ways to make change for each amount from 0 to n. + int[] ways = new int[n + 1]; + + // Initialize the base case: There is one way to make change for amount 0 (using no coins). + ways[0] = 1; + + // Iterate over each denomination. + for (int denom : denoms) { + // For each denomination, iterate over each amount from 1 to n. + for (int amount = 1; amount < n + 1; amount++) { + // Check if the denomination can be used to make change for the current amount. + if (denom <= amount) { + // Add the number of ways to make change for the current amount + // by considering the current denomination. + ways[amount] += ways[amount - denom]; + } + } + } + + // Return the number of ways to make change for the target amount n. + return ways[n]; + } + + public static void main(String[] args) { + int n = 10; + int[] denoms = {1, 2, 5}; + + int numberOfWays = numberOfWaysToMakeChange(n, denoms); + System.out.println("Number of ways to make change: " + numberOfWays); + } +} diff --git a/Dynamic Programming/num_ways_to_make_change.js b/Dynamic Programming/num_ways_to_make_change.js new file mode 100644 index 00000000..0ba6d8a3 --- /dev/null +++ b/Dynamic Programming/num_ways_to_make_change.js @@ -0,0 +1,73 @@ +/* + Given an array of distinct positive integers representing coin denominations and a single non-negative integer n + representing a target amount of money, write a function that returns the number of ways to make change for + that target amount using the given coin denominations. + + Sample Input: n = 6 denominations : [1, 5] + Output: 2 (1 * 1 + 1 * 5 and 6 * 1) + + Explanation: + + The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. + This algorithm calculates the number of ways to make change for a given amount using a set of denominations. + + Here's how the algorithm works: + + 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" + (an array of coin denominations). + + 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount + from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make + change for zero. + + 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. + + 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each + amount. + + 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, + it means that the current denomination can contribute to the change for the current amount. + + 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus + the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. + + 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total + number of ways to make change for the target amount "n" using the given denominations. + + In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given + amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. + + Time complexity : O(nd) + Space complexity : O(n) where n is the target amount and d is the number of coin denominations + +*/ +function numberOfWaysToMakeChange(n, denoms) { + // Create an array to store the number of ways to make change for each amount from 0 to n. + const ways = new Array(n + 1).fill(0); + + // Initialize the base case: There is one way to make change for amount 0 (using no coins). + ways[0] = 1; + + // Iterate over each denomination. + for (const denom of denoms) { + // For each denomination, iterate over each amount from 1 to n. + for (let amount = 1; amount < n + 1; amount++) { + // Check if the denomination can be used to make change for the current amount. + if (denom <= amount) { + // Add the number of ways to make change for the current amount + // by considering the current denomination. + ways[amount] += ways[amount - denom]; + } + } + } + + // Return the number of ways to make change for the target amount n. + return ways[n]; +} + +// Example usage +const n = 10; +const denoms = [1, 2, 5]; + +const numberOfWays = numberOfWaysToMakeChange(n, denoms); +console.log("Number of ways to make change:", numberOfWays); diff --git a/Dynamic Programming/num_ways_to_make_change.py b/Dynamic Programming/num_ways_to_make_change.py new file mode 100644 index 00000000..0bd8fcc5 --- /dev/null +++ b/Dynamic Programming/num_ways_to_make_change.py @@ -0,0 +1,69 @@ +''' + Given an array of distinct positive integers representing coin denominations and a single non-negative integer n + representing a target amount of money, write a function that returns the number of ways to make change for + that target amount using the given coin denominations. + + Sample Input: n = 6 denominations : [1, 5] + Output: 2 (1 * 1 + 1 * 5 and 6 * 1) + + Explanation: + + The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. + This algorithm calculates the number of ways to make change for a given amount using a set of denominations. + + Here's how the algorithm works: + + 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" + (an array of coin denominations). + + 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount + from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make + change for zero. + + 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. + + 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each + amount. + + 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, + it means that the current denomination can contribute to the change for the current amount. + + 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus + the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. + + 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total + number of ways to make change for the target amount "n" using the given denominations. + + In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given + amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. + + Time complexity : O(nd) + Space complexity : O(n) where n is the target amount and d is the number of coin denominations +''' +def number_of_ways_to_make_change(n, denoms): + # Create a list to store the number of ways to make change for each amount from 0 to n. + ways = [0] * (n + 1) + + # Initialize the base case: There is one way to make change for amount 0 (using no coins). + ways[0] = 1 + + # Iterate over each denomination. + for denom in denoms: + # For each denomination, iterate over each amount from 1 to n. + for amount in range(1, n + 1): + # Check if the denomination can be used to make change for the current amount. + if denom <= amount: + # Add the number of ways to make change for the current amount + # by considering the current denomination. + ways[amount] += ways[amount - denom] + + # Return the number of ways to make change for the target amount n. + return ways[n] + + +# Example usage +n = 10 +denoms = [1, 2, 5] + +number_of_ways = number_of_ways_to_make_change(n, denoms) +print("Number of ways to make change:", number_of_ways) diff --git a/Dynamic Programming/num_ways_to_traverse_graph.cpp b/Dynamic Programming/num_ways_to_traverse_graph.cpp new file mode 100644 index 00000000..ed5ce093 --- /dev/null +++ b/Dynamic Programming/num_ways_to_traverse_graph.cpp @@ -0,0 +1,179 @@ +/* + + You're given two positive integers representing the width and height of a grid-shaped, rectangular graph. + Write a function that returns the number of ways to reach the bottom right corner of the graph when starting + at the top left corner. Each move you take must either go down or right. In other words, you can never move up + or left in the graph. + + For example, given the graph illustrated below, with width = 2 and height = 3 , there are three ways to + reach the bottom right corner when starting at the top left corner: + _ _ + |_|_| + |_|_| + |_|_| + Down Down Right + Right Down Down + Down Right Down + + Sample Input: Width : 4 height: 3 + Output: 10 + + Explanation: + The code snippet implements the `NumberOfWaysToTraverseGraph` function, which calculates the number of ways to + traverse a 2D graph from the top-left corner to the bottom-right corner. The graph has a given width and height. + + Here's a breakdown of the code: + + 1. The function takes two parameters: `width` and `height`, representing the dimensions of the graph. + + 2. It initializes a 2D slice called `numberOfWays` with dimensions `(height+1) x (width+1)`. The additional "+1" is to + account for the boundary cases when traversing the graph. + + 3. It enters a nested loop to iterate over the graph cells. The outer loop iterates over the width indices (`widthIdx`), + and the inner loop iterates over the height indices (`heightIdx`). + + 4. For each cell, it checks if it is on the top row (`heightIdx == 1`) or the leftmost column (`widthIdx == 1`). If so, it + means that there is only one way to reach that cell, either by moving right or moving down. Therefore, it sets `numberOfWays[heightIdx][widthIdx]` to 1. + + 5. If the cell is not on the top row or the leftmost column, it means that it can be reached by either moving from the + cell above (up) or the cell to the left (left). The number of ways to reach the current cell is the sum of the number of + ways to reach the cell above and the number of ways to reach the cell to the left. This value is stored in + `numberOfWays[heightIdx][widthIdx]`. + + 6. After iterating over all cells, the function returns the value stored in the bottom-right corner of `numberOfWays`, + which represents the total number of ways to traverse the graph. + + The algorithm uses dynamic programming to build the `numberOfWays` matrix iteratively, starting from the top-left corner + and moving towards the bottom-right corner. By calculating the number of ways to reach each cell based on the number of ways to reach its neighboring cells, it avoids redundant calculations and computes the result efficiently. + + The time complexity of the algorithm is O(width * height) since it iterates over all cells of the graph. + + The space complexity is also O(width * height) since it uses the `numberOfWays` matrix to store intermediate results. + + +*/ + +int numberOfWaysToTraverseGraph(int width, int height) { + // Create a 2D vector to store the number of ways to reach each cell + vector> numberOfWays(height, vector(width, 1)); + + // Iterate through the cells from top to bottom and left to right + for (int i = 1; i < height; i++) { + for (int j = 1; j < width; j++) { + // Calculate the number of ways to reach the current cell + // by summing the number of ways from the cell above and the cell to the left + numberOfWays[i][j] = numberOfWays[i - 1][j] + numberOfWays[i][j - 1]; + } + } + + // Return the number of ways to reach the bottom-right corner of the graph + return numberOfWays[height - 1][width - 1]; +} + + +/* + Combinatorics Solution + + The given code snippet aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right + corner. Let's break down the solution and provide a detailed explanation: + + 1. The `NumberOfWaysToTraverseGraph` function takes two parameters: `width` and `height`, representing the dimensions of + the graph. + + 2. The variables `xDistanceToCorner` and `yDistanceToCorner` are calculated by subtracting 1 from the `width` and `height` + respectively. These variables represent the distances from the top-left corner to the bottom-right corner along the x-axis and y-axis. + + 3. The `factorial` function is defined separately to calculate the factorial of a number. It takes a number `num` as input + and uses an iterative approach to calculate the factorial. + + 4. In the `NumberOfWaysToTraverseGraph` function, the numerator is calculated as the factorial of the sum of + `xDistanceToCorner` and `yDistanceToCorner`. This represents the total number of possible paths from the top-left + corner to the bottom-right corner. + + 5. The denominator is calculated as the product of the factorials of `xDistanceToCorner` and `yDistanceToCorner`. + This represents the number of ways to arrange the steps along the x-axis and y-axis. + + 6. Finally, the function returns the result by dividing the numerator by the denominator, giving the total number of + ways to traverse the graph. + + The solution relies on the concept of combinatorics, specifically the binomial coefficient, to calculate the number of + ways to traverse the graph. By using factorials, it accounts for all possible paths and eliminates duplicate paths. + This approach provides an efficient solution to the problem. + + O(n + m) time | O(1) space - where n is the width of the graph and m is the height +*/ + +int numberOfWaysToTraverseGraphCombinatorics(int width, int height) { + // Calculate the distances to the bottom-right corner of the graph + int xDistanceToCorner = width - 1; + int yDistanceToCorner = height - 1; + + // Calculate the numerator and denominator for the binomial coefficient + int numerator = factorial(xDistanceToCorner + yDistanceToCorner); + int denominator = factorial(xDistanceToCorner) * factorial(yDistanceToCorner); + + // Return the result by dividing the numerator by the denominator + return numerator / denominator; +} + +int factorial(int n) { + // Base case: factorial of 0 or 1 is 1 + if (n <= 1) { + return 1; + } + + // Recursive case: compute factorial by multiplying n with factorial(n-1) + return n * factorial(n - 1); +} + + +/* + Recursive solution + The given solution aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right + corner. It uses a recursive approach to break down the problem into smaller subproblems. + + Here's how the solution works: + + 1. The function `NumberOfWaysToTraverseGraph` takes the width and height of the graph as input and returns the number of ways to traverse it. + + 2. The base case of the recursion is when either the width or height is equal to 1. In this case, there is only one way to traverse the graph: either by moving only horizontally or vertically. Therefore, the function returns 1. + + 3. For other cases where the width and height are both greater than 1, the function recursively calls itself with two smaller subproblems: + - One subproblem is created by reducing the width by 1 and keeping the same height. + - The other subproblem is created by keeping the same width and reducing the height by 1. + + 4. The number of ways to traverse the current graph is calculated by summing up the number of ways from the two subproblems. + + 5. The recursion continues until it reaches the base case, where the width or height becomes 1, and eventually returns the total number of ways to traverse the graph. + + While this recursive approach is conceptually simple, it suffers from efficiency issues due to exponential time complexity and redundant + calculations. As the graph size increases, the number of recursive calls grows exponentially, leading to a significant increase in computation time. Additionally, without memoization, the function recalculates the same subproblems multiple times, further reducing efficiency. + + To address these drawbacks, alternative approaches like dynamic programming or memoization can be employed to store and + reuse the results of previously solved subproblems, avoiding redundant calculations and improving efficiency. + + The given solution uses a recursive approach to calculate the number of ways to traverse a graph from the top-left corner + to the bottom-right corner. However, this solution has some drawbacks that make it inefficient for larger inputs: + + 1. Exponential Time Complexity: The recursive function makes multiple recursive calls, each with a smaller width or height. + As a result, the number of function calls grows exponentially with the size of the input. This leads to a high time complexity, making the solution inefficient for larger graphs. The time complexity is O(2^(width+height)), which can quickly become unmanageable. + + 2. Overlapping Subproblems: The recursive function suffers from redundant calculations of the same subproblems. For example, + when calculating the number of ways for a specific width and height, the function may recursively calculate the number of ways for smaller widths and heights multiple times. This leads to redundant work and decreases efficiency. + + 3. Lack of Memoization: The solution does not utilize memoization to store the results of previously solved subproblems. + Without memoization, the recursive function ends up recalculating the same subproblems multiple times, further reducing efficiency. + + Due to these reasons, the given recursive solution is considered inefficient and impractical for larger graph sizes. + It is prone to exponential time complexity and redundant calculations, making it unsuitable for real-world scenarios where efficiency is crucial. Alternative approaches, such as the dynamic programming solution mentioned earlier, can provide better performance by avoiding redundant calculations and improving time complexity. +*/ + +int numberOfWaysToTraverseGraphRecursive(int width, int height) { + // Base case: when the width or height is 1, there is only one way to reach the destination + if (width == 1 || height == 1) { + return 1; + } + + // Recursive case: sum the number of ways from the cell above and the cell to the left + return numberOfWaysToTraverseGraphRecursive(width - 1, height) + numberOfWaysToTraverseGraphRecursive(width, height - 1); +} diff --git a/Dynamic Programming/num_ways_to_traverse_graph.go b/Dynamic Programming/num_ways_to_traverse_graph.go new file mode 100644 index 00000000..cefac13d --- /dev/null +++ b/Dynamic Programming/num_ways_to_traverse_graph.go @@ -0,0 +1,189 @@ +/* + + You're given two positive integers representing the width and height of a grid-shaped, rectangular graph. + Write a function that returns the number of ways to reach the bottom right corner of the graph when starting + at the top left corner. Each move you take must either go down or right. In other words, you can never move up + or left in the graph. + + For example, given the graph illustrated below, with width = 2 and height = 3 , there are three ways to + reach the bottom right corner when starting at the top left corner: + _ _ + |_|_| + |_|_| + |_|_| + Down Down Right + Right Down Down + Down Right Down + + Sample Input: Width : 4 height: 3 + Output: 10 + + Explanation: + The code snippet implements the `NumberOfWaysToTraverseGraph` function, which calculates the number of ways to + traverse a 2D graph from the top-left corner to the bottom-right corner. The graph has a given width and height. + + Here's a breakdown of the code: + + 1. The function takes two parameters: `width` and `height`, representing the dimensions of the graph. + + 2. It initializes a 2D slice called `numberOfWays` with dimensions `(height+1) x (width+1)`. The additional "+1" is to + account for the boundary cases when traversing the graph. + + 3. It enters a nested loop to iterate over the graph cells. The outer loop iterates over the width indices (`widthIdx`), + and the inner loop iterates over the height indices (`heightIdx`). + + 4. For each cell, it checks if it is on the top row (`heightIdx == 1`) or the leftmost column (`widthIdx == 1`). If so, it + means that there is only one way to reach that cell, either by moving right or moving down. Therefore, it sets `numberOfWays[heightIdx][widthIdx]` to 1. + + 5. If the cell is not on the top row or the leftmost column, it means that it can be reached by either moving from the + cell above (up) or the cell to the left (left). The number of ways to reach the current cell is the sum of the number of + ways to reach the cell above and the number of ways to reach the cell to the left. This value is stored in + `numberOfWays[heightIdx][widthIdx]`. + + 6. After iterating over all cells, the function returns the value stored in the bottom-right corner of `numberOfWays`, + which represents the total number of ways to traverse the graph. + + The algorithm uses dynamic programming to build the `numberOfWays` matrix iteratively, starting from the top-left corner + and moving towards the bottom-right corner. By calculating the number of ways to reach each cell based on the number of ways to reach its neighboring cells, it avoids redundant calculations and computes the result efficiently. + + The time complexity of the algorithm is O(width * height) since it iterates over all cells of the graph. + + The space complexity is also O(width * height) since it uses the `numberOfWays` matrix to store intermediate results. + + +*/ +package main + +func NumberOfWaysToTraverseGraph(width int, height int) int { + // Initialize the numberOfWays matrix with dimensions (height+1) x (width+1) + // The extra "+1" is to account for the boundary cases when traversing the graph + numberOfWays := make([][]int, height+1) + for i := range numberOfWays { + numberOfWays[i] = make([]int, width+1) + } + + // Iterate over the graph cells + for widthIdx := 1; widthIdx < width+1; widthIdx++ { + for heightIdx := 1; heightIdx < height+1; heightIdx++ { + // Check if the current cell is on the top row or the leftmost column + if widthIdx == 1 || heightIdx == 1 { + // If so, there is only one way to reach this cell (moving right or moving down) + numberOfWays[heightIdx][widthIdx] = 1 + } else { + // If the cell is not on the top row or the leftmost column, + // calculate the number of ways to reach this cell based on the + // number of ways to reach the cell above (up) and the cell to the left (left) + waysLeft := numberOfWays[heightIdx][widthIdx-1] + waysUp := numberOfWays[heightIdx-1][widthIdx] + numberOfWays[heightIdx][widthIdx] = waysLeft + waysUp + } + } + } + + // Return the number of ways to reach the bottom-right corner of the graph + return numberOfWays[height][width] +} + + +/* + Combinatorics Solution + + The given code snippet aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right + corner. Let's break down the solution and provide a detailed explanation: + + 1. The `NumberOfWaysToTraverseGraph` function takes two parameters: `width` and `height`, representing the dimensions of + the graph. + + 2. The variables `xDistanceToCorner` and `yDistanceToCorner` are calculated by subtracting 1 from the `width` and `height` + respectively. These variables represent the distances from the top-left corner to the bottom-right corner along the x-axis and y-axis. + + 3. The `factorial` function is defined separately to calculate the factorial of a number. It takes a number `num` as input + and uses an iterative approach to calculate the factorial. + + 4. In the `NumberOfWaysToTraverseGraph` function, the numerator is calculated as the factorial of the sum of + `xDistanceToCorner` and `yDistanceToCorner`. This represents the total number of possible paths from the top-left + corner to the bottom-right corner. + + 5. The denominator is calculated as the product of the factorials of `xDistanceToCorner` and `yDistanceToCorner`. + This represents the number of ways to arrange the steps along the x-axis and y-axis. + + 6. Finally, the function returns the result by dividing the numerator by the denominator, giving the total number of + ways to traverse the graph. + + The solution relies on the concept of combinatorics, specifically the binomial coefficient, to calculate the number of + ways to traverse the graph. By using factorials, it accounts for all possible paths and eliminates duplicate paths. + This approach provides an efficient solution to the problem. + + O(n + m) time | O(1) space - where n is the width of the graph and m is the height +*/ +func NumberOfWaysToTraverseGraphCombinatorics(width int, height int) int { + // Calculate the distance to the bottom-right corner of the graph + xDistanceToCorner := width - 1 + yDistanceToCorner := height - 1 + + // Calculate the number of ways to traverse the graph using combinatorics + // by calculating the binomial coefficient of (xDistanceToCorner + yDistanceToCorner) choose xDistanceToCorner + // where (n choose k) = n! / (k! * (n-k)!) + numerator := factorial(xDistanceToCorner + yDistanceToCorner) + denominator := factorial(xDistanceToCorner) * factorial(yDistanceToCorner) + + // Return the result by dividing the numerator by the denominator + return numerator / denominator +} + +func factorial(num int) int { + // Calculate the factorial of a number using an iterative approach + result := 1 + for n := 2; n <= num; n++ { + result *= n + } + return result +} + + +/* + Recursive solution + The given solution aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right + corner. It uses a recursive approach to break down the problem into smaller subproblems. + + Here's how the solution works: + + 1. The function `NumberOfWaysToTraverseGraph` takes the width and height of the graph as input and returns the number of ways to traverse it. + + 2. The base case of the recursion is when either the width or height is equal to 1. In this case, there is only one way to traverse the graph: either by moving only horizontally or vertically. Therefore, the function returns 1. + + 3. For other cases where the width and height are both greater than 1, the function recursively calls itself with two smaller subproblems: + - One subproblem is created by reducing the width by 1 and keeping the same height. + - The other subproblem is created by keeping the same width and reducing the height by 1. + + 4. The number of ways to traverse the current graph is calculated by summing up the number of ways from the two subproblems. + + 5. The recursion continues until it reaches the base case, where the width or height becomes 1, and eventually returns the total number of ways to traverse the graph. + + While this recursive approach is conceptually simple, it suffers from efficiency issues due to exponential time complexity and redundant + calculations. As the graph size increases, the number of recursive calls grows exponentially, leading to a significant increase in computation time. Additionally, without memoization, the function recalculates the same subproblems multiple times, further reducing efficiency. + + To address these drawbacks, alternative approaches like dynamic programming or memoization can be employed to store and + reuse the results of previously solved subproblems, avoiding redundant calculations and improving efficiency. + + The given solution uses a recursive approach to calculate the number of ways to traverse a graph from the top-left corner + to the bottom-right corner. However, this solution has some drawbacks that make it inefficient for larger inputs: + + 1. Exponential Time Complexity: The recursive function makes multiple recursive calls, each with a smaller width or height. + As a result, the number of function calls grows exponentially with the size of the input. This leads to a high time complexity, making the solution inefficient for larger graphs. The time complexity is O(2^(width+height)), which can quickly become unmanageable. + + 2. Overlapping Subproblems: The recursive function suffers from redundant calculations of the same subproblems. For example, + when calculating the number of ways for a specific width and height, the function may recursively calculate the number of ways for smaller widths and heights multiple times. This leads to redundant work and decreases efficiency. + + 3. Lack of Memoization: The solution does not utilize memoization to store the results of previously solved subproblems. + Without memoization, the recursive function ends up recalculating the same subproblems multiple times, further reducing efficiency. + + Due to these reasons, the given recursive solution is considered inefficient and impractical for larger graph sizes. + It is prone to exponential time complexity and redundant calculations, making it unsuitable for real-world scenarios where efficiency is crucial. Alternative approaches, such as the dynamic programming solution mentioned earlier, can provide better performance by avoiding redundant calculations and improving time complexity. +*/ +func NumberOfWaysToTraverseGraphRecursive(width int, height int) int { + if width == 1 || height == 1 { + return 1 + } + return NumberOfWaysToTraverseGraph(width - 1, height) + NumberOfWaysToTraverseGraph(width, height - 1) +} diff --git a/Dynamic Programming/num_ways_to_traverse_graph.js b/Dynamic Programming/num_ways_to_traverse_graph.js new file mode 100644 index 00000000..38316f93 --- /dev/null +++ b/Dynamic Programming/num_ways_to_traverse_graph.js @@ -0,0 +1,182 @@ +/* + + You're given two positive integers representing the width and height of a grid-shaped, rectangular graph. + Write a function that returns the number of ways to reach the bottom right corner of the graph when starting + at the top left corner. Each move you take must either go down or right. In other words, you can never move up + or left in the graph. + + For example, given the graph illustrated below, with width = 2 and height = 3 , there are three ways to + reach the bottom right corner when starting at the top left corner: + _ _ + |_|_| + |_|_| + |_|_| + Down Down Right + Right Down Down + Down Right Down + + Sample Input: Width : 4 height: 3 + Output: 10 + + Explanation: + The code snippet implements the `NumberOfWaysToTraverseGraph` function, which calculates the number of ways to + traverse a 2D graph from the top-left corner to the bottom-right corner. The graph has a given width and height. + + Here's a breakdown of the code: + + 1. The function takes two parameters: `width` and `height`, representing the dimensions of the graph. + + 2. It initializes a 2D slice called `numberOfWays` with dimensions `(height+1) x (width+1)`. The additional "+1" is to + account for the boundary cases when traversing the graph. + + 3. It enters a nested loop to iterate over the graph cells. The outer loop iterates over the width indices (`widthIdx`), + and the inner loop iterates over the height indices (`heightIdx`). + + 4. For each cell, it checks if it is on the top row (`heightIdx == 1`) or the leftmost column (`widthIdx == 1`). If so, it + means that there is only one way to reach that cell, either by moving right or moving down. Therefore, it sets `numberOfWays[heightIdx][widthIdx]` to 1. + + 5. If the cell is not on the top row or the leftmost column, it means that it can be reached by either moving from the + cell above (up) or the cell to the left (left). The number of ways to reach the current cell is the sum of the number of + ways to reach the cell above and the number of ways to reach the cell to the left. This value is stored in + `numberOfWays[heightIdx][widthIdx]`. + + 6. After iterating over all cells, the function returns the value stored in the bottom-right corner of `numberOfWays`, + which represents the total number of ways to traverse the graph. + + The algorithm uses dynamic programming to build the `numberOfWays` matrix iteratively, starting from the top-left corner + and moving towards the bottom-right corner. By calculating the number of ways to reach each cell based on the number of ways to reach its neighboring cells, it avoids redundant calculations and computes the result efficiently. + + The time complexity of the algorithm is O(width * height) since it iterates over all cells of the graph. + + The space complexity is also O(width * height) since it uses the `numberOfWays` matrix to store intermediate results. + + +*/ +function numberOfWaysToTraverseGraph(width, height) { + // Create a 2D array to store the number of ways to reach each cell + const numberOfWays = new Array(height) + .fill(1) + .map(() => new Array(width).fill(1)); + + // Iterate through the cells from top to bottom and left to right + for (let i = 1; i < height; i++) { + for (let j = 1; j < width; j++) { + // Calculate the number of ways to reach the current cell + // by summing the number of ways from the cell above and the cell to the left + numberOfWays[i][j] = numberOfWays[i - 1][j] + numberOfWays[i][j - 1]; + } + } + + // Return the number of ways to reach the bottom-right corner of the graph + return numberOfWays[height - 1][width - 1]; +} + +/* + Combinatorics Solution + + The given code snippet aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right + corner. Let's break down the solution and provide a detailed explanation: + + 1. The `NumberOfWaysToTraverseGraph` function takes two parameters: `width` and `height`, representing the dimensions of + the graph. + + 2. The variables `xDistanceToCorner` and `yDistanceToCorner` are calculated by subtracting 1 from the `width` and `height` + respectively. These variables represent the distances from the top-left corner to the bottom-right corner along the x-axis and y-axis. + + 3. The `factorial` function is defined separately to calculate the factorial of a number. It takes a number `num` as input + and uses an iterative approach to calculate the factorial. + + 4. In the `NumberOfWaysToTraverseGraph` function, the numerator is calculated as the factorial of the sum of + `xDistanceToCorner` and `yDistanceToCorner`. This represents the total number of possible paths from the top-left + corner to the bottom-right corner. + + 5. The denominator is calculated as the product of the factorials of `xDistanceToCorner` and `yDistanceToCorner`. + This represents the number of ways to arrange the steps along the x-axis and y-axis. + + 6. Finally, the function returns the result by dividing the numerator by the denominator, giving the total number of + ways to traverse the graph. + + The solution relies on the concept of combinatorics, specifically the binomial coefficient, to calculate the number of + ways to traverse the graph. By using factorials, it accounts for all possible paths and eliminates duplicate paths. + This approach provides an efficient solution to the problem. + + O(n + m) time | O(1) space - where n is the width of the graph and m is the height +*/ + +function numberOfWaysToTraverseGraphCombinatorics(width, height) { + // Calculate the distances to the bottom-right corner of the graph + const xDistanceToCorner = width - 1; + const yDistanceToCorner = height - 1; + + // Calculate the numerator and denominator for the binomial coefficient + const numerator = factorial(xDistanceToCorner + yDistanceToCorner); + const denominator = + factorial(xDistanceToCorner) * factorial(yDistanceToCorner); + + // Return the result by dividing the numerator by the denominator + return numerator / denominator; +} + +function factorial(n) { + // Base case: factorial of 0 or 1 is 1 + if (n <= 1) { + return 1; + } + + // Recursive case: compute factorial by multiplying n with factorial(n-1) + return n * factorial(n - 1); +} + +/* + Recursive solution + The given solution aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right + corner. It uses a recursive approach to break down the problem into smaller subproblems. + + Here's how the solution works: + + 1. The function `NumberOfWaysToTraverseGraph` takes the width and height of the graph as input and returns the number of ways to traverse it. + + 2. The base case of the recursion is when either the width or height is equal to 1. In this case, there is only one way to traverse the graph: either by moving only horizontally or vertically. Therefore, the function returns 1. + + 3. For other cases where the width and height are both greater than 1, the function recursively calls itself with two smaller subproblems: + - One subproblem is created by reducing the width by 1 and keeping the same height. + - The other subproblem is created by keeping the same width and reducing the height by 1. + + 4. The number of ways to traverse the current graph is calculated by summing up the number of ways from the two subproblems. + + 5. The recursion continues until it reaches the base case, where the width or height becomes 1, and eventually returns the total number of ways to traverse the graph. + + While this recursive approach is conceptually simple, it suffers from efficiency issues due to exponential time complexity and redundant + calculations. As the graph size increases, the number of recursive calls grows exponentially, leading to a significant increase in computation time. Additionally, without memoization, the function recalculates the same subproblems multiple times, further reducing efficiency. + + To address these drawbacks, alternative approaches like dynamic programming or memoization can be employed to store and + reuse the results of previously solved subproblems, avoiding redundant calculations and improving efficiency. + + The given solution uses a recursive approach to calculate the number of ways to traverse a graph from the top-left corner + to the bottom-right corner. However, this solution has some drawbacks that make it inefficient for larger inputs: + + 1. Exponential Time Complexity: The recursive function makes multiple recursive calls, each with a smaller width or height. + As a result, the number of function calls grows exponentially with the size of the input. This leads to a high time complexity, making the solution inefficient for larger graphs. The time complexity is O(2^(width+height)), which can quickly become unmanageable. + + 2. Overlapping Subproblems: The recursive function suffers from redundant calculations of the same subproblems. For example, + when calculating the number of ways for a specific width and height, the function may recursively calculate the number of ways for smaller widths and heights multiple times. This leads to redundant work and decreases efficiency. + + 3. Lack of Memoization: The solution does not utilize memoization to store the results of previously solved subproblems. + Without memoization, the recursive function ends up recalculating the same subproblems multiple times, further reducing efficiency. + + Due to these reasons, the given recursive solution is considered inefficient and impractical for larger graph sizes. + It is prone to exponential time complexity and redundant calculations, making it unsuitable for real-world scenarios where efficiency is crucial. Alternative approaches, such as the dynamic programming solution mentioned earlier, can provide better performance by avoiding redundant calculations and improving time complexity. +*/ + +function numberOfWaysToTraverseGraphRecursive(width, height) { + // Base case: when the width or height is 1, there is only one way to reach the destination + if (width === 1 || height === 1) { + return 1; + } + + // Recursive case: sum the number of ways from the cell above and the cell to the left + return ( + numberOfWaysToTraverseGraphRecursive(width - 1, height) + + numberOfWaysToTraverseGraphRecursive(width, height - 1) + ); +} diff --git a/Dynamic Programming/num_ways_to_traverse_graph.py b/Dynamic Programming/num_ways_to_traverse_graph.py new file mode 100644 index 00000000..f7933554 --- /dev/null +++ b/Dynamic Programming/num_ways_to_traverse_graph.py @@ -0,0 +1,168 @@ +''' + You're given two positive integers representing the width and height of a grid-shaped, rectangular graph. + Write a function that returns the number of ways to reach the bottom right corner of the graph when starting + at the top left corner. Each move you take must either go down or right. In other words, you can never move up + or left in the graph. + + For example, given the graph illustrated below, with width = 2 and height = 3 , there are three ways to + reach the bottom right corner when starting at the top left corner: + _ _ + |_|_| + |_|_| + |_|_| + Down Down Right + Right Down Down + Down Right Down + + Sample Input: Width : 4 height: 3 + Output: 10 + + Explanation: + The code snippet implements the `NumberOfWaysToTraverseGraph` function, which calculates the number of ways to + traverse a 2D graph from the top-left corner to the bottom-right corner. The graph has a given width and height. + + Here's a breakdown of the code: + + 1. The function takes two parameters: `width` and `height`, representing the dimensions of the graph. + + 2. It initializes a 2D slice called `numberOfWays` with dimensions `(height+1) x (width+1)`. The additional "+1" is to + account for the boundary cases when traversing the graph. + + 3. It enters a nested loop to iterate over the graph cells. The outer loop iterates over the width indices (`widthIdx`), + and the inner loop iterates over the height indices (`heightIdx`). + + 4. For each cell, it checks if it is on the top row (`heightIdx == 1`) or the leftmost column (`widthIdx == 1`). If so, it + means that there is only one way to reach that cell, either by moving right or moving down. Therefore, it sets `numberOfWays[heightIdx][widthIdx]` to 1. + + 5. If the cell is not on the top row or the leftmost column, it means that it can be reached by either moving from the + cell above (up) or the cell to the left (left). The number of ways to reach the current cell is the sum of the number of + ways to reach the cell above and the number of ways to reach the cell to the left. This value is stored in + `numberOfWays[heightIdx][widthIdx]`. + + 6. After iterating over all cells, the function returns the value stored in the bottom-right corner of `numberOfWays`, + which represents the total number of ways to traverse the graph. + + The algorithm uses dynamic programming to build the `numberOfWays` matrix iteratively, starting from the top-left corner + and moving towards the bottom-right corner. By calculating the number of ways to reach each cell based on the number of ways to reach its neighboring cells, it avoids redundant calculations and computes the result efficiently. + + The time complexity of the algorithm is O(width * height) since it iterates over all cells of the graph. + + The space complexity is also O(width * height) since it uses the `numberOfWays` matrix to store intermediate results. + +''' + +def number_of_ways_to_traverse_graph(width, height): + # Initialize the numberOfWays matrix with dimensions (height+1) x (width+1) + # The extra "+1" is to account for the boundary cases when traversing the graph + numberOfWays = [[0] * (width + 1) for _ in range(height + 1)] + + # Iterate over the graph cells + for widthIdx in range(1, width + 1): + for heightIdx in range(1, height + 1): + # Check if the current cell is on the top row or the leftmost column + if widthIdx == 1 or heightIdx == 1: + # If so, there is only one way to reach this cell (moving right or moving down) + numberOfWays[heightIdx][widthIdx] = 1 + else: + # If the cell is not on the top row or the leftmost column, + # calculate the number of ways to reach this cell based on the + # number of ways to reach the cell above (up) and the cell to the left (left) + waysLeft = numberOfWays[heightIdx][widthIdx - 1] + waysUp = numberOfWays[heightIdx - 1][widthIdx] + numberOfWays[heightIdx][widthIdx] = waysLeft + waysUp + + # Return the number of ways to reach the bottom-right corner of the graph + return numberOfWays[height][width] + +''' + Combinatorics Solution + + The given code snippet aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right + corner. Let's break down the solution and provide a detailed explanation: + + 1. The `NumberOfWaysToTraverseGraph` function takes two parameters: `width` and `height`, representing the dimensions of + the graph. + + 2. The variables `xDistanceToCorner` and `yDistanceToCorner` are calculated by subtracting 1 from the `width` and `height` + respectively. These variables represent the distances from the top-left corner to the bottom-right corner along the x-axis and y-axis. + + 3. The `factorial` function is defined separately to calculate the factorial of a number. It takes a number `num` as input + and uses an iterative approach to calculate the factorial. + + 4. In the `NumberOfWaysToTraverseGraph` function, the numerator is calculated as the factorial of the sum of + `xDistanceToCorner` and `yDistanceToCorner`. This represents the total number of possible paths from the top-left + corner to the bottom-right corner. + + 5. The denominator is calculated as the product of the factorials of `xDistanceToCorner` and `yDistanceToCorner`. + This represents the number of ways to arrange the steps along the x-axis and y-axis. + + 6. Finally, the function returns the result by dividing the numerator by the denominator, giving the total number of + ways to traverse the graph. + + The solution relies on the concept of combinatorics, specifically the binomial coefficient, to calculate the number of + ways to traverse the graph. By using factorials, it accounts for all possible paths and eliminates duplicate paths. + This approach provides an efficient solution to the problem. + + O(n + m) time | O(1) space - where n is the width of the graph and m is the height +''' + +import math + +def number_of_ways_to_traverse_graph_combinatorics(width, height): + # Calculate the distance to the bottom-right corner of the graph + x_distance_to_corner = width - 1 + y_distance_to_corner = height - 1 + + # Calculate the number of ways to traverse the graph using combinatorics + # by calculating the binomial coefficient of (x_distance_to_corner + y_distance_to_corner) choose x_distance_to_corner + # where (n choose k) = n! / (k! * (n-k)!) + numerator = math.factorial(x_distance_to_corner + y_distance_to_corner) + denominator = math.factorial(x_distance_to_corner) * math.factorial(y_distance_to_corner) + + # Return the result by dividing the numerator by the denominator + return numerator // denominator + +''' + Recursive solution + The given solution aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right + corner. It uses a recursive approach to break down the problem into smaller subproblems. + + Here's how the solution works: + + 1. The function `NumberOfWaysToTraverseGraph` takes the width and height of the graph as input and returns the number of ways to traverse it. + + 2. The base case of the recursion is when either the width or height is equal to 1. In this case, there is only one way to traverse the graph: either by moving only horizontally or vertically. Therefore, the function returns 1. + + 3. For other cases where the width and height are both greater than 1, the function recursively calls itself with two smaller subproblems: + - One subproblem is created by reducing the width by 1 and keeping the same height. + - The other subproblem is created by keeping the same width and reducing the height by 1. + + 4. The number of ways to traverse the current graph is calculated by summing up the number of ways from the two subproblems. + + 5. The recursion continues until it reaches the base case, where the width or height becomes 1, and eventually returns the total number of ways to traverse the graph. + + While this recursive approach is conceptually simple, it suffers from efficiency issues due to exponential time complexity and redundant + calculations. As the graph size increases, the number of recursive calls grows exponentially, leading to a significant increase in computation time. Additionally, without memoization, the function recalculates the same subproblems multiple times, further reducing efficiency. + + To address these drawbacks, alternative approaches like dynamic programming or memoization can be employed to store and + reuse the results of previously solved subproblems, avoiding redundant calculations and improving efficiency. + + The given solution uses a recursive approach to calculate the number of ways to traverse a graph from the top-left corner + to the bottom-right corner. However, this solution has some drawbacks that make it inefficient for larger inputs: + + 1. Exponential Time Complexity: The recursive function makes multiple recursive calls, each with a smaller width or height. + As a result, the number of function calls grows exponentially with the size of the input. This leads to a high time complexity, making the solution inefficient for larger graphs. The time complexity is O(2^(width+height)), which can quickly become unmanageable. + + 2. Overlapping Subproblems: The recursive function suffers from redundant calculations of the same subproblems. For example, + when calculating the number of ways for a specific width and height, the function may recursively calculate the number of ways for smaller widths and heights multiple times. This leads to redundant work and decreases efficiency. + + 3. Lack of Memoization: The solution does not utilize memoization to store the results of previously solved subproblems. + Without memoization, the recursive function ends up recalculating the same subproblems multiple times, further reducing efficiency. + + Due to these reasons, the given recursive solution is considered inefficient and impractical for larger graph sizes. + It is prone to exponential time complexity and redundant calculations, making it unsuitable for real-world scenarios where efficiency is crucial. Alternative approaches, such as the dynamic programming solution mentioned earlier, can provide better performance by avoiding redundant calculations and improving time complexity. +''' +def number_of_ways_to_traverse_graph_recursive(width, height): + if width == 1 or height == 1: + return 1 + return number_of_ways_to_traverse_graph_recursive(width - 1, height) + number_of_ways_to_traverse_graph_recursive(width, height - 1) diff --git a/Dynamic Programming/numbers_in_pi.go b/Dynamic Programming/numbers_in_pi.go new file mode 100644 index 00000000..98ac432d --- /dev/null +++ b/Dynamic Programming/numbers_in_pi.go @@ -0,0 +1,103 @@ +/* + + Given a string representation of the first n digits of Pi and a list of positive integers (all in string format), write a function that returns the + smallest number of spaces that can be added to the n digits of Pi such that all resulting numbers are found in the list of integers. + + Sample Input pi = "3141592653589793238462643383279" + numbers : ["314159265358979323846", "26433", "8", "3279", "314159265", "35897932384626433832", "79"] + + Output: 2 + + + Explanation: + + The given code snippet is for solving the "Numbers in Pi" problem using a recursive approach with memoization (dynamic programming) to find the minimum number of spaces required to divide the given string representation of pi into valid numbers from a list of given numbers. + + The problem is as follows: Given a string representation of the irrational number pi and a list of numbers, find the minimum number of spaces required to divide the string into valid numbers such that each number is present in the given list. + + Let's go through the code step by step: + + 1. `NumbersInPi` function: + - This is the main function that takes the string representation of pi and a list of numbers as input and returns the minimum number of spaces required. It initializes a `numbersTable` to store the numbers from the input list for quick lookup and then calls the `getMinSpaces` function with initial parameters. + + 2. `getMinSpaces` function: + - This is a recursive function with memoization. It takes the string representation of pi, the `numbersTable`, a `cache` (a map to store previously calculated values to avoid redundant calculations), and the current `idx` (position in the pi string) as input. + - It first checks if the base case has been reached by comparing `idx` with the length of the pi string. If so, it returns -1. + - Next, it checks if the result for the current `idx` is already present in the cache. If yes, it returns the cached result. + - If the base case is not reached and the result is not in the cache, it initializes a variable `minSpaces` to store the minimum spaces required for the current `idx`. It sets `minSpaces` to a large value (initialized as `math.MaxInt32`) to ensure correct comparisons later. + - Then, it iterates from the current `idx` to the end of the pi string and forms a prefix string from `idx` to the current iteration index (`i`). + - If the prefix string is found in the `numbersTable`, it means it is a valid number from the given list. The function then recursively calls itself with the suffix (remaining part) of the pi string starting from index `i+1`. + - The result of the recursive call is stored in `minSpacesInSuffix`. + - The minimum of `minSpaces` and `minSpacesInSuffix + 1` is computed and assigned back to `minSpaces`. The "+1" indicates the current valid number prefix, which requires one space. + - The loop continues, trying all possible valid prefixes from the current index. + - Finally, the `minSpaces` value is stored in the `cache` to avoid redundant calculations and returned as the result for the current `idx`. + + 3. `min` function: + - A simple utility function to return the minimum of two integers. + + The `NumbersInPi` function is the entry point, and the `getMinSpaces` function handles the recursive computation with memoization. + By using memoization, the code optimizes and reduces redundant calculations, making it more efficient than a pure + recursive solution. The result returned by `NumbersInPi` is the minimum number of spaces required to divide the pi string + into valid numbers from the given list. If it is not possible to form valid numbers using the given list, + the function returns -1. +*/ +package main + +import "math" + +// NumbersInPi finds the minimum number of spaces needed to divide the pi string +// into valid numbers from the given list of numbers. +func NumbersInPi(pi string, numbers []string) int { + numbersTable := map[string]bool{} + for _, number := range numbers { + numbersTable[number] = true + } + + // Cache to store results of subproblems to avoid redundant calculations + cache := map[int]int{} + minSpaces := getMinSpaces(pi, numbersTable, cache, 0) + + if minSpaces == math.MaxInt32 { + return -1 + } + return minSpaces +} + +// getMinSpaces calculates the minimum number of spaces needed to divide the remaining +// suffix of the pi string into valid numbers from the numbersTable. +func getMinSpaces(pi string, numbersTable map[string]bool, cache map[int]int, idx int) int { + // Base case: If the end of the pi string is reached, return -1. + // This indicates that the suffix of the pi string cannot be divided into valid numbers. + if idx == len(pi) { + return -1 + } else if val, found := cache[idx]; found { + // If the result for the current index is already in the cache, return it. + return val + } + + minSpaces := math.MaxInt32 + // Iterate over possible prefixes starting from the current index. + for i := idx; i < len(pi); i++ { + prefix := pi[idx : i+1] + + // If the prefix is found in the numbersTable, it is a valid number prefix. + if _, found := numbersTable[prefix]; found { + // Recursively calculate the minimum number of spaces in the suffix. + minSpacesInSuffix := getMinSpaces(pi, numbersTable, cache, i+1) + // Update the minimum spaces with the current prefix if it leads to a valid number. + minSpaces = min(minSpaces, minSpacesInSuffix+1) + } + } + + // Cache the result for the current index to avoid redundant calculations. + cache[idx] = minSpaces + return cache[idx] +} + +// min returns the minimum of two integers. +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/Dynamic Programming/reconstruct_bst_in_python.py b/Dynamic Programming/reconstruct_bst_in_python.py new file mode 100644 index 00000000..24610a73 --- /dev/null +++ b/Dynamic Programming/reconstruct_bst_in_python.py @@ -0,0 +1,36 @@ +class TreeNode: + def __init__(self, val): + self.val = val + self.left = None + self.right = None + +def constructBST(preorder): + if not preorder: + return None + + root = TreeNode(preorder[0]) + + i = 1 + while i < len(preorder) and preorder[i] < root.val: + i += 1 + + root.left = constructBST(preorder[1:i]) + root.right = constructBST(preorder[i:]) + + return root + +def inorderTraversal(root): + if root is None: + return [] + + return inorderTraversal(root.left) + [root.val] + inorderTraversal(root.right) + +# Sample Input +preorder = [10, 4, 2, 1, 5, 17, 19, 18] + +# Construct the BST +root = constructBST(preorder) + +# Print the inorder traversal of the reconstructed BST +inorder = inorderTraversal(root) +print(inorder) diff --git a/Dynamic_Programming/rod_cutting_problem_dp.cpp b/Dynamic Programming/rod_cutting_problem_dp.cpp similarity index 97% rename from Dynamic_Programming/rod_cutting_problem_dp.cpp rename to Dynamic Programming/rod_cutting_problem_dp.cpp index 938da9ab..93096ff3 100644 --- a/Dynamic_Programming/rod_cutting_problem_dp.cpp +++ b/Dynamic Programming/rod_cutting_problem_dp.cpp @@ -1,35 +1,35 @@ - /* - Given a rod of length n inches and an array of prices that contains prices of all - pieces of size smaller than n. Determine the maximum value obtainable by cutting - up the rod and selling the pieces. For example, if length of the rod is 8 and the - values of different pieces are given as following, then the maximum obtainable - value is 22 (by cutting in two pieces of lengths 2 and 6) - Input : 8 - : 1 5 8 9 10 17 17 20 - Output : 22 -*/ -// Dynamic Programming solution TC : O(n^2) -// Program Author: Abhisek Kumar Gupta -#include -using namespace std; -int max_profit(vector profit, int total_length){ - int dp[100] = {}; - for(int length = 1; length <= total_length; length++){ - int best = 0; - for(int cut = 1; cut <= length; cut++){ - best = max(best, profit[cut] + dp[length - cut]); - } - dp[length] = best; - } - return dp[total_length]; -} -int main(){ - int total_length; - cin >> total_length; - vector profit(total_length + 1); - for(int length = 1; length <= total_length; length++) - cin >> profit[length]; - int result = max_profit(profit, total_length); - cout << result; - return 0; + /* + Given a rod of length n inches and an array of prices that contains prices of all + pieces of size smaller than n. Determine the maximum value obtainable by cutting + up the rod and selling the pieces. For example, if length of the rod is 8 and the + values of different pieces are given as following, then the maximum obtainable + value is 22 (by cutting in two pieces of lengths 2 and 6) + Input : 8 + : 1 5 8 9 10 17 17 20 + Output : 22 +*/ +// Dynamic Programming solution TC : O(n^2) +// Program Author: Abhisek Kumar Gupta +#include +using namespace std; +int max_profit(vector profit, int total_length){ + int dp[100] = {}; + for(int length = 1; length <= total_length; length++){ + int best = 0; + for(int cut = 1; cut <= length; cut++){ + best = max(best, profit[cut] + dp[length - cut]); + } + dp[length] = best; + } + return dp[total_length]; +} +int main(){ + int total_length; + cin >> total_length; + vector profit(total_length + 1); + for(int length = 1; length <= total_length; length++) + cin >> profit[length]; + int result = max_profit(profit, total_length); + cout << result; + return 0; } \ No newline at end of file diff --git a/Dynamic_Programming/rod_cutting_problem_memoized.cpp b/Dynamic Programming/rod_cutting_problem_memoized.cpp similarity index 97% rename from Dynamic_Programming/rod_cutting_problem_memoized.cpp rename to Dynamic Programming/rod_cutting_problem_memoized.cpp index 94d51ccb..3b15e569 100644 --- a/Dynamic_Programming/rod_cutting_problem_memoized.cpp +++ b/Dynamic Programming/rod_cutting_problem_memoized.cpp @@ -1,37 +1,37 @@ -/* - Given a rod of length n inches and an array of prices that contains prices of all - pieces of size smaller than n. Determine the maximum value obtainable by cutting - up the rod and selling the pieces. For example, if length of the rod is 8 and the - values of different pieces are given as following, then the maximum obtainable - value is 22 (by cutting in two pieces of lengths 2 and 6) - Input : 8 - : 1 5 8 9 10 17 17 20 - Output : 22 -*/ -// Memoized solution TC : O(n^2) -// Program Author: Abhisek Kumar Gupta -#include -using namespace std; -int memoized[1000]; -int max_profit(vector length, int n){ - if(n == 0) return 0; - int best = 0; - if(memoized[n] != -1) return memoized[n]; - for(int i = 0; i < n; i++){ - int total_profit = length[i] + max_profit(length, n - (i + 1)); - best = max(best, total_profit); - memoized[n] = best; - } - return memoized[n]; -} -int main(){ - memset(memoized, -1, sizeof(memoized)); - int n; - cin >> n; - vector length(n); - for(int i = 0; i < n; i++) - cin >> length[i]; - int result = max_profit(length, n); - cout << result; - return 0; +/* + Given a rod of length n inches and an array of prices that contains prices of all + pieces of size smaller than n. Determine the maximum value obtainable by cutting + up the rod and selling the pieces. For example, if length of the rod is 8 and the + values of different pieces are given as following, then the maximum obtainable + value is 22 (by cutting in two pieces of lengths 2 and 6) + Input : 8 + : 1 5 8 9 10 17 17 20 + Output : 22 +*/ +// Memoized solution TC : O(n^2) +// Program Author: Abhisek Kumar Gupta +#include +using namespace std; +int memoized[1000]; +int max_profit(vector length, int n){ + if(n == 0) return 0; + int best = 0; + if(memoized[n] != -1) return memoized[n]; + for(int i = 0; i < n; i++){ + int total_profit = length[i] + max_profit(length, n - (i + 1)); + best = max(best, total_profit); + memoized[n] = best; + } + return memoized[n]; +} +int main(){ + memset(memoized, -1, sizeof(memoized)); + int n; + cin >> n; + vector length(n); + for(int i = 0; i < n; i++) + cin >> length[i]; + int result = max_profit(length, n); + cout << result; + return 0; } \ No newline at end of file diff --git a/Dynamic_Programming/rod_cutting_problem_recursive.cpp b/Dynamic Programming/rod_cutting_problem_recursive.cpp similarity index 97% rename from Dynamic_Programming/rod_cutting_problem_recursive.cpp rename to Dynamic Programming/rod_cutting_problem_recursive.cpp index 154f30b8..59009a48 100644 --- a/Dynamic_Programming/rod_cutting_problem_recursive.cpp +++ b/Dynamic Programming/rod_cutting_problem_recursive.cpp @@ -1,33 +1,33 @@ -/* - Given a rod of length n inches and an array of prices that contains prices of all - pieces of size smaller than n. Determine the maximum value obtainable by cutting - up the rod and selling the pieces. For example, if length of the rod is 8 and the - values of different pieces are given as following, then the maximum obtainable - value is 22 (by cutting in two pieces of lengths 2 and 6) - Input : 8 - : 1 5 8 9 10 17 17 20 - Output : 22 -*/ -// Recursive solution TC : O(2^n) -// Program Author: Abhisek Kumar Gupta -#include -using namespace std; -int max_profit(vector length, int n){ - if(n == 0) return 0; - int best = 0; - for(int i = 0; i < n; i++){ - int total_profit = length[i] + max_profit(length, n - (i + 1)); - best = max(best, total_profit); - } - return best; -} -int main(){ - int n; - cin >> n; - vector length(n); - for(int i = 0; i < n; i++) - cin >> length[i]; - int result = max_profit(length, n); - cout << result; - return 0; -} +/* + Given a rod of length n inches and an array of prices that contains prices of all + pieces of size smaller than n. Determine the maximum value obtainable by cutting + up the rod and selling the pieces. For example, if length of the rod is 8 and the + values of different pieces are given as following, then the maximum obtainable + value is 22 (by cutting in two pieces of lengths 2 and 6) + Input : 8 + : 1 5 8 9 10 17 17 20 + Output : 22 +*/ +// Recursive solution TC : O(2^n) +// Program Author: Abhisek Kumar Gupta +#include +using namespace std; +int max_profit(vector length, int n){ + if(n == 0) return 0; + int best = 0; + for(int i = 0; i < n; i++){ + int total_profit = length[i] + max_profit(length, n - (i + 1)); + best = max(best, total_profit); + } + return best; +} +int main(){ + int n; + cin >> n; + vector length(n); + for(int i = 0; i < n; i++) + cin >> length[i]; + int result = max_profit(length, n); + cout << result; + return 0; +} diff --git a/Dynamic Programming/trapping_rain_water.cpp b/Dynamic Programming/trapping_rain_water.cpp new file mode 100644 index 00000000..3bcc198d --- /dev/null +++ b/Dynamic Programming/trapping_rain_water.cpp @@ -0,0 +1,48 @@ + +/* +Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining. + +Example 1: + +Input: height = [0,1,0,2,1,0,1,3,2,1,2,1] +Output: 6 + +Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. +Example 2: + +Input: height = [4,2,0,3,2,5] +Output: 9 + +Constraints: + +n == height.length +1 <= n <= 2 * 104 +0 <= height[i] <= 105 +*/ +#include + +class Solution { +public: + int trap(vector& height) { + int len = height.size(), result = 0; + if(len == 0) return 0; + int low = 0, high = len - 1, leftmax = 0, rightmax = 0; + while(low <= high){ + if(height[low] < height[high]){ + if(height[low] > leftmax) + leftmax = height[low]; + else + result += leftmax - height[low]; + low++; + } + else{ + if(height[high] > rightmax) + rightmax = height[high]; + else + result += rightmax - height[high]; + high--; + } + } + return result; + } +}; \ No newline at end of file diff --git a/Dynamic Programming/unique_paths_with_obstacles.cpp b/Dynamic Programming/unique_paths_with_obstacles.cpp new file mode 100644 index 00000000..01a9ce9b --- /dev/null +++ b/Dynamic Programming/unique_paths_with_obstacles.cpp @@ -0,0 +1,34 @@ +/* + You are given an m x n integer array grid. There is a robot initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time. + + An obstacle and space are marked as 1 or 0 respectively in grid. A path that the robot takes cannot include any square that is an obstacle. + + Return the number of possible unique paths that the robot can take to reach the bottom-right corner. + + The testcases are generated so that the answer will be less than or equal to 2 * 109. + + Example 1: + Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] + Output: 2 + Explanation: There is one obstacle in the middle of the 3x3 grid above. + There are two ways to reach the bottom-right corner: + 1. Right -> Right -> Down -> Down + 2. Down -> Down -> Right -> Right +*/ + +class Solution { +public: + int uniquePathsWithObstacles(vector>& obstacleGrid) { + int m = obstacleGrid.size(), n = obstacleGrid[0].size(); + vector >dp(m + 1, vector (n + 1, 0)); + dp[0][1] = 1; + for(int i = 1; i<= m; i++){ + for(int j = 1; j <= n; j++){ + if(!obstacleGrid[i - 1][j - 1]){ + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + } + return dp[m][n]; + } +}; \ No newline at end of file diff --git a/Dynamic_Programming/wine_selling_problem_dp.cpp b/Dynamic Programming/wine_selling_problem_dp.cpp similarity index 96% rename from Dynamic_Programming/wine_selling_problem_dp.cpp rename to Dynamic Programming/wine_selling_problem_dp.cpp index f8ebbf59..48b7f751 100644 --- a/Dynamic_Programming/wine_selling_problem_dp.cpp +++ b/Dynamic Programming/wine_selling_problem_dp.cpp @@ -1,57 +1,57 @@ -/* - Given n wines in a row, with integers denoting the cost of each wine respectively. - Each year you can sale the first or the last wine in the row. However, the price - of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. - On the Yth year, the profit from the ith wine will be Y*Pi. - Calculate the maximum profit from all the wines. - - Input : 5 - : 2 4 6 2 5 - Output : 64 -*/ -// Dynamic Programming Approach TC : O(N^2) -// Program Author : Abhisek Kumar Gupta - -#include -using namespace std; -int find_max_profit(int *A, int n){ - int dp[100][100] = {}; - int year = n; - for(int i = 0; i < n; i++){ - dp[i][i] = year * A[i]; - } - year--; - for(int i = 2; i <= n; i++){ - int start = 0; - int end = n - i; - while(start <= end){ - int end_window = start + i - 1; - int x = A[start] * year + dp[start + 1][end_window]; - int y = A[end_window]* year + dp[start][end_window - 1]; - dp[start][end_window] = max(x, y); - start++; - } - year--; - } - /* for(int i = 0; i < n; i++){ - for(int j = 0; j < n; j++){ - cout << setw(5) << dp[i][j] << " "; - } - cout << "\n"; - } - */ - return dp[0][n-1]; -} -int main(){ - int n; - cin >> n; - int *A; - for(int i = 0; i < n; i++) - cin >> A[i]; - int start = 0; - int end = n - 1; - int year = 1; - int result = find_max_profit(A, n); - cout << result; - return 0; -} +/* + Given n wines in a row, with integers denoting the cost of each wine respectively. + Each year you can sale the first or the last wine in the row. However, the price + of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. + On the Yth year, the profit from the ith wine will be Y*Pi. + Calculate the maximum profit from all the wines. + + Input : 5 + : 2 4 6 2 5 + Output : 64 +*/ +// Dynamic Programming Approach TC : O(N^2) +// Program Author : Abhisek Kumar Gupta + +#include +using namespace std; +int find_max_profit(int *A, int n){ + int dp[100][100] = {}; + int year = n; + for(int i = 0; i < n; i++){ + dp[i][i] = year * A[i]; + } + year--; + for(int i = 2; i <= n; i++){ + int start = 0; + int end = n - i; + while(start <= end){ + int end_window = start + i - 1; + int x = A[start] * year + dp[start + 1][end_window]; + int y = A[end_window]* year + dp[start][end_window - 1]; + dp[start][end_window] = max(x, y); + start++; + } + year--; + } + /* for(int i = 0; i < n; i++){ + for(int j = 0; j < n; j++){ + cout << setw(5) << dp[i][j] << " "; + } + cout << "\n"; + } + */ + return dp[0][n-1]; +} +int main(){ + int n; + cin >> n; + int *A; + for(int i = 0; i < n; i++) + cin >> A[i]; + int start = 0; + int end = n - 1; + int year = 1; + int result = find_max_profit(A, n); + cout << result; + return 0; +} diff --git a/Dynamic_Programming/wine_selling_problem_memoized.cpp b/Dynamic Programming/wine_selling_problem_memoized.cpp similarity index 96% rename from Dynamic_Programming/wine_selling_problem_memoized.cpp rename to Dynamic Programming/wine_selling_problem_memoized.cpp index 6cf1321b..1c773de5 100644 --- a/Dynamic_Programming/wine_selling_problem_memoized.cpp +++ b/Dynamic Programming/wine_selling_problem_memoized.cpp @@ -1,42 +1,42 @@ -/* - Given n wines in a row, with integers denoting the cost of each wine respectively. - Each year you can sale the first or the last wine in the row. However, the price - of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. - On the Yth year, the profit from the ith wine will be Y*Pi. - Calculate the maximum profit from all the wines. - - Input : 5 - : 2 4 6 2 5 - Output : 64 -*/ -// Memoized Approach TC : O(N^2) -// Program Author : Abhisek Kumar Gupta - -#include -using namespace std; -int memoized[1000][1000]; -int find_max_profit(int *A, int start, int end, int year){ - if(start > end) - return 0; - if(memoized[start][end] != -1) - return memoized[start][end]; - int r1 = A[start] * year + find_max_profit(A, start + 1, end, year + 1); - int r2 = A[end] * year + find_max_profit(A, start, end - 1, year + 1); - int answer = max(r1, r2); ; - memoized[start][end] = answer; - return memoized[start][end]; -} -int main(){ - memset(memoized, -1, sizeof(memoized)); - int n; - cin >> n; - int *A; - for(int i = 0; i < n; i++) - cin >> A[i]; - int start = 0; - int end = n - 1; - int year = 1; - int result = find_max_profit(A, start, end, year); - cout << result; - return 0; +/* + Given n wines in a row, with integers denoting the cost of each wine respectively. + Each year you can sale the first or the last wine in the row. However, the price + of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. + On the Yth year, the profit from the ith wine will be Y*Pi. + Calculate the maximum profit from all the wines. + + Input : 5 + : 2 4 6 2 5 + Output : 64 +*/ +// Memoized Approach TC : O(N^2) +// Program Author : Abhisek Kumar Gupta + +#include +using namespace std; +int memoized[1000][1000]; +int find_max_profit(int *A, int start, int end, int year){ + if(start > end) + return 0; + if(memoized[start][end] != -1) + return memoized[start][end]; + int r1 = A[start] * year + find_max_profit(A, start + 1, end, year + 1); + int r2 = A[end] * year + find_max_profit(A, start, end - 1, year + 1); + int answer = max(r1, r2); ; + memoized[start][end] = answer; + return memoized[start][end]; +} +int main(){ + memset(memoized, -1, sizeof(memoized)); + int n; + cin >> n; + int *A; + for(int i = 0; i < n; i++) + cin >> A[i]; + int start = 0; + int end = n - 1; + int year = 1; + int result = find_max_profit(A, start, end, year); + cout << result; + return 0; } \ No newline at end of file diff --git a/Dynamic_Programming/wine_selling_problem_recursive.cpp b/Dynamic Programming/wine_selling_problem_recursive.cpp similarity index 96% rename from Dynamic_Programming/wine_selling_problem_recursive.cpp rename to Dynamic Programming/wine_selling_problem_recursive.cpp index 7bb0ec80..3879d21b 100644 --- a/Dynamic_Programming/wine_selling_problem_recursive.cpp +++ b/Dynamic Programming/wine_selling_problem_recursive.cpp @@ -1,36 +1,36 @@ -/* - Given n wines in a row, with integers denoting the cost of each wine respectively. - Each year you can sale the first or the last wine in the row. However, the price - of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. - On the Yth year, the profit from the ith wine will be Y*Pi. - Calculate the maximum profit from all the wines. - - Input : 5 - : 2 4 6 2 5 - Output : 64 -*/ -// Recursive Approach TC : O(2^n) -// Program Author : Abhisek Kumar Gupta - -#include -using namespace std; -int find_max_profit(int *A, int start, int end, int year){ - if(start > end) - return 0; - int r1 = A[start] * year + find_max_profit(A, start + 1, end, year + 1); - int r2 = A[end] * year + find_max_profit(A, start, end - 1, year + 1); - return max(r1, r2); -} -int main(){ - int n; - cin >> n; - int *A; - for(int i = 0; i < n; i++) - cin >> A[i]; - int start = 0; - int end = n - 1; - int year = 1; - int result = find_max_profit(A, start, end, year); - cout << result; - return 0; +/* + Given n wines in a row, with integers denoting the cost of each wine respectively. + Each year you can sale the first or the last wine in the row. However, the price + of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. + On the Yth year, the profit from the ith wine will be Y*Pi. + Calculate the maximum profit from all the wines. + + Input : 5 + : 2 4 6 2 5 + Output : 64 +*/ +// Recursive Approach TC : O(2^n) +// Program Author : Abhisek Kumar Gupta + +#include +using namespace std; +int find_max_profit(int *A, int start, int end, int year){ + if(start > end) + return 0; + int r1 = A[start] * year + find_max_profit(A, start + 1, end, year + 1); + int r2 = A[end] * year + find_max_profit(A, start, end - 1, year + 1); + return max(r1, r2); +} +int main(){ + int n; + cin >> n; + int *A; + for(int i = 0; i < n; i++) + cin >> A[i]; + int start = 0; + int end = n - 1; + int year = 1; + int result = find_max_profit(A, start, end, year); + cout << result; + return 0; } \ No newline at end of file diff --git a/Dynamic_Programming/min_steps_to_reduce_a_number_to_one_memoized.cpp b/Dynamic_Programming/min_steps_to_reduce_a_number_to_one_memoized.cpp deleted file mode 100644 index 5b33c8c5..00000000 --- a/Dynamic_Programming/min_steps_to_reduce_a_number_to_one_memoized.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Minimum steps to reduce a number to one conditions are as follows -// a) subtract 1 [one operation] -// b) divide by 2 [one operation] -// c) divide by 3 [one operation] -// Recursive + Memoized solution -// Program Author : Abhisek Kumar Gupta -/* - Input : 10 - Output : 3 - Explanation : 10 reduced to 9 reduced to 3 reduced to 1 [total 3 operations] - Input : 15 - Output : 4 -*/ -#include -using namespace std; -int memoized[10004]; -int find_min_steps(int number){ - if(number == 1) - return 0; - int r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; - if(memoized[number] != -1) return memoized[number]; - r1 = 1 + find_min_steps(number - 1); - if(number % 2 == 0) - r2 = 1 + find_min_steps(number / 2); - if(number % 3 == 0) - r3 = 1 + find_min_steps(number / 3); - memoized[number] = min(r1, min(r2, r3)); - return memoized[number]; -} - - -int main(){ - int number; - cin >> number; - memset(memoized, -1, sizeof(memoized)); - int result = find_min_steps(number); - cout << result; - return 0; -} diff --git a/Famous Algorithms/N_queen.js b/Famous Algorithms/N_queen.js new file mode 100644 index 00000000..b83980b3 --- /dev/null +++ b/Famous Algorithms/N_queen.js @@ -0,0 +1,59 @@ +function solveNQueens(n) { + const board = new Array(n).fill().map(() => new Array(n).fill('.')); // Create an empty NxN chessboard + + const solutions = []; + + function isSafe(row, col) { + // Check if no other queens are in the same column + for (let i = 0; i < row; i++) { + if (board[i][col] === 'Q') { + return false; + } + } + + // Check upper-left diagonal + for (let i = row, j = col; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] === 'Q') { + return false; + } + } + + // Check upper-right diagonal + for (let i = row, j = col; i >= 0 && j < n; i--, j++) { + if (board[i][j] === 'Q') { + return false; + } + } + + return true; + } + + function solve(row) { + if (row === n) { + // Found a valid solution, push a copy of the board to the solutions array + solutions.push(board.map(row => row.join(''))); + return; + } + + for (let col = 0; col < n; col++) { + if (isSafe(row, col)) { + board[row][col] = 'Q'; // Place a queen + solve(row + 1); // Recursively move to the next row + board[row][col] = '.'; // Backtrack by removing the queen + } + } + } + + solve(0); // Start solving from the first row + + return solutions; +} + +// Example usage: +const n = 4; // Change this to the desired board size +const solutions = solveNQueens(n); + +console.log(`Solutions for ${n}-Queens:`); +for (const solution of solutions) { + console.log(solution); +} diff --git a/Famous Algorithms/euclidean_algorithm.java b/Famous Algorithms/euclidean_algorithm.java new file mode 100644 index 00000000..cc31a2e1 --- /dev/null +++ b/Famous Algorithms/euclidean_algorithm.java @@ -0,0 +1,27 @@ +//extended version of Euclid's algorithm to find GCD of 2 numbers +//runs in O(log N) time/space complexity for GCD of numbers a and b +//in comparison, the standard Euclidean algorithm runs in O (log (min (a,b))) + +public class euclidean_algorithm { + public static int euclid(int a, int b, int c, int d){ + if(a == 0){ + c = 0; + d = 0; + return b; + } + int c1 = 1,d1 = 1; + int result = euclid(b%a, a, c1, d1); + //update with recursive call + c = d1 - (b / a) * c1; + d = c1; + return result; + } + //driver + public static void main(String[] args) { + int c =1, d = 1; + int a = 45; + int b = 10; + int gcd = euclid(a, b, c, d); + System.out.print("gcd of "+ a+"," +b +" is equal to: " + gcd); + } +} diff --git a/Famous Algorithms/euclidean_algorithm.js b/Famous Algorithms/euclidean_algorithm.js new file mode 100644 index 00000000..0aaca194 --- /dev/null +++ b/Famous Algorithms/euclidean_algorithm.js @@ -0,0 +1,22 @@ +//extended version of Euclid's algorithm to find GCD of 2 numbers +//runs in O(log N) time/space complexity for GCD of numbers a and b +//in comparison, the standard Euclidean algorithm runs in O (log (min (a,b))) + + +function euclid(a,b,c,d){ + if(a == 0){ + c = 0; + d = 1; + return b; + } + let result = euclid(b%a,a,c,d); + //update with recursive values + c = d- (b / a) * c; + d = c; + return result; +} +//modify a and b to find gcd of any 2 numbers +let a = 450; +let b = 100; +let gcd = euclid(a,b,0,0); +console.log(`GCD of ${a}, ${b} is equal to ${gcd}`); diff --git a/Famous Algorithms/euclidean_algorithm.py b/Famous Algorithms/euclidean_algorithm.py new file mode 100644 index 00000000..081079bc --- /dev/null +++ b/Famous Algorithms/euclidean_algorithm.py @@ -0,0 +1,21 @@ +#extended version of Euclid's algorithm to find GCD of 2 numbers +#runs in O(log N) time/space complexity for GCD of numbers a and b +#in comparison, the standard Euclidean algorithm runs in O (log (min (a,b))) + +def euclidExtended(a,b): + if a==0: + return b,0, 1 + result, a1, b1 = euclidExtended(b%a,a) + + a2 = b1- (b//a) *a1 + b2 = a1 + return result, a2, b2 #used as input to recursive call, + +#example driver, change a and b as desired + +a,b = 45,10 +g,x,y = euclidExtended(a,b) +print("gcd of", a,"," ,b ,"is equal to: " , g) + + + diff --git a/Famous Algorithms/kadanes_algorithm.c++ b/Famous Algorithms/kadanes_algorithm.c++ new file mode 100644 index 00000000..c6b6fbdb --- /dev/null +++ b/Famous Algorithms/kadanes_algorithm.c++ @@ -0,0 +1,76 @@ +/* Name : Rajeev Kumar +Github username : Tonystark121 +Repository name : data-structures-and-algorithms +Problem : Kadane's algorithm in C++ +Issue Number : #1179 +Problem statement : Given an integer array nums, find the subarray with the largest sum, and return its sum. + +Sample testcases: + +Testcase 1 --> + +Input: number of elements in array = 8 +nums = [-2,-3,5,-1,-2,1,5,-3] +Output: 8 + +Testcase 2 --> +Input: number of elements in array = 5 +nums = [5,4,-1,7,8] +Output: 23 + +Time Complexity = O(n) +Space Complexity = O(1) + + +Explanation: +This code asks the user to enter the number of elements in an array, +and then prompts them to enter each element of the array one at a time. +Once the array is complete, the code applies the Kadane's algorithm to +find the maximum sum of any subarray within the array, and then prints +the result to the console. + +Kadane's algorithm is a way of finding the maximum sum of a contiguous subarray within an array, +and it does so by keeping track of the maximum sum seen so far as it iterates through the array. +At each step, it adds the current element to a running sum, and if that sum becomes negative, +it resets the running sum to zero. If the running sum is ever greater than the maximum seen so far, +it updates the maximum. Finally, it returns the maximum sum. +*/ + +// ----------------------------------------------------------------------------- code begins now! + + +#include +using namespace std; + +int main(){ + + // taking input number of array elements. + int n; + cin>>n; + + // taking input array elements. + int arr[n]; + for(int i=0;i>arr[i]; + } + + // declare current maximum and maximum so far variable. + int curr_max=0,max_so_far=INT_MIN; + + for(int i=0;imax_so_far){ + max_so_far = curr_max; + } + } + + // output result. + cout< b { + return a + } + return b +} + q \ No newline at end of file diff --git a/Famous Algorithms/kadanes_algorithm.java b/Famous Algorithms/kadanes_algorithm.java new file mode 100644 index 00000000..7de2d844 --- /dev/null +++ b/Famous Algorithms/kadanes_algorithm.java @@ -0,0 +1,80 @@ +/* Name : Aneesh +Github username : 007aneesh +Repository name : data-structures-and-algorithms +Problem : Kadane's algorithm in Java +Issue Number : #1180 +Problem statement : Given an integer array nums, find the subarray with the largest sum, and return its sum. + +Sample testcases: + +Testcase 1 --> + +Input: number of elements in array = 9 +nums = [-2,1,-3,4,-1,2,1,-5,4] +Output: 6 + +Testcase 2 --> +Input: number of elements in array +nums = [5,4,-1,7,8] +Output: 23 + +Time Complexity = O(n) +Space Complexity = O(1) + + +Explanation: +This code asks the user to enter the number of elements in an array, +and then prompts them to enter each element of the array one at a time. +Once the array is complete, the code applies the Kadane's algorithm to +find the maximum sum of any subarray within the array, and then prints +the result to the console. + +Kadane's algorithm is a way of finding the maximum sum of a contiguous subarray within an array, +and it does so by keeping track of the maximum sum seen so far as it iterates through the array. +At each step, it adds the current element to a running sum, and if that sum becomes negative, +it resets the running sum to zero. If the running sum is ever greater than the maximum seen so far, +it updates the maximum. Finally, it returns the maximum sum. +*/ + +// ----------------------------------------------------------------------------- code begins now! +import java.util.Scanner; + +public class kadanes_algo { + public static int maxSubArraySum(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + + int max = 0; + int sum = Integer.MIN_VALUE; + + for (int i = 0; i < arr.length; i++) { + max += arr[i]; + + if (max < arr[i]) { + max = arr[i]; + } + + if (sum < max) { + sum = max; + } + } + + return sum; + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + System.out.print("Enter the number of elements in the array: "); + int n = sc.nextInt(); + int[] arr = new int[n]; + System.out.println("Enter the elements of the array:"); + for (int i = 0; i < n; i++) { + arr[i] = sc.nextInt(); + } + + int maxSum = maxSubArraySum(arr); + System.out.println("The maximum subarray sum is " + maxSum); + } + +} diff --git a/Famous Algorithms/kadanes_algorithm.py b/Famous Algorithms/kadanes_algorithm.py new file mode 100644 index 00000000..6634247f --- /dev/null +++ b/Famous Algorithms/kadanes_algorithm.py @@ -0,0 +1,87 @@ +""" +What is Kadane's Algorithm? +Kadane's Algorithm is a way to find the maximum subarray sum in an array with a runtime of O(n). +It is a dynamic programming algorithm that uses the fact that the maximum subarray sum ending at index i is either the value at index i or the maximum subarray sum ending at index i-1 plus the value at index i. + +Note: The subarray must be contiguous, all the values in the subarray must be next to each other in the original array. + +How does it work? +The algorithm works by iterating through the array and keeping track of the maximum subarray sum seen so far and the maximum subarray sum ending at the current index. +The maximum subarray sum ending at the current index is either the value at the current index or the maximum subarray sum ending at the previous index plus the value at the current index. + +Lets take the example: {-2, -3, 4, -1, -2, 1, 5, -3} + max_so_far = INT_MIN + max_ending_here = 0 + + for i=0, a[0] = -2 + max_ending_here = max_ending_here + (-2) + Set max_ending_here = 0 because max_ending_here < 0 + and set max_so_far = -2 + + for i=1, a[1] = -3 + max_ending_here = max_ending_here + (-3) + Since max_ending_here = -3 and max_so_far = -2, max_so_far will remain -2 + Set max_ending_here = 0 because max_ending_here < 0 + + for i=2, a[2] = 4 + max_ending_here = max_ending_here + (4) + max_ending_here = 4 + max_so_far is updated to 4 because max_ending_here greater + than max_so_far which was -2 till now + + for i=3, a[3] = -1 + max_ending_here = max_ending_here + (-1) + max_ending_here = 3 + + for i=4, a[4] = -2 + max_ending_here = max_ending_here + (-2) + max_ending_here = 1 + + for i=5, a[5] = 1 + max_ending_here = max_ending_here + (1) + max_ending_here = 2 + + for i=6, a[6] = 5 + max_ending_here = max_ending_here + (5) + max_ending_here = 7 + max_so_far is updated to 7 because max_ending_here is + greater than max_so_far + + for i=7, a[7] = -3 + max_ending_here = max_ending_here + (-3) + max_ending_here = 4 + + Time Complexity: O(n) + Space Complexity: O(1) +""" + +from sys import maxint + +# maxint is a constant that holds the maximum possible value for an integer in Python. + + +def maxSubArraySum(a, size): + # we take the max_so_far to be the smallest possible integer value + max_so_far = -maxint - 1 + + # initialize max_ending_here to 0 + max_ending_here = 0 + + for i in range(0, size): + max_ending_here = max_ending_here + a[i] + if max_so_far < max_ending_here: + max_so_far = max_ending_here + + # if max_ending_here is negative, we set it to 0 + if max_ending_here < 0: + max_ending_here = 0 + + return max_so_far + + +# Driver function to check the above function + + +a = [-2, -3, 4, -1, -2, 1, 5, -3] + +print("Maximum contiguous sum is", maxSubArraySum(a, len(a))) diff --git a/Famous Algorithms/kadenes_algorithm.js b/Famous Algorithms/kadenes_algorithm.js new file mode 100644 index 00000000..0a22dc31 --- /dev/null +++ b/Famous Algorithms/kadenes_algorithm.js @@ -0,0 +1,98 @@ +/* What is Kadane's Algorithm? +Kadane's Algorithm is a way to find the maximum subarray sum in an array with a runtime of O(n). +It is a dynamic programming algorithm that uses the fact that the maximum subarray sum ending at index i is either the value at index i or the maximum subarray sum ending at index i-1 plus the value at index i. + +Note: The subarray must be contiguous, all the values in the subarray must be next to each other in the original array. + +How does it work? +In this algorithim we maintain two variables, one will hold the maximum sum of contagious subarray and the other variable will hold sum of next element + current sum from previous iteration. +If at any point the current sum + next element sum is less then 0 then we will reset the current sum to 0 and current sum + next element sum will start again from the next element. +If the current sum + next element sum is greater then 0 and it is also greater then the maximum sum of contagious subarray then the variable of maximum sum of contagious subarray will be updated with current sum + next element sum + + +Lets take the example: {-2, -3, 4, -1, -2, 1, 5, -3} + In this example maxLargestSumTillNow holds the maximum sum of contagious subarray and newLargestSum hold the value of current sum + next element + On initalizing max so far will be the max -ve number + maxLargestSumTillNow = INT_MIN + newLargestSum = 0 + + for i=0, a[0] = -2 + newLargestSum = newLargestSum + (-2) + Set newLargestSum = 0 because newLargestSum < 0 + and set maxLargestSumTillNow = -2 + + for i=1, a[1] = -3 + newLargestSum = newLargestSum + (-3) + Since newLargestSum = -3 and maxLargestSumTillNow = -2, maxLargestSumTillNow will remain -2 + Set newLargestSum = 0 because newLargestSum < 0 + + for i=2, a[2] = 4 + newLargestSum = newLargestSum + (4) + newLargestSum = 4 + maxLargestSumTillNow is updated to 4 because newLargestSum greater + than maxLargestSumTillNow which was -2 till now + + for i=3, a[3] = -1 + newLargestSum = newLargestSum + (-1) + newLargestSum = 3 + + for i=4, a[4] = -2 + newLargestSum = newLargestSum + (-2) + newLargestSum = 1 + + for i=5, a[5] = 1 + newLargestSum = newLargestSum + (1) + newLargestSum = 2 + + for i=6, a[6] = 5 + newLargestSum = newLargestSum + (5) + newLargestSum = 7 + maxLargestSumTillNow is updated to 7 because newLargestSum is + greater than maxLargestSumTillNow + + for i=7, a[7] = -3 + newLargestSum = newLargestSum + (-3) + newLargestSum = 4 + + Time Complexity: O(n) + Space Complexity: O(1) +*/ + +function largestSumOfSubArray(arr) { + if (arr.lenth == 1) { + return arr[0]; + } + + // Variable for maintaining Maximum sum of the subarray + var maxint = Math.pow(2, 53); + var maxLargestSumTillNow = -maxint - 1; + + // Variable to calclate the sum of subarray after each iteration + var newLargestSum = 0; + + // Looping through the entire array + for (i = 0; i < arr.length - 1; i++) { + // Calculating the largest sum on each iteration + newLargestSum += arr[i]; + + // If the largest sum value is greater then the maximum largest subarray value we have maintained then we will assign new value to maintained maximum largest subarray + if (maxLargestSumTillNow < newLargestSum) { + maxLargestSumTillNow = newLargestSum; + } + + // If the largest sum is negative then we will reset the value of largest sum to 0 and start the calculation again of largest sum from next element + if (newLargestSum < 0) { + newLargestSum = 0; + } + } + + // After the completion of iteration we will return the max largest sub array value + return maxLargestSumTillNow; +} + +// Driver code +var arr = [-2, -3, 4, -1, -2, 1, 5, -3]; +console.log(largestSumOfSubArray(arr)); + +// Input: arr = [-2, -3, 4, -1, -2, 1, 5, -3]; +//Output: 7 diff --git a/Famous Algorithms/kmp.java b/Famous Algorithms/kmp.java new file mode 100644 index 00000000..ee97dcb1 --- /dev/null +++ b/Famous Algorithms/kmp.java @@ -0,0 +1,100 @@ +/** + * Summary: + * This program implements the Knuth-Morris-Pratt (KMP) algorithm for string pattern matching. + * Given a text string and a pattern string, the algorithm searches for the pattern in the text and returns + * the index where the pattern is found in the text. If the pattern is not found, it returns -1. + * + * Inputs: + * The program expects two strings as inputs: text and pattern. + * + * Outputs: + * The output of the program is the index where the pattern is found in the text, or -1 if the pattern is not found. + * + * Example Usage: + * // Input + * String text = "ABABDABACDABABCABAB"; + * String pattern = "ABABCABAB"; + * + * // Execute the KMP algorithm for string pattern matching + * int index = searchPattern(text, pattern); + * + * // Output + * if (index != -1) { + * System.out.println("Pattern found at index: " + index); + * } else { + * System.out.println("Pattern not found in the text."); + * } + * + * Complexity Analysis: + * The time complexity of the KMP algorithm is O(n + m), where n is the length of the text and m is the length of the pattern. + * This is because the algorithm avoids unnecessary comparisons by utilizing the computed Longest Proper Prefix-Suffix (LPS) array. + * The space complexity is O(m) as it requires storing the LPS array of the pattern. + */ + +public class Graph_KMPAlgorithm { + + public static int[] computeLPSArray(String pattern) { + int[] lps = new int[pattern.length()]; + int len = 0; // Length of the previous longest prefix suffix + int i = 1; + + while (i < pattern.length()) { + if (pattern.charAt(i) == pattern.charAt(len)) { + len++; + lps[i] = len; + i++; + } else { + if (len != 0) { + len = lps[len - 1]; + } else { + lps[i] = 0; + i++; + } + } + } + + return lps; + } + + public static int searchPattern(String text, String pattern) { + int n = text.length(); + int m = pattern.length(); + + int[] lps = computeLPSArray(pattern); + + int i = 0; // index for text + int j = 0; // index for pattern + + while (i < n) { + if (text.charAt(i) == pattern.charAt(j)) { + i++; + j++; + } + + if (j == m) { + return i - j; + } else if (i < n && text.charAt(i) != pattern.charAt(j)) { + if (j != 0) { + j = lps[j - 1]; + } else { + i++; + } + } + } + + return -1; // pattern not found in text + } + + public static void main(String[] args) { + String text = "ABABDABACDABABCABAB"; + String pattern = "ABABCABAB"; + + int index = searchPattern(text, pattern); + + if (index != -1) { + System.out.println("Pattern found at index: " + index); + } else { + System.out.println("Pattern not found in the text."); + } + } +} diff --git a/Famous Algorithms/kmp.js b/Famous Algorithms/kmp.js new file mode 100644 index 00000000..dcfe50c6 --- /dev/null +++ b/Famous Algorithms/kmp.js @@ -0,0 +1,117 @@ +/**The Knuth-Morris-Pratt (KMP) algorithm is a string matching algorithm that efficiently finds occurrences of a pattern within a text. It was developed by Donald Knuth and Vaughan Pratt in 1977. + +The key idea behind the KMP algorithm is to take advantage of the information present in the pattern itself to avoid unnecessary character comparisons during the search process. It achieves this by utilizing a preprocessed array called the Longest Proper Prefix which is also Suffix (LPS) array or failure function. + +Here's a step-by-step explanation of the KMP algorithm: + +1. Preprocessing (Compute LPS Array): + - Given a pattern of length m, the first step is to compute the LPS array, which holds information about the longest proper prefix that is also a suffix for each position in the pattern. + - The LPS array is initialized with the first element as 0 and then iteratively calculated for each position of the pattern using the following rules: + - If the characters at the current position and the previous longest proper prefix suffix match, increment the length of the prefix and store it in the LPS array. + - If the characters don't match: + - If the length of the prefix is not zero, move to the previous longest proper prefix suffix and continue the comparison. + - If the length of the prefix is zero, store 0 in the LPS array for the current position. + - This preprocessing step is done in O(m) time complexity. + +2. Search (Pattern Matching): + - With the LPS array computed, the search process begins by comparing characters of the text and pattern. + - Initialize two pointers, i for the text and j for the pattern, both starting from 0. + - Iterate over the text from left to right until i reaches the end of the text: + - If the characters at the current positions i and j match, increment both i and j. + - If j reaches the end of the pattern, a match is found: + - Store the index (i - j) as an occurrence of the pattern in the text. + - Move j to the previous longest proper prefix suffix using the LPS array. + - If the characters at the current positions i and j don't match: + - If j is not at the beginning of the pattern, move j to the previous longest proper prefix suffix using the LPS array. + - If j is at the beginning of the pattern, increment i and continue the search. + - The search process iterates over the text once and performs comparisons using the LPS array, resulting in a time complexity of O(n), where n is the length of the text. + +The KMP algorithm provides an efficient way to search for patterns within a text by avoiding redundant comparisons. It achieves this by utilizing the LPS array, which stores information about the pattern's structure. This makes the KMP algorithm more efficient than naive approaches such as the brute-force method, especially when the pattern has repeated characters or subsequences. */ + +/** + * Computes the Longest Proper Prefix which is also Suffix (LPS) array for the given pattern. + * The LPS array is used to determine the longest prefix of the pattern that is also a suffix. + * + * @param {string} pattern - The pattern string. + * @returns {number[]} - The LPS array. + */ + function computeLPSArray(pattern) { + const lps = [0]; // Initialize LPS array with the first element as 0. + let len = 0; // Length of the previous longest prefix suffix. + let i = 1; // Current index in the pattern string. + + while (i < pattern.length) { + if (pattern[i] === pattern[len]) { + len++; + lps[i] = len; + i++; + } else { + if (len !== 0) { + // Move len to the previous longest prefix suffix value. + len = lps[len - 1]; + } else { + lps[i] = 0; + i++; + } + } + } + + return lps; + } + + /** + * Performs the Knuth-Morris-Pratt (KMP) algorithm to find all occurrences of a pattern within a text. + * + * @param {string} text - The text string. + * @param {string} pattern - The pattern string. + * @returns {number[]} - An array of indices where the pattern is found within the text. + */ + function KMP(text, pattern) { + const m = pattern.length; // Length of the pattern. + const n = text.length; // Length of the text. + const lps = computeLPSArray(pattern); // Compute the LPS array for the pattern. + const indices = []; // Array to store the indices where the pattern is found. + + let i = 0; // Current index in the text string. + let j = 0; // Current index in the pattern string. + + while (i < n) { + if (pattern[j] === text[i]) { + i++; + j++; + } + + if (j === m) { + // Pattern found at index i - j. + indices.push(i - j); + j = lps[j - 1]; // Move j to the previous longest prefix suffix value. + } else if (i < n && pattern[j] !== text[i]) { + if (j !== 0) { + j = lps[j - 1]; // Move j to the previous longest prefix suffix value. + } else { + i++; + } + } + } + + return indices; + } + + // Example usage: + const text = "ABABDABACDABABCABAB"; + const pattern = "ABABCABAB"; + + const indices = KMP(text, pattern); + console.log("Pattern found at indices:", indices); + + // Pattern found at indices: [10] + + /** + +Time Complexity: + +The KMP algorithm has a time complexity of O(m + n), where m is the length of the pattern and n is the length of the text. It computes the LPS array in O(m) time and performs a single pass over the text in O(n) time. + +Space Complexity: + +The space complexity of the KMP algorithm is O(m), where m is the length of the pattern. It is due to the LPS array, which requires O(m) space to store the longest prefix suffix values for each index in the pattern. The additional space used by the algorithm is minimal and does not depend on the input size */ \ No newline at end of file diff --git a/Famous Algorithms/kmp.py b/Famous Algorithms/kmp.py new file mode 100644 index 00000000..ab92c741 --- /dev/null +++ b/Famous Algorithms/kmp.py @@ -0,0 +1,44 @@ +# Implementation of KMP Algorithm. Program Author : SNEHA CHAUHAN +''' problem: Given a text txt and a pattern pat, write a function search(char pat[], char txt[]) + that prints all occurrences of pat[] in txt[]. + + KMP algorithm is used to find the pattern in the given string. This is naive approach to find + the pattern in the given string. + + Time Complexity: O(n*m) + Space Complexity: O(1) + + Example: + input: txt = “AAAABAAABA” pat = “AAAA” + output: Pattern found at index 0 + + Algorithm: + 1. Start from the leftmost character of txt and one by one compare it with each character of pat. + 2. If a character matches, then move both txt and pat ahead and compare the next character. + 3. If a mismatch occurs, then move pat to the index where the mismatch occurs and compare again. + 4. If pat reaches its end without any mismatch, then pattern found. + + ''' + +def search(pat, txt): + M = len(pat) + N = len(txt) + + # A loop to slide pat[] one by one + for i in range(N - M + 1): + j = 0 + + # For current index i, check for pattern match + for j in range(0, M): + if (txt[i + j] != pat[j]): + break + + if (j == M - 1): + print("Pattern found at index ", i) + + +# Driver Code +if __name__ == '__main__': + txt = "AABAACAADAABAAABAA" + pat = "AABA" + search(pat, txt) \ No newline at end of file diff --git a/Fast and Slow Pointers/happy_number.go b/Fast and Slow Pointers/happy_number.go new file mode 100644 index 00000000..2186796e --- /dev/null +++ b/Fast and Slow Pointers/happy_number.go @@ -0,0 +1,58 @@ +/* +Write an algorithm to determine if a number num is happy. + +A happy number is a number defined by the following process: + +Starting with any positive integer, replace the number by the sum of the squares of its digits. +Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1 + +Those numbers for which this process ends in 1 are happy. +Return TRUE if num is a happy number, and FALSE if not. + +Sample Input : 4 +Output: False + +Sample Input : 19 +Output: True + +*/ + +package main + +import "fmt" + +// pow calculates the power of the given digit +func pow(digit int, power int) int { + res := 1 + for i := 0; i < power; i++ { + res = res * digit + } + return res +} + +// sumDigits is a helper function that calculates the sum of digits. +func sumDigits(number int) int { + totalSum := 0 + for number > 0 { + digit := number % 10 + number = number / 10 + totalSum += pow(digit, 2) + } + return totalSum +} + +func happyNumber(num int) bool { + slow := num + fast := sumDigits(num) + for fast != 1 && fast != slow { + slow = sumDigits(slow) + fast = sumDigits(sumDigits(fast)) + } + return fast == 1 +} + +func main() { + fmt.Println(happyNumber(4)) // false + fmt.Println(happyNumber(19)) // true + fmt.Println(happyNumber(100)) // true +} \ No newline at end of file diff --git a/Linked_List/linked_list_compute_midpoint.cpp b/Fast and Slow Pointers/linked_list_compute_midpoint.cpp similarity index 96% rename from Linked_List/linked_list_compute_midpoint.cpp rename to Fast and Slow Pointers/linked_list_compute_midpoint.cpp index 1348dd7c..9d908695 100644 --- a/Linked_List/linked_list_compute_midpoint.cpp +++ b/Fast and Slow Pointers/linked_list_compute_midpoint.cpp @@ -1,62 +1,62 @@ -// Finding Midpoint of a LinkedList -// Program Author : Abhisek Kumar Gupta -// Naive Approach: Find the length of the linked list and return the (length/2)th node. -// One pass Approach: Use two pointers, the 2nd pointer should traverse twice as fast at the first. -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -node* compute_midpoint(node *head){ - if(head->next == NULL || head == NULL){ - return head; - } - node *slow = head; - node *fast = head->next; - while(fast != NULL && fast->next != NULL){ - fast = fast->next->next; - slow = slow->next; - } - return slow; -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - node *midpoint = compute_midpoint(head); - cout << endl; - cout << midpoint->data << endl; - return 0; +// Finding Midpoint of a LinkedList +// Program Author : Abhisek Kumar Gupta +// Naive Approach: Find the length of the linked list and return the (length/2)th node. +// One pass Approach: Use two pointers, the 2nd pointer should traverse twice as fast at the first. +#include +using namespace std; +class node{ + public: + int data; + node* next; + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +node* compute_midpoint(node *head){ + if(head->next == NULL || head == NULL){ + return head; + } + node *slow = head; + node *fast = head->next; + while(fast != NULL && fast->next != NULL){ + fast = fast->next->next; + slow = slow->next; + } + return slow; +} +int main(){ + node *head = NULL; + makeLinkedList(head); + print_linked_list(head); + node *midpoint = compute_midpoint(head); + cout << endl; + cout << midpoint->data << endl; + return 0; } \ No newline at end of file diff --git a/Fast and Slow Pointers/linked_list_compute_midpoint.java b/Fast and Slow Pointers/linked_list_compute_midpoint.java new file mode 100644 index 00000000..1cc2e363 --- /dev/null +++ b/Fast and Slow Pointers/linked_list_compute_midpoint.java @@ -0,0 +1,113 @@ +/* + * LEETCODE 876 +Given the head of a singly linked list, return the middle node of the linked list. +If there are two middle nodes, return the second middle node. +*Example 1: +Input: head = [1,2,3,4,5] +Output: [3,4,5] +Explanation: The middle node of the list is node 3. + +*Example 2: +Input: head = [1,2,3,4,5,6] +Output: [4,5,6] +Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one. + +*CODE EXPLAINATION WITH DRY RUN: +This Java code finds the midpoint node of a singly-linked list. If the linked list has an even number +of nodes, it returns the second middle node. + +First, the Node class is defined with a constructor that takes in an integer value and initializes +the next reference to null. The insertAtTail() method takes in a head node and an integer value, +and inserts a new node with the given value at the end of the linked list. The printLinkedList() method +takes in a head node and prints out all the values in the linked list. + +The makeLinkedList() method prompts the user to enter integers until -1 is inputted. Each integer is +inserted into a new node at the tail of the linked list. The computeMidpoint() method takes in a head +node and returns the middle node(s) of the linked list. If the linked list has no nodes or only one node, +it just returns the head node. Otherwise, it initializes a slow pointer and a fast pointer to the head +node. The while loop advances the fast pointer by two nodes and the slow pointer by one node at each +iteration until the fast pointer reaches the end of the linked list. At that point, the slow pointer +will be pointing to the midpoint node(s) of the linked list. + +Finally, in the main() method, a new linked list is created by calling makeLinkedList(). The linked +list is printed using printLinkedList(). The midpoint of the linked list is computed using +computeMidpoint(), and its value is printed out. + +*Example Dry Run: +Suppose we have the following input: +1 2 3 4 5 -1 +This creates a linked list with the following structure: +1 -> 2 -> 3 -> 4 -> 5 -> null +Initially, the slow pointer and fast pointer both point to the head node, which is 1. In the first iteration of the while loop, the fast pointer moves two nodes ahead to node 3, while the slow pointer moves one node ahead to node 2. In the second iteration, the fast pointer moves another two nodes ahead to null, while the slow pointer moves one more node ahead to node 3. At this point, the slow pointer is pointing to the midpoint node(s) of the linked list. +Therefore, computeMidpoint() returns node 3, which is printed out as output. + +*/ +import java.util.Scanner; + +public class linked_list_compute_midpoint { + + static class Node { + + int data; + Node next; + + public Node(int data) { + this.data = data; + next = null; + } + } + + static Node insertAtTail(Node head, int data) { + if (head == null) { + head = new Node(data); + return head; + } + Node n = new Node(data); + Node temp = head; + while (temp.next != null) { + temp = temp.next; + } + temp.next = n; + return head; + } + + static void printLinkedList(Node head) { + while (head != null) { + System.out.print(head.data + "->"); + head = head.next; + } + } + + static Node makeLinkedList() { + Scanner scanner = new Scanner(System.in); + int data = scanner.nextInt(); + Node head = null; + while (data != -1) { + head = insertAtTail(head, data); + data = scanner.nextInt(); + } + scanner.close(); + return head; + } + + static Node computeMidpoint(Node head) { + if (head == null || head.next == null) { + return head; + } + Node slow = head; + Node fast = head.next; + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + } + return slow; + } + + public static void main(String[] args) { + Node head = makeLinkedList(); + printLinkedList(head); + System.out.println(); + Node midpoint = computeMidpoint(head); + System.out.println(midpoint.data); + } +} diff --git a/Fast and Slow Pointers/linked_list_find_middle.py b/Fast and Slow Pointers/linked_list_find_middle.py new file mode 100644 index 00000000..41c6b9d6 --- /dev/null +++ b/Fast and Slow Pointers/linked_list_find_middle.py @@ -0,0 +1,45 @@ +# Finding Midpoint of a LinkedList +# Node class +class Node: + + # Function to initialise the node object + def __init__(self, data): + self.data = data + self.next = None + +class LinkedList: + + def __init__(self): + self.head = None + + def push(self, new_data): + new_node = Node(new_data) + new_node.next = self.head + self.head = new_node + + + + # Function to get the middle of + # the linked list using pointers + def printMiddle(self): + slow_ptr = self.head + fast_ptr = self.head + + if self.head is not None: + while (fast_ptr is not None and fast_ptr.next is not None): + fast_ptr = fast_ptr.next.next + slow_ptr = slow_ptr.next + print("The middle element is: ", slow_ptr.data) + + + +# Driver code +list1 = LinkedList() +list1.push(25) +list1.push(33) +list1.push(14) +list1.push(22) +list1.push(9) +list1.push(11) +list1.push(20) +list1.printMiddle() diff --git a/Linked_List/linked_floyds_cycle_detection.cpp b/Fast and Slow Pointers/linked_list_floyds_cycle_detection.cpp similarity index 96% rename from Linked_List/linked_floyds_cycle_detection.cpp rename to Fast and Slow Pointers/linked_list_floyds_cycle_detection.cpp index bd62ffc2..4f28ef38 100644 --- a/Linked_List/linked_floyds_cycle_detection.cpp +++ b/Fast and Slow Pointers/linked_list_floyds_cycle_detection.cpp @@ -1,80 +1,80 @@ -// Floyds Cycle detection and removal -// Program Author : Abhisek Kumar Gupta -// The cycle detection problem is to find the cycle in a sequence, -// and Floyd’s cycle detection algorithm, aka Tortoise and Hare algorithm, -// is a two-pointer algorithm to detect the cycle and locate the start of the cycle as well. -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } - head->next->next->next = head; -} -bool detect_cycle(node *head){ - node *slow = head; - node *fast = head; - while(fast != NULL && fast->next != NULL){ - fast = fast->next->next; - slow = slow->next; - if(fast == slow){ - //return true; - slow = head; - if(slow == fast){ - while(fast->next != slow) fast = fast->next; - } - else{ - while(slow->next != fast->next){ - slow = slow->next; - fast = fast->next; - } - } - fast->next = NULL; - cout << "Cycle Detected and Removed \n"; - } - } - return false; -} -int main(){ - node *head = NULL; - makeLinkedList(head); - //print_linked_list(head); - if(detect_cycle(head)){ - cout << "Cycle detected \n"; - } - else { - cout << "No cycle \n"; - } - print_linked_list(head); - return 0; +// Floyds Cycle detection and removal +// Program Author : Abhisek Kumar Gupta +// The cycle detection problem is to find the cycle in a sequence, +// and Floyd’s cycle detection algorithm, aka Tortoise and Hare algorithm, +// is a two-pointer algorithm to detect the cycle and locate the start of the cycle as well. +#include +using namespace std; +class node{ + public: + int data; + node* next; + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } + head->next->next->next = head; +} +bool detect_cycle(node *head){ + node *slow = head; + node *fast = head; + while(fast != NULL && fast->next != NULL){ + fast = fast->next->next; + slow = slow->next; + if(fast == slow){ + //return true; + slow = head; + if(slow == fast){ + while(fast->next != slow) fast = fast->next; + } + else{ + while(slow->next != fast->next){ + slow = slow->next; + fast = fast->next; + } + } + fast->next = NULL; + cout << "Cycle Detected and Removed \n"; + } + } + return false; +} +int main(){ + node *head = NULL; + makeLinkedList(head); + //print_linked_list(head); + if(detect_cycle(head)){ + cout << "Cycle detected \n"; + } + else { + cout << "No cycle \n"; + } + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Fast and Slow Pointers/linked_list_floyds_cycle_detection.py b/Fast and Slow Pointers/linked_list_floyds_cycle_detection.py new file mode 100644 index 00000000..923acd77 --- /dev/null +++ b/Fast and Slow Pointers/linked_list_floyds_cycle_detection.py @@ -0,0 +1,24 @@ +''' + Floyds Cycle detection and removal + Program Author : Abhisek Kumar Gupta + The cycle detection problem is to find the cycle in a sequence, + and Floyd’s cycle detection algorithm, aka Tortoise and Hare algorithm, + is a two-pointer algorithm to detect the cycle and locate the start of the cycle as well. +''' +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def hasCycle(self, head: Optional[ListNode]) -> bool: + #Floyd's cycle detection algorithm uses slow and fast points to find the loop + #Reference - https://www.geeksforgeeks.org/floyds-cycle-finding-algorithm/ + slow,fast= head,head + while(slow!=None and fast!=None and fast.next!=None): + slow = slow.next + fast = fast.next.next + if(slow == fast): + return True + return False \ No newline at end of file diff --git a/Graphs/Diljstra.go b/Graphs/Diljstra.go new file mode 100644 index 00000000..8802aa27 --- /dev/null +++ b/Graphs/Diljstra.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "math" +) + +// Define a struct to represent a graph. +type Graph struct { + nodes map[string]map[string]float64 +} + +// Add an edge to the graph. +func (g *Graph) AddEdge(node1, node2 string, weight float64) { + if g.nodes == nil { + g.nodes = make(map[string]map[string]float64) + } + if g.nodes[node1] == nil { + g.nodes[node1] = make(map[string]float64) + } + if g.nodes[node2] == nil { + g.nodes[node2] = make(map[string]float64) + } + g.nodes[node1][node2] = weight + g.nodes[node2][node1] = weight // Assuming an undirected graph +} + +// Dijkstra's algorithm to find the shortest path. +func Dijkstra(graph Graph, startNode string) map[string]float64 { + distances := make(map[string]float64) + visited := make(map[string]bool) + + for node := range graph.nodes { + distances[node] = math.Inf(1) + } + + distances[startNode] = 0 + + for { + var closestNode string + var shortestDistance float64 = math.Inf(1) + + for node, distance := range distances { + if !visited[node] && distance < shortestDistance { + closestNode = node + shortestDistance = distance + } + } + + if closestNode == "" { + break + } + + visited[closestNode] = true + + for neighbor, weight := range graph.nodes[closestNode] { + if newDistance := distances[closestNode] + weight; newDistance < distances[neighbor] { + distances[neighbor] = newDistance + } + } + } + + return distances +} + +func main() { + // Create a graph. + g := Graph{} + g.AddEdge("A", "B", 1) + g.AddEdge("A", "C", 4) + g.AddEdge("B", "C", 2) + g.AddEdge("B", "D", 5) + g.AddEdge("C", "D", 1) + g.AddEdge("D", "E", 3) + g.AddEdge("E", "F", 2) + + // Find the shortest distances from node "A" to all other nodes. + shortestDistances := Dijkstra(g, "A") + + // Print the shortest distances. + for node, distance := range shortestDistances { + fmt.Printf("Shortest distance from A to %s: %v\n", node, distance) + } +} diff --git a/Graphs/GraphBFS.java b/Graphs/GraphBFS.java new file mode 100644 index 00000000..e9f921b7 --- /dev/null +++ b/Graphs/GraphBFS.java @@ -0,0 +1,63 @@ +/** + * The GraphBFS class represents a simple undirected graph using an adjacency list + * and provides a breadth-first search (BFS) algorithm to traverse the graph. + */ +import java.util.*; +import java.io.*; + +class GraphBFS { + private int V; // Number of vertices in the graph + private List adjacency[]; // Adjacency list to represent the graph + + // Constructor to initialize the graph with the given number of vertices + GraphBFS(int v) { + V = v; + adjacency = new ArrayList[V]; + for(int i = 0; i < V; i++) { + adjacency[i] = new ArrayList(); + } + } + + // Method to add an edge between vertices 'u' and 'v' in the graph + void addEdge(int u, int v) { + adjacency[u].add(v); + adjacency[v].add(u); + } + + /** + * Performs breadth-first search (BFS) starting from the given source vertex 's'. + * Prints the vertices in BFS order. + * @param s The source vertex from which BFS starts. + */ + void bfs(int s) { + boolean[] visited = new boolean[V]; // Array to track visited vertices + visited[s] = true; // Mark the source vertex as visited + LinkedList Q = new LinkedList(); // Queue for BFS traversal + Q.add(s); // Enqueue the source vertex + + // BFS traversal + while(Q.size() != 0) { + int current = Q.poll(); // Dequeue the current vertex + System.out.print(current + " -> "); // Print the current vertex + + // Visit all neighbors of the current vertex + for(int neighbour: adjacency[current]) { + if(!visited[neighbour]) { + visited[neighbour] = true; // Mark the neighbour as visited + Q.add(neighbour); // Enqueue the neighbour for further exploration + } + } + } + } + + // Main method for testing the GraphBFS class + public static void main(String[] args) { + GraphBFS g = new GraphBFS(5); // Create a graph with 5 vertices + g.addEdge(2, 3); // Add edges to the graph + g.addEdge(2, 4); + g.addEdge(3, 1); + g.addEdge(4, 1); + + g.bfs(2); // Perform BFS starting from vertex 2 + } +} diff --git a/Graphs/Graph_Dijstra.java b/Graphs/Graph_Dijstra.java new file mode 100644 index 00000000..f40ac596 --- /dev/null +++ b/Graphs/Graph_Dijstra.java @@ -0,0 +1,79 @@ +import java.util.*; + +class Graph { + private int V; // Number of vertices + private List> adj; // Adjacency list + + public Graph(int V) { + this.V = V; + adj = new ArrayList<>(V); + for (int i = 0; i < V; i++) { + adj.add(new ArrayList<>()); + } + } + + // Add an edge to the graph + public void addEdge(int source, int destination, int weight) { + Node node = new Node(destination, weight); + adj.get(source).add(node); + } + + public void dijkstra(int source) { + int[] distance = new int[V]; + Arrays.fill(distance, Integer.MAX_VALUE); + distance[source] = 0; + + PriorityQueue pq = new PriorityQueue<>(V, Comparator.comparingInt(node -> node.weight)); + pq.add(new Node(source, 0)); + + while (!pq.isEmpty()) { + int u = pq.poll().vertex; + + for (Node neighbor : adj.get(u)) { + int v = neighbor.vertex; + int w = neighbor.weight; + + if (distance[u] != Integer.MAX_VALUE && distance[u] + w < distance[v]) { + distance[v] = distance[u] + w; + pq.add(new Node(v, distance[v])); + } + } + } + + // Print the shortest distances from the source + System.out.println("Shortest distances from source vertex " + source + ":"); + for (int i = 0; i < V; i++) { + System.out.println("Vertex " + i + ": " + distance[i]); + } + } + + private static class Node { + int vertex; + int weight; + + Node(int vertex, int weight) { + this.vertex = vertex; + this.weight = weight; + } + } +} + +public class DijkstraAlgorithm { + public static void main(String[] args) { + int V = 6; // Number of vertices + Graph graph = new Graph(V); + + // Add edges and their weights + graph.addEdge(0, 1, 2); + graph.addEdge(0, 2, 4); + graph.addEdge(1, 2, 1); + graph.addEdge(1, 3, 7); + graph.addEdge(2, 4, 3); + graph.addEdge(3, 4, 1); + graph.addEdge(3, 5, 5); + graph.addEdge(4, 5, 2); + + int source = 0; // Source vertex + graph.dijkstra(source); + } +} diff --git a/Graphs/Graphs_Dijkstras.py b/Graphs/Graphs_Dijkstras.py new file mode 100644 index 00000000..e435d96b --- /dev/null +++ b/Graphs/Graphs_Dijkstras.py @@ -0,0 +1,51 @@ +# Graphs Dijkstras Implementation +# Program Author : Tyler Le + +# Dijkstra's algorithm is a graph search algorithm +# that solves the single-source shortest path problem +# for a graph with non-negative edge weights + +def dijkstra(graph, start): + # Create a dictionary to store the shortest distances to each node + shortest_distances = {node: float('inf') for node in graph} + shortest_distances[start] = 0 + + # Create a set to store nodes that have been processed + processed_nodes = set() + + # Create a priority queue to store nodes and their distances + queue = [(start, 0)] + + while queue: + # Get the node with the smallest distance from the start node + current_node, current_distance = min(queue, key=lambda x: x[1]) + queue.remove((current_node, current_distance)) + + # If we've already processed this node, skip it + if current_node in processed_nodes: + continue + + # Mark this node as processed + processed_nodes.add(current_node) + + # Update the shortest distances to all neighboring nodes + for neighbor, weight in graph[current_node].items(): + distance = current_distance + weight + if distance < shortest_distances[neighbor]: + shortest_distances[neighbor] = distance + queue.append((neighbor, distance)) + + return shortest_distances + +# Example usage +graph = { + 'A': {'B': 5, 'C': 1}, + 'B': {'A': 5, 'C': 2, 'D': 1}, + 'C': {'A': 1, 'B': 2, 'D': 4}, + 'D': {'B': 1, 'C': 4} +} + +start_node = 'A' +distances = dijkstra(graph, start_node) + +print(distances) diff --git a/Graphs/Graphs_Ford_Fulkerson.cpp b/Graphs/Graphs_Ford_Fulkerson.cpp new file mode 100644 index 00000000..809f4cb2 --- /dev/null +++ b/Graphs/Graphs_Ford_Fulkerson.cpp @@ -0,0 +1,122 @@ +The Ford-Fulkerson algorithm is a graph algorithm used to find the maximum flow in a flow network. +Here is a high-level overview of the algorithm and some resources for further documentation: + +Ford-Fulkerson Algorithm Overview: + +Start with an initial flow of zero. +While there exists an augmenting path from the source to the sink: +Find the residual capacity of the augmenting path (minimum capacity edge along the path). +Update the flow by increasing the flow along the augmenting path. +Update the residual capacities of the edges. +The maximum flow is the sum of the flows along the augmenting paths. + +Here's an example of the Ford-Fulkerson algorithm implemented in C++: + +#include +#include +#include +#include +using namespace std; + +// Number of vertices in the graph +#define V 6 + +// A BFS based function to check whether there is an augmenting path +// from source to sink. Returns true if there is an augmenting path, +// else returns false +bool bfs(int rGraph[V][V], int s, int t, int parent[]) +{ + // Create a visited array and mark all vertices as not visited + bool visited[V]; + memset(visited, 0, sizeof(visited)); + + // Create a queue, enqueue source vertex and mark source vertex + // as visited + queue q; + q.push(s); + visited[s] = true; + parent[s] = -1; + + // Standard BFS Loop + while (!q.empty()) { + int u = q.front(); + q.pop(); + + for (int v = 0; v < V; v++) { + if (visited[v] == false && rGraph[u][v] > 0) { + q.push(v); + parent[v] = u; + visited[v] = true; + } + } + } + + // If we reached the sink in BFS starting from source, then return + // true, else false + return (visited[t] == true); +} + +// A function to implement the Ford-Fulkerson algorithm +// This will find the maximum possible flow from the source to the sink +int fordFulkerson(int graph[V][V], int s, int t) +{ + int u, v; + + // Create a residual graph and fill the residual graph with + // given capacities in the original graph as residual capacities + // in residual graph + int rGraph[V][V]; // Residual graph where rGraph[i][j] indicates + // residual capacity of edge from i to j (if there + // is an edge. If rGraph[i][j] is 0, then there is not) + for (u = 0; u < V; u++) + for (v = 0; v < V; v++) + rGraph[u][v] = graph[u][v]; + + int parent[V]; // This array is filled by BFS and to store path + + int maxFlow = 0; // There is no flow initially + + // Augument the flow while there is path from source to sink + while (bfs(rGraph, s, t, parent)) { + // Find minimum residual capacity of the edges along the + // path filled by BFS. Or we can say find the maximum flow + // through the path found. + int pathFlow = INT_MAX; + for (v = t; v != s; v = parent[v]) { + u = parent[v]; + pathFlow = min(pathFlow, rGraph[u][v]); + } + + // Update residual capacities of the edges and reverse edges + // along the path + for (v = t; v != s; v = parent[v]) { + u = parent[v]; + rGraph[u][v] -= pathFlow; + rGraph[v][u] += pathFlow; + } + + // Add path flow to overall flow + maxFlow += pathFlow; + } + + // Return the overall flow as the maximum flow + return maxFlow; +} +// Driver program to test above functions +int main() +{ + // Let us create a graph shown in the above example + int graph[V][V] = { { 0, 16, 13, 0, 0, 0 }, + { 0, 0, 10, 12, 0, 0 }, + { 0, 4, 0, 0, 14, 0 }, + { 0, 0, 9, 0, 0, 20 }, + { 0, 0, 0, 7, 0, 4 }, + { 0, 0, 0, 0, 0, 0 } }; + + int source = 0; + int sink = 5; + + cout << "The maximum possible flow is: " << fordFulkerson(graph, source, sink) << endl; + + return 0; +} diff --git a/Graphs/Graphs_bfs.js b/Graphs/Graphs_bfs.js new file mode 100644 index 00000000..f6cf01b3 --- /dev/null +++ b/Graphs/Graphs_bfs.js @@ -0,0 +1,67 @@ +// Define a function that takes a graph and a starting node as input +function bfs(graph, startNode) { + // Initialize an empty object to keep track of visited nodes + const visited = {} + // Initialize a queue with the starting node + const queue = [startNode] + + // Mark the starting node as visited + visited[startNode] = true + + // While there are nodes in the queue + while (queue.length > 0) { + // Get the next node from the front of the queue + const currentNode = queue.shift() + // Log the current node to the console (or do something else with it) + console.log(currentNode) + + // Get the adjacent nodes of the current node from the graph + const adjacentNodes = graph[currentNode] + + // For each adjacent node + for (let i = 0; i < adjacentNodes.length; i++) { + // Get the adjacent node + const adjacentNode = adjacentNodes[i].node + + // If the adjacent node has not been visited + if (!visited[adjacentNode]) { + // Mark the adjacent node as visited + visited[adjacentNode] = true + // Add the adjacent node to the back of the queue + queue.push(adjacentNode) + } + } + } + } + +// SAMPLE USE CASE +// The graph will be represented in form of an adjacency list. +/* + The graph will be in form of an object where each key represents the nodes and each value will be an array of the neighbors of the node. + + The array in values will be an object array, where each object has the node, which is the neighbor, and weight, which is the distance to that neighbor from the current node. +*/ +const graph = { + 0: [{ node: 1, weight: 10 }], + 1: [ + { node: 0, weight: 10 }, + { node: 2, weight: 10 }, + { node: 3, weight: 5 }, + ], + 2: [{ node: 1, weight: 10 }], + 3: [ + { node: 1, weight: 5 }, + { node: 4, weight: 10 }, + { node: 5, weight: 10 }, + ], + 4: [ + { node: 3, weight: 10 }, + { node: 5, weight: 10 }, + ], + 5: [ + { node: 3, weight: 10 }, + { node: 4, weight: 10 }, + ], +} + +bfs(graph, 2) \ No newline at end of file diff --git a/Graphs/Graphs_bfs.py b/Graphs/Graphs_bfs.py new file mode 100644 index 00000000..bc39ec56 --- /dev/null +++ b/Graphs/Graphs_bfs.py @@ -0,0 +1,35 @@ +from collections import defaultdict + +class Graph: + def __init__(self): + self.graph = defaultdict(list) + + def insertEdge(self,v1,v2): + self.graph[v1].append(v2) + + def subBfs(self,visited,queue): + while(queue): + v=queue.pop(0) + visited.add(v) + print(v,end=" ") + + for neighbour in self.graph[v]: + if neighbour not in visited: + queue.append(neighbour) + visited.add(neighbour) + + def bfs(self,v): + visited=set() + queue=[v] + self.subBfs(visited,queue) + +g=Graph() +g.insertEdge(1,2) +g.insertEdge(2,1) +g.insertEdge(2,3) +g.insertEdge(3,4) +g.insertEdge(4,5) +g.insertEdge(5,6) +g.insertEdge(2,6) + +g.bfs(1) diff --git a/Graphs/Graphs_dfs.java b/Graphs/Graphs_dfs.java new file mode 100644 index 00000000..d62b868a --- /dev/null +++ b/Graphs/Graphs_dfs.java @@ -0,0 +1,74 @@ +/*Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Implement Depth First Search in Java +Issue Number : #733 +Problem statement : + +Explanation of the below Java code : + +In this example, we have a Node class representing each node in the graph. Each Node has a value, a list of neighbors, and a boolean flag to track if it has been visited. + +The DepthFirstSearch class contains the dfs method, which performs the depth-first search traversal. It takes a starting node as an argument and recursively visits all unvisited neighbors of that node. When visiting a node, it marks it as visited and prints its value. + +In the main method, we create a small graph with five nodes and test the dfs method by starting the traversal from the first node (node1). The output will display the visited nodes in the order they are traversed: + + +*/ + +-------------------------------------------------------------------------//Java code begins here------------------------------------------------------------------------ + + +import java.util.ArrayList; +import java.util.List; + +class Node { + int value; + List neighbors; + boolean visited; + + Node(int value) { + this.value = value; + this.neighbors = new ArrayList<>(); + this.visited = false; + } + + void addNeighbor(Node neighbor) { + this.neighbors.add(neighbor); + } +} + +class DepthFirstSearch { + void dfs(Node startNode) { + // Mark the startNode as visited + startNode.visited = true; + System.out.println("Visited Node: " + startNode.value); + + // Recursively visit all unvisited neighbors + for (Node neighbor : startNode.neighbors) { + if (!neighbor.visited) { + dfs(neighbor); + } + } + } +} + +public class Main { + public static void main(String[] args) { + // Create a graph for testing + Node node1 = new Node(1); + Node node2 = new Node(2); + Node node3 = new Node(3); + Node node4 = new Node(4); + Node node5 = new Node(5); + + node1.addNeighbor(node2); + node1.addNeighbor(node3); + node2.addNeighbor(node4); + node3.addNeighbor(node4); + node4.addNeighbor(node5); + + DepthFirstSearch dfs = new DepthFirstSearch(); + dfs.dfs(node1); + } +} diff --git a/Graphs/Graphs_dfs.js b/Graphs/Graphs_dfs.js new file mode 100644 index 00000000..4b0dee62 --- /dev/null +++ b/Graphs/Graphs_dfs.js @@ -0,0 +1,65 @@ +// Takes in a graph object and a starting node +function dfs(graph, startNode) { + // Initialize an empty visited object + const visited = {} + + // Call the dfsHelper function with the starting node, the visited object, and the graph object + dfsHelper(startNode, visited, graph) +} + +// Recursive helper function for the DFS algorithm +// Takes in a current node, a visited object, and a graph object +function dfsHelper(node, visited, graph) { + // Mark the current node as visited by adding it to the visited object + visited[node] = true + + // Print the current node (or perform some other action) + console.log(node) + + // Get the adjacent nodes of the current node from the graph object + const adjacentNodes = graph[node] + + // Iterate over the adjacent nodes + for (let i = 0; i < adjacentNodes.length; i++) { + // Get the node of the current adjacent node + const adjacentNode = adjacentNodes[i].node + + // If the adjacent node has not been visited yet, recursively call dfsHelper with the adjacent node + if (!visited[adjacentNode]) { + dfsHelper(adjacentNode, visited, graph) + } + } +} + +// SAMPLE USE CASE +// The graph will be represented in form of an adjacency list. +/* + The graph will be in form of an object where each key represents the nodes and each value will be an array of the neighbors of the node. + + The array in values will be an object array, where each object has the node, which is the neighbor, and weight, which is the distance to that neighbor from the current node. +*/ + +const graph = { + 0: [{ node: 1, weight: 10 }], + 1: [ + { node: 0, weight: 10 }, + { node: 2, weight: 10 }, + { node: 3, weight: 5 }, + ], + 2: [{ node: 1, weight: 10 }], + 3: [ + { node: 1, weight: 5 }, + { node: 4, weight: 10 }, + { node: 5, weight: 10 }, + ], + 4: [ + { node: 3, weight: 10 }, + { node: 5, weight: 10 }, + ], + 5: [ + { node: 3, weight: 10 }, + { node: 4, weight: 10 }, + ], +} + +dfs(graph, 0) \ No newline at end of file diff --git a/Graphs/Graphs_dfs.py b/Graphs/Graphs_dfs.py new file mode 100644 index 00000000..3dcf74fa --- /dev/null +++ b/Graphs/Graphs_dfs.py @@ -0,0 +1,33 @@ +from collections import defaultdict + +class Graph: + def __init__(self): + self.graph = defaultdict(list) + + def insertEdge(self,v1,v2): + self.graph[v1].append(v2) + + def dfsSub(self,v,visited): + + visited.add(v) + print(v,end=" ") + + for neighbour in self.graph[v]: + if neighbour not in visited: + self.dfsSub(neighbour,visited) + + def dfs(self,v): + + visited = set() + self.dfsSub(v,visited) + +g=Graph() +g.insertEdge(1,2) +g.insertEdge(2,1) +g.insertEdge(3,4) +g.insertEdge(4,5) +g.insertEdge(5,6) +g.insertEdge(2,6) + +g.dfs(2) + diff --git a/Graphs/Graphs_dijkstras.js b/Graphs/Graphs_dijkstras.js new file mode 100644 index 00000000..f3cdf50e --- /dev/null +++ b/Graphs/Graphs_dijkstras.js @@ -0,0 +1,141 @@ +// A class representing a priority queue +class PriorityQueue { + constructor() { + this.items = [] + } + + // Adds an item to the priority queue with a given priority + enqueue(item, priority) { + let added = false + + for (let i = 0; i < this.items.length; i++) { + if (priority < this.items[i].priority) { + this.items.splice(i, 0, { item, priority }) + added = true + break + } + } + + if (!added) this.items.push({ item, priority }) + } + + // Removes and returns the item with minimum priority from the priority queue + dequeue() { + if (this.isEmpty()) return null + + return this.items.shift() + } + + // Returns true if the priority queue is empty, false otherwise + isEmpty() { + return this.items.length === 0 + } + + // Returns the number of items in the priority queue + size() { + return this.items.length + } +} + +// Performs Dijkstra's shortest path algorithm on a given graph with a specified start node +function dijkstra(graph, n, start) { + const visited = new Array(n) // An array to keep track of visited nodes + visited.fill(false) + + const distances = new Array(n) // An array to store the distances from the start node to each node in the graph based on indices + distances.fill(Number.POSITIVE_INFINITY) + distances[start] = 0 + + // An array to store the previous node in the shortest path to each node in the graph based on indices + const prev = new Array(n) + prev.fill(null) + + const pq = new PriorityQueue() //Creating a new priority queue + pq.enqueue(start, 0) // Initializing the priority queue + + while (!pq.isEmpty()) { + const val = pq.dequeue() // Dequeue the node with the lowest priority + const idx = val.item // Get the index of the dequeued node + const min = val.priority // Get the distance of the dequeued node + visited[idx] = true // Mark the node as visited + + if (distances[idx] < min) continue // If the distance of the dequeued node is less than the minimum distance, continue to the next iteration of the loop + + // Iterate over the neighbors of the dequeued/current node + for (let edge of graph[idx]) { + if (visited[edge]) continue // If the destination node of the edge has already been visited, continue to the next iteration of the loop + + const newDist = distances[idx] + edge.weight // Calculate the new distance to the destination node + + // If the new distance is less than the current distance to the destination node, update the previous node, distance, and add the destination node to the priority queue + if (newDist < distances[edge.node]) { + prev[edge.node] = idx + distances[edge.node] = newDist + pq.enqueue(edge.node, newDist) + } + } + } + + // Return the distances and previous nodes arrays in an object + return { distances, prev } +} + +// Finds the shortest path between the start and end nodes in the given graph using Dijkstra's algorithm +function findShortestPath(graph, n, start, end) { + + // check if start node is present in the graph, if not, throw error. + if (!graph.hasOwnProperty(start)) { + throw new Error(`Start node ${start} not found in graph`) + } + + // check if end node is present in the graph, if not, throw error. + if (!graph.hasOwnProperty(end)) { + throw new Error(`End node ${end} not found in graph`) + } + + const res = dijkstra(graph, n, start) // Run Dijkstra's algorithm to get the shortest distances and paths from the start node to all other nodes + const distances = res.distances // Extract the distances array from the result of Dijkstra's algorithm + const prev = res.prev // Extract the previous nodes array from the result of Dijkstra's algorithm + const path = [] // Initialize an empty array to store the shortest path from the start to end node + + // If the end node is not reachable from the start node, return an empty path + if (distances[parseInt(end)] == Number.POSITIVE_INFINITY) return path + + // Traverse the previous nodes array backwards from the end node to the start node to construct the shortest path + for (let i = end; i !== null; i = prev[i]) path.unshift(i) + + // Return the shortest path + return path +} + +// Sample Use Case +// The graph will be represented in form of an adjacency list. +/* + The graph will be in form of an object where each key represents the nodes and each value will be an array of the neighbors of the node. + + The array in values will be an object array, where each object has the node, which is the neighbor, and weight, which is the distance to that neighbor from the current node. +*/ +const graph = { + 0: [{ node: 1, weight: 10 }], + 1: [ + { node: 0, weight: 10 }, + { node: 2, weight: 10 }, + { node: 3, weight: 5 }, + ], + 2: [{ node: 1, weight: 10 }], + 3: [ + { node: 1, weight: 5 }, + { node: 4, weight: 10 }, + { node: 5, weight: 10 }, + ], + 4: [ + { node: 3, weight: 10 }, + { node: 5, weight: 10 }, + ], + 5: [ + { node: 3, weight: 10 }, + { node: 4, weight: 10 }, + ], +} + +console.log(findShortestPath(graph, 6, 2, 5)) \ No newline at end of file diff --git a/Graphs/Graphs_kruskals_algo.cpp b/Graphs/Graphs_kruskals_algo.cpp new file mode 100644 index 00000000..5d2c4584 --- /dev/null +++ b/Graphs/Graphs_kruskals_algo.cpp @@ -0,0 +1,136 @@ +/* +Name : MAnmay Ghosh +Github username : ManmayGhosh +Repository name : data-structures-and-algorithms +Problem : Implement Kruskal's algorithm in C++ +Issue Number : #1301 + +Explanation of the below C++ code : + +In this implementation, In Kruskal’s algorithm, sort all edges of the given graph in increasing order. +Then it keeps on adding new edges and nodes in the MST if the newly added edge does not form a cycle. +It picks the minimum weighted edge at first at the maximum weighted edge at last. +Thus we can say that it makes a locally optimal choice in each step in order to find the optimal solution + +pseudosteps +1. Sort all the edges in non-decreasing order of their weight. +2. Pick the smallest edge. Check if it forms a cycle with the spanning tree formed so far. + If the cycle is not formed, include this edge. Else, discard it. +3. Repeat step#2 until there are (V-1) edges in the spanning tree. +-------------------------------------------------------------------------//C++ code begins here------------------------------------------------------------------------ +*/ + +#include +using namespace std; + +// DS data structure class will help in building graph +class D_S { + int* parent; // to create a parent relationship b/w two nodes + int* child; // to create a child relationship b/w two nodes +public: + + //This function will create a user defined data structure which will help to create a graph + D_S(int n) + { + parent = new int[n]; + child = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = -1; + child[i] = 1; + } + } + + // Find function to find edges b/w vertices + int find(int i) + { + if (parent[i] == -1) + return i; + return parent[i] = find(parent[i]); + } + + // Union function to joint two nodes via smallest possible weighted edge + void unite(int x, int y) + { + int s1 = find(x); + int s2 = find(y); + + if (s1 != s2) { + if (child[s1] < child[s2]) + parent[s1] = s2; + else if (child[s1] > child[s2]) + parent[s2] = s1; + else { + parent[s2] = s1; + child[s1] += 1; + } + } + } +}; + +class Graph { + //As we know for kruskal's algorithm we have to maintain a list for edges b/w two vertices + //from lowest weight to highest weight in increasing order + vector> edgelist; + int V; + +public: + Graph(int V) { this->V = V; } + + // Function to add edge in a graph + void addEdge(int x, int y, int w) + { + edgelist.push_back({ w, x, y }); + } + + + //Kruskal's Algorithm + void kruskals_mst() + { + // Arrange all edges in ascending order to find minimum weight edge + sort(edgelist.begin(), edgelist.end()); + + // Initialize the DSU + D_S gr(V); + int ans = 0; + cout << "Following are the edges in the constructed MST"<< endl; + for (auto edge : edgelist) { + int w = edge[0]; + int x = edge[1]; + int y = edge[2]; + + // Take the edge in MST if it does not forms a cycle + if (gr.find(x) != gr.find(y)) { + gr.unite(x, y); + ans += w; + cout << x << " -- " << y << " == " << w<< endl; + } + } + cout << "Minimum Cost Spanning Tree: " << ans; + } +}; + +// Driver code +int main() +{ + Graph g(4); + g.addEdge(0, 1, 10); + g.addEdge(1, 3, 15); + g.addEdge(2, 3, 4); + g.addEdge(2, 0, 6); + g.addEdge(0, 3, 5); + + // Function call + g.kruskals_mst(); + + return 0; +} + + +/* +Time Complexity: O(E * logE) or O(E * logV) + +Sorting of edges takes O(E * logE) time. +After sorting, we iterate through all edges and apply the find-union algorithm. The find and union operations can take at most O(logV) time. +So overall complexity is O(E * logE + E * logV) time. +The value of E can be at most O(V2), so O(logV) and O(logE) are the same. Therefore, the overall time complexity is O(E * logE) or O(E*logV). +*/ \ No newline at end of file diff --git a/Graphs/Graphs_kruskals_algorithm.js b/Graphs/Graphs_kruskals_algorithm.js new file mode 100644 index 00000000..6a8388a3 --- /dev/null +++ b/Graphs/Graphs_kruskals_algorithm.js @@ -0,0 +1,97 @@ +/** + * Graphs: Implement Kruskal's Algorithm in JavaScript + * + * This code implements Kruskal's Algorithm to find the Minimum Spanning Tree (MST) + * in a graph. The graph is represented using an adjacency list. The algorithm works + * by repeatedly adding the minimum weight edges that do not form a cycle in the MST. + * + * Time Complexity: O(E log V), where E is the number of edges and V is the number of vertices. + * Space Complexity: O(V), where V is the number of vertices. + */ + +/** + * Class representing a Disjoint Set data structure. + * Used to track disjoint sets and perform union-find operations. + */ +class DisjointSet { + constructor(n) { + this.parent = new Array(n).fill(-1); // Array to store parent of each node + this.rank = new Array(n).fill(0); // Array to store the rank of each node + } + + /** + * Find the parent of a node in the disjoint set. + * Implements path compression to optimize future finds. + * @param {number} x - The node to find the parent for. + * @returns {number} The parent of the given node. + */ + find(x) { + if (this.parent[x] === -1) { + return x; + } + this.parent[x] = this.find(this.parent[x]); // Path compression + return this.parent[x]; + } + + /** + * Union two disjoint sets by rank. + * Uses union by rank to optimize the merge operation. + * @param {number} x - The first node to union. + * @param {number} y - The second node to union. + */ + union(x, y) { + let xRoot = this.find(x); + let yRoot = this.find(y); + + if (this.rank[xRoot] < this.rank[yRoot]) { + this.parent[xRoot] = yRoot; + } else if (this.rank[xRoot] > this.rank[yRoot]) { + this.parent[yRoot] = xRoot; + } else { + this.parent[yRoot] = xRoot; + this.rank[xRoot]++; + } + } +} + +/** + * Function to implement Kruskal's Algorithm for finding the Minimum Spanning Tree (MST). + * @param {number} n - The number of vertices in the graph. + * @param {Array} edges - The edges of the graph represented as an adjacency list. + * @returns {Array} The edges of the Minimum Spanning Tree. + */ +function kruskalsAlgorithm(n, edges) { + // Sort edges in non-decreasing order by weight + edges.sort((a, b) => a[2] - b[2]); + + const mst = []; // Minimum Spanning Tree + const disjointSet = new DisjointSet(n); // Create a disjoint set for tracking sets + + for (let [src, dest, weight] of edges) { + let srcRoot = disjointSet.find(src); + let destRoot = disjointSet.find(dest); + + // If adding the edge does not create a cycle, add it to the MST + if (srcRoot !== destRoot) { + mst.push([src, dest, weight]); + disjointSet.union(srcRoot, destRoot); + } + } + + return mst; +} + +// Example usage +const numVertices = 5; +const graphEdges = [ + [0, 1, 2], + [1, 2, 3], + [0, 3, 6], + [1, 3, 8], + [1, 4, 5], + [2, 4, 7], + [3, 4, 9], +]; + +const minimumSpanningTree = kruskalsAlgorithm(numVertices, graphEdges); +console.log("Minimum Spanning Tree:", minimumSpanningTree); diff --git a/Graphs/a_star_algorithm.py b/Graphs/a_star_algorithm.py new file mode 100644 index 00000000..47ec9840 --- /dev/null +++ b/Graphs/a_star_algorithm.py @@ -0,0 +1,160 @@ +""" +A* Algorithm + +Motivation - To approximate the shortest path in real-life situations, like- in maps, games where there can be many hindrances. + +Unlike other traversal techniques which focus on the path to be taken, A* algorithm focuses on the distance to be travelled. +It is a combination of Uniform Cost Search(UCS) and Best First Search(BFS). + +Explanation +A* algorithm is a best-first search algorithm in which the cost associated with a node is f(n) = g(n) + h(n), where g(n) is the cost of the path from the initial state to node n and h(n) is the heuristic estimate or the cost or a path from node n to a goal. +heuristic means guess or estimate. + +Consider a square grid having many obstacles and we are given a starting cell and a target cell. We want to reach the target cell (if possible) from the starting cell as quickly as possible. + +Here g(n) is the distance between the current cell and the start cell. So we can say g(n) is the cost of the path from initial state to n. +h(n) is a heuristic function that is used to estimate the cost of the cheapest path from n to the goal. So we can say h(n) is the cost from n to goal. + +So we can say f(n) is the cost of the cheapest path from initial state to goal passing through n. + +pseudocode +1. Initialize the open list +2. Initialize the closed list +put the starting node on the open list (you can leave its f at zero) + +3. while the open list is not empty + a) find the node with the least f on the open list, call it "q" + b) pop q off the open list + c) generate q's 8 successors and set their parents to q + d) for each successor + i) if successor is the goal, stop search + + ii) else, compute both g and h for successor + successor.g = q.g + distance between successor and q + successor.h = distance from goal to successor + successor.f = successor.g + successor.h + + iii) if a node with the same position as successor is in the OPEN list which has a lower f than successor, skip this successor + + iv) if a node with the same position as successor is in the CLOSED list which has a lower f than successor, skip this successor + otherwise, add the node to the open list + + e) push q on the closed list + +For more information, visit: https://www.geeksforgeeks.org/a-search-algorithm/ or https://stackabuse.com/courses/graphs-in-python-theory-and-implementation/lessons/a-star-search-algorithm/ + +""" + +from collections import deque + + +class Graph: + # example of adjacency list (or rather map) + # adjacency_list = { + # 'A': [('B', 1), ('C', 3), ('D', 7)], + # 'B': [('D', 5)], + # 'C': [('D', 12)] + # } + + def __init__(self, adjacency_list): + self.adjacency_list = adjacency_list + + def get_neighbors(self, v): + return self.adjacency_list[v] + + # heuristic function with equal values for all nodes + def h(self, n): + H = {"A": 1, "B": 1, "C": 1, "D": 1} + + return H[n] + + def a_star_algorithm(self, start_node, stop_node): + # open_list is a list of nodes which have been visited, but who's neighbors + # haven't all been inspected, starts off with the start node + # closed_list is a list of nodes which have been visited + # and who's neighbors have been inspected + open_list = set([start_node]) + closed_list = set([]) + + # g contains current distances from start_node to all other nodes + # the default value (if it's not found in the map) is +infinity + g = {} + + g[start_node] = 0 + + # parents contains an adjacency map of all nodes + parents = {} + parents[start_node] = start_node + + while len(open_list) > 0: + n = None + + # find a node with the lowest value of f() - evaluation function + for v in open_list: + if n == None or g[v] + self.h(v) < g[n] + self.h(n): + n = v + + if n == None: + print("Path does not exist!") + return None + + # if the current node is the stop_node + # then we begin reconstructin the path from it to the start_node + if n == stop_node: + reconst_path = [] + + while parents[n] != n: + reconst_path.append(n) + n = parents[n] + + reconst_path.append(start_node) + + reconst_path.reverse() + + print("Path found: {}".format(reconst_path)) + return reconst_path + + # for all neighbors of the current node do + for m, weight in self.get_neighbors(n): + # if the current node isn't in both open_list and closed_list + # add it to open_list and note n as it's parent + if m not in open_list and m not in closed_list: + open_list.add(m) + parents[m] = n + g[m] = g[n] + weight + + # otherwise, check if it's quicker to first visit n, then m + # and if it is, update parent data and g data + # and if the node was in the closed_list, move it to open_list + else: + if g[m] > g[n] + weight: + g[m] = g[n] + weight + parents[m] = n + + if m in closed_list: + closed_list.remove(m) + open_list.add(m) + + # remove n from the open_list, and add it to closed_list + # because all of his neighbors were inspected + open_list.remove(n) + closed_list.add(n) + + print("Path does not exist!") + return None + + +# Driver code + +adjacency_list = { + "A": [("B", 1), ("C", 3), ("D", 7)], + "B": [("D", 5)], + "C": [("D", 12)], +} + +graph1 = Graph(adjacency_list) +graph1.a_star_algorithm("A", "D") + +# Output: +# Path found: ['A', 'B', 'D'] +# Explanation: The path with the lowest cost is A -> B -> D with a cost of 6. diff --git a/Graphs/adjacency_list.java b/Graphs/adjacency_list.java new file mode 100644 index 00000000..312e1a22 --- /dev/null +++ b/Graphs/adjacency_list.java @@ -0,0 +1,101 @@ +/* + This code is an implementation of a graph data structure using an adjacency list representation in Java. Let's break down the code and explain it step by step: + + 1. `GraphNode` Class: + - This class represents a node in the graph. + - It has three attributes: + - `name`: A string that represents the name or label of the node. + - `index`: An integer that serves as a unique identifier/index for the node. + - `neighbors`: An ArrayList of `GraphNode` objects that stores the neighboring nodes of the current node. + + 2. `AdjacencyList` Class: + - This class represents the graph using an adjacency list. + - It has the following attributes: + - `nodeList`: An ArrayList of `GraphNode` objects that stores all the nodes in the graph. + - The constructor takes an ArrayList of `GraphNode` objects and initializes the `nodeList` attribute with it. + - The class provides the following methods: + - `addUndirectedEdge(int i, int j)`: This method takes two indices (`i` and `j`) representing two nodes in the graph and + adds an undirected edge between them. It does so by retrieving the corresponding `GraphNode` objects from the `nodeList` + and adding each node to the `neighbors` list of the other. + - `printGraph()`: This method returns a human-readable representation of the graph. It iterates through the `nodeList`, + prints the name of each node, and lists its neighboring nodes. + + 3. `main` Class: + - This is the main class to demonstrate the graph creation and printing. + - Inside the `main` method: + - An ArrayList of `GraphNode` objects (`nodeList`) is created, and five nodes are added to it, each with a name and index. + - An `AdjacencyList` object (`al`) is created, passing the `nodeList` to its constructor. + - Several undirected edges are added using the `addUndirectedEdge` method to establish connections between nodes. + - Finally, the `printGraph` method is called on the `al` object to print the graph's structure. + + The code demonstrates how to create a graph using an adjacency list and provides a readable representation of the graph, including its nodes and edges. +*/ + + +import java.util.ArrayList; + +public class GraphNode { + public String name; + public int index; + // An ArrayList to store neighboring nodes + public ArrayList neighbors = new ArrayList(); + + public GraphNode(String name, int index) { + this.name = name; + this.index = index; + } +} + +public class AdjacencyList { + // An ArrayList to store all nodes in the graph + ArrayList nodeList = new ArrayList(); + + public AdjacencyList(ArrayList nodeList) { + this.nodeList = nodeList; + } + + // Method to add an undirected edge between two nodes + public void addUndirectedEdge(int i, int j) { + GraphNode first = nodeList.get(i); + GraphNode second = nodeList.get(j); + first.neighbors.add(second); + second.neighbors.add(first); + } + + // Method to print a human-readable representation of the graph + public String printGraph() { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < nodeList.size(); i++) { + s.append(nodeList.get(i).name + ": "); + for (int j = 0; j < nodeList.get(i).neighbors.size(); j++) { + if (j == nodeList.get(i).neighbors.size() - 1) { + s.append(nodeList.get(i).neighbors.get(j).name); + } else { + s.append(nodeList.get(i).neighbors.get(j).name + " --> "); + } + } + s.append("\n"); + } + return s.toString(); + } +} + +public class Main { + public static void main(String[] args) { + ArrayList nodeList = new ArrayList(); + nodeList.add(new GraphNode("A", 0)); + nodeList.add(new GraphNode("B", 1)); + nodeList add(new GraphNode("C", 2)); + nodeList.add(new GraphNode("D", 3)); + nodeList.add(new GraphNode("E", 4)); + + AdjacencyList al = new AdjacencyList(nodeList); + al.addUndirectedEdge(0, 1); + al.addUndirectedEdge(0, 2); + al.addUndirectedEdge(0, 3); + al.addUndirectedEdge(1, 4); + al.addUndirectedEdge(2, 3); + al.addUndirectedEdge(3, 4); + System.out.println(al.printGraph()); + } +} diff --git a/Graphs/adjacency_matrix.java b/Graphs/adjacency_matrix.java new file mode 100644 index 00000000..12815a94 --- /dev/null +++ b/Graphs/adjacency_matrix.java @@ -0,0 +1,118 @@ +/* + This code snippet is a Java implementation of a graph using an adjacency matrix to represent the connections between nodes. Here's an explanation of the code: + + 1. `GraphNode` Class: + - `GraphNode` represents a node or vertex in the graph. + - It has two attributes: `name` to store the name of the node and `index` to store the index of the node. + - The constructor initializes these attributes. + + 2. `AdjacencyMatrix` Class: + - `AdjacencyMatrix` represents the graph using an adjacency matrix. + - It has the following attributes: + - `nodeList`: An `ArrayList` that stores all the nodes in the graph. + - `adjacencyMatrix`: A 2D array that represents the connections between nodes. + - The constructor takes an `ArrayList` of `GraphNode` objects and initializes the `nodeList` and `adjacencyMatrix` based on the size of the node list. + + 3. `addUndirectedEdge` Method: + - This method is used to add an undirected edge between two nodes. + - It takes two indices `i` and `j` to represent the nodes between which the edge is added. + - It sets the corresponding values in the adjacency matrix to 1, indicating an edge exists between nodes `i` and `j`. Since it's an undirected graph, it sets both `adjacencyMatrix[i][j]` and `adjacencyMatrix[j][i]` to 1. + + 4. `printGraph` Method: + - This method generates a human-readable string representation of the graph, including the adjacency matrix. + - It first prints the node names as column headers and then iterates through the adjacency matrix to display the connections between nodes. + + 5. `Main` Class: + - In the `Main` class, a list of `GraphNode` objects (`nodeList`) is created, each representing a node with a name and an index. + - An `AdjacencyMatrix` object (`am`) is created, passing the `nodeList` to its constructor. + - Undirected edges are added between nodes using the `addUndirectedEdge` method. + - Finally, the graph is printed using the `printGraph` method. + + The example in the `main` method demonstrates the creation of a simple graph with five nodes and several edges. When you run this program, it will print a representation of the graph showing the connections between the nodes based on the adjacency matrix. + + Output: + A B C D E + A: 0 1 1 1 0 + B: 1 0 0 0 1 + C: 1 0 0 1 0 + D: 1 0 1 0 1 + E: 0 1 0 1 0 + +*/ +import java.util.ArrayList; + +// Class to represent a graph node +public class GraphNode { + public String name; + public int index; + + // Constructor to initialize name and index + GraphNode(String name, int index) { + this.name = name; + this.index = index; + } +} + +// Class to represent a graph using an adjacency matrix +public class AdjacencyMatrix { + ArrayList nodeList = new ArrayList(); + int[][] adjacencyMatrix; + + // Constructor to initialize nodeList and adjacencyMatrix + public AdjacencyMatrix(ArrayList nodeList) { + this.nodeList = nodeList; + adjacencyMatrix = new int[nodeList.size()][nodeList.size()]; + } + + // Method to add an undirected edge between two nodes + public void addUndirectedEdge(int i, int j) { + adjacencyMatrix[i][j] = 1; + adjacencyMatrix[j][i] = 1; + } + + // Method to print a human-readable representation of the graph + public String printGraph() { + StringBuilder s = new StringBuilder(); + + // Print column headers (node names) + s.append(" "); + for (int i = 0; i < nodeList.size(); i++) { + s.append(nodeList.get(i).name + " "); + } + s.append("\n"); + + // Print node names and adjacency matrix + for (int i = 0; i < nodeList.size(); i++) { + s.append(nodeList.get(i).name + ": "); + for (int j : adjacencyMatrix[i]) { + s.append((j) + " "); + } + s.append("\n"); + } + return s.toString(); + } +} + +// Main class to demonstrate the graph creation +public class Main { + public static void main(String[] args) { + ArrayList nodeList = new ArrayList(); + nodeList.add(new GraphNode("A", 0)); + nodeList.add(new GraphNode("B", 1)); + nodeList.add(new GraphNode("C", 2)); + nodeList.add(new GraphNode("D", 3)); + nodeList.add(new GraphNode("E", 4)); + AdjacencyMatrix am = new AdjacencyMatrix(nodeList); + + // Adding undirected edges + am.addUndirectedEdge(0, 1); + am.addUndirectedEdge(0, 2); + am.addUndirectedEdge(0, 3); + am.addUndirectedEdge(1, 4); + am.addUndirectedEdge(2, 3); + am.addUndirectedEdge(3, 4); + + // Printing the graph + System.out.println(am.printGraph()); + } +} diff --git a/Graphs/dijkstras.cpp b/Graphs/dijkstras.cpp new file mode 100644 index 00000000..52d9dfe7 --- /dev/null +++ b/Graphs/dijkstras.cpp @@ -0,0 +1,93 @@ +/*Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Implement Dijkstra's algorithm in C++ +Issue Number : #947 +Problem statement : + +Explanation of the below C++ code : + +In this implementation, we have a dijkstra function that takes a graph represented as an adjacency list, the starting node, and the total number of nodes. It returns a vector containing the shortest distances from the start node to all other nodes. + +The dijkstra function initializes all distances to infinity except for the start node, which is set to 0. It uses a min heap priority queue to process nodes based on their distances. The algorithm iteratively selects the node with the minimum distance, updates the distances of its neighbors if a shorter path is found, and adds them to the priority queue. + +In the main function, we create a graph using the adjacency list representation. Each element of the graph vector is a vector of pairs, where the first element of the pair represents the neighbor node, and the second element represents the weight of the edge. + +We then call the dijkstra function with the graph, starting node, and the total number of nodes. Finally, we print the shortest distances from the start node to all other nodes. + + +*/ + +-------------------------------------------------------------------------//C++ code begins here------------------------------------------------------------------------ + + +#include +#include +#include +#include + +using namespace std; + +typedef pair pii; + +vector dijkstra(vector>& graph, int start, int n) { + vector dist(n, INT_MAX); // Initialize distances to infinity + dist[start] = 0; // Distance from start node to itself is 0 + + // Create a min heap priority queue to store vertices based on their distances + priority_queue, greater> pq; + pq.push(make_pair(0, start)); + + while (!pq.empty()) { + int u = pq.top().second; + pq.pop(); + + // Traverse all neighboring nodes of u + for (auto& neighbor : graph[u]) { + int v = neighbor.first; + int weight = neighbor.second; + + // Update distance if a shorter path is found + if (dist[v] > dist[u] + weight) { + dist[v] = dist[u] + weight; + pq.push(make_pair(dist[v], v)); + } + } + } + + return dist; +} + +int main() { + int n, m; // Number of nodes and edges + int start; // Starting node + + cout << "Enter the number of nodes: "; + cin >> n; + + cout << "Enter the number of edges: "; + cin >> m; + + cout << "Enter the starting node: "; + cin >> start; + + // Create an adjacency list representation of the graph + vector> graph(n); + + cout << "Enter the edges and their weights (node1 node2 weight):" << endl; + for (int i = 0; i < m; i++) { + int node1, node2, weight; + cin >> node1 >> node2 >> weight; + graph[node1].push_back(make_pair(node2, weight)); + } + + // Run Dijkstra's algorithm + vector distances = dijkstra(graph, start, n); + + // Print the shortest distances from the start node to all other nodes + for (int i = 0; i < n; i++) { + cout << "Shortest distance from node " << start << " to node " << i << ": " << distances[i] << endl; + } + + return 0; +} diff --git a/Graphs/dijkstras.go b/Graphs/dijkstras.go new file mode 100644 index 00000000..3376d266 --- /dev/null +++ b/Graphs/dijkstras.go @@ -0,0 +1,132 @@ +/* + + Write a function that computes the lengths of the shortest paths between start and all of the other vertices in the graph using Dijkstra's algorithm and returns them in an array. + + Sample Input: + Start: 0 + Edges : = [ + [[1, 7]], + [[2, 6], [3, 20], [4, 3]], + [[3, 14]], + [[4, 2]], + [], + [], + ] + Output: [0, 7, 13, 27, 10, -1] + + Dijkstras Algorithm + + Explanation: + + The code snippet is an implementation of Dijkstra's algorithm for finding the shortest path from a given starting vertex to all other vertices in a graph. Here's a breakdown of the code: + + 1. The `DijkstrasAlgorithm` function takes the starting vertex (`start`) and the graph represented by the adjacency list (`edges`) as input and returns a list of minimum distances from the starting vertex to all other vertices. + + 2. It initializes `numberOfVertices` as the total number of vertices in the graph. + + 3. The `minDistances` slice is initialized with maximum integer values to represent infinity distance for all vertices. The length of `minDistances` is set to the number of vertices. + + 4. The minimum distance from the starting vertex to itself is set to 0. + + 5. The `visited` map is used to keep track of visited vertices. Initially, it is empty. + + 6. The algorithm iterates until all vertices have been visited. In each iteration, it selects the vertex with the minimum distance from the `minDistances` slice using the `getVertexWithMinDistance` function. + + 7. If the current minimum distance is infinity (i.e., no more vertices to visit), the loop breaks. + + 8. The selected vertex is marked as visited by adding it to the `visited` map. + + 9. For each neighboring vertex of the selected vertex, it calculates the new path distance and updates the `minDistances` if the new distance is smaller. + + 10. After all iterations, the `finalDistances` slice is created to convert the `minDistances` into a format where unreachable vertices are represented as -1. + + 11. The `getVertexWithMinDistance` function returns the vertex with the minimum distance from the `distances` slice and the current minimum distance. + + Overall, the code implements Dijkstra's algorithm to find the shortest path from a starting vertex to all other vertices in a graph, using an adjacency list representation. It keeps track of minimum distances, visited vertices, and updates the distances based on the neighboring vertices. + + Time Complexity: O(V^2 + e) + Space complexity: O(V) +*/ +package main + +import "math" + +// DijkstrasAlgorithm finds the shortest path from a starting vertex to all other vertices in a graph. +func DijkstrasAlgorithm(start int, edges [][][]int) []int { + numberOfVertices := len(edges) + minDistances := make([]int, 0, len(edges)) + + // Initialize the minDistances slice with maximum integer values + for range edges { + minDistances = append(minDistances, math.MaxInt32) + } + + // Set the distance of the starting vertex to 0 + minDistances[start] = 0 + visited := map[int]bool{} + + // Iterate until all vertices have been visited + for len(visited) != numberOfVertices { + // Get the vertex with the minimum distance + vertex, currentMinDistance := getVertexWithMinDistance(minDistances, visited) + + // If the current minimum distance is infinity, break the loop + if currentMinDistance == math.MaxInt32 { + break + } + + // Mark the vertex as visited + visited[vertex] = true + + // Explore neighboring vertices + for _, edge := range edges[vertex] { + destination, distanceToDestination := edge[0], edge[1] + + // Skip if the destination vertex is already visited + if visited[destination] { + continue + } + + // Calculate the new path distance to the destination + newPathDistance := currentMinDistance + distanceToDestination + currentDestinationDistance := minDistances[destination] + + // Update the minimum distance if the new distance is smaller + if newPathDistance < currentDestinationDistance { + minDistances[destination] = newPathDistance + } + } + } + + // Convert the minDistances slice to finalDistances, representing unreachable vertices as -1 + finalDistances := make([]int, 0, len(minDistances)) + for _, distance := range minDistances { + if distance == math.MaxInt32 { + finalDistances = append(finalDistances, -1) + } else { + finalDistances = append(finalDistances, distance) + } + } + + return finalDistances +} + +// getVertexWithMinDistance returns the vertex with the minimum distance from the distances slice. +func getVertexWithMinDistance(distances []int, visited map[int]bool) (int, int) { + currentMinDistance := math.MaxInt32 + vertex := -1 + + // Find the vertex with the minimum distance among unvisited vertices + for vertexIdx, distance := range distances { + if visited[vertexIdx] { + continue + } + + if distance <= currentMinDistance { + vertex = vertexIdx + currentMinDistance = distance + } + } + + return vertex, currentMinDistance +} diff --git a/Graphs/dijkstras.java b/Graphs/dijkstras.java new file mode 100644 index 00000000..70c9ed80 --- /dev/null +++ b/Graphs/dijkstras.java @@ -0,0 +1,100 @@ +import java.util.*; + +class Main { + // Represents a node in the graph + static class Node implements Comparable { + String name; + int distance; + + Node(String name) { + this.name = name; + this.distance = Integer.MAX_VALUE; // Initialize distance to infinity + } + + @Override + public int compareTo(Node other) { + return Integer.compare(this.distance, other.distance); + } + } + + // Represents a weighted edge between two nodes + static class Edge { + Node source; + Node destination; + int weight; + + Edge(Node source, Node destination, int weight) { + this.source = source; + this.destination = destination; + this.weight = weight; + } + } + + // Dijkstra's algorithm implementation + static void dijkstra(Map> graph, String start) { + Map nodes = new HashMap<>(); // Stores nodes and their distances + + // Initialize nodes with their respective distances + for (String nodeName : graph.keySet()) { + Node node = new Node(nodeName); + if (nodeName.equals(start)) { + node.distance = 0; // Set distance of start node to 0 + } + nodes.put(nodeName, node); + } + + PriorityQueue queue = new PriorityQueue<>(); // Priority queue for node selection + queue.add(nodes.get(start)); // Add the start node to the queue + + // Dijkstra's algorithm main loop + while (!queue.isEmpty()) { + Node current = queue.poll(); // Get the node with the smallest distance from the queue + + // Iterate over the edges of the current node + for (Edge edge : graph.get(current.name)) { + int newDistance = current.distance + edge.weight; // Calculate new distance to the neighbor node + Node neighbor = nodes.get(edge.destination.name); // Get the neighbor node + + // If the new distance is shorter, update the neighbor node's distance and re-add it to the queue + if (newDistance < neighbor.distance) { + queue.remove(neighbor); // Remove the neighbor node from the queue + neighbor.distance = newDistance; // Update the distance of the neighbor node + queue.add(neighbor); // Re-add the neighbor node to the queue + } + } + } + } + + public static void main(String[] args) { + Map> graph = new HashMap<>(); // Graph represented as a map of nodes and edges + + // Create edges and add them to the graph + List edgesA = new ArrayList<>(); + edgesA.add(new Edge(new Node("A"), new Node("B"), 2)); + edgesA.add(new Edge(new Node("A"), new Node("C"), 3)); + graph.put("A", edgesA); + + List edgesB = new ArrayList<>(); + edgesB.add(new Edge(new Node("B"), new Node("C"), 1)); + edgesB.add(new Edge(new Node("B"), new Node("D"), 1)); + graph.put("B", edgesB); + + List edgesC = new ArrayList<>(); + edgesC.add(new Edge(new Node("C"), new Node("D"), 4)); + graph.put("C", edgesC); + + List edgesD = new ArrayList<>(); + edgesD.add(new Edge(new Node("D"), new Node("C"), 2)); + graph.put("D", edgesD); + + String start = "A"; // Starting node + dijkstra(graph, start); // Run Dijkstra's algorithm + + // Print the shortest distances from the start node to each node in the graph + for (String nodeName : graph.keySet()) { + Node node = graph.get(nodeName).get(0).source; + System.out.println("Shortest distance from " + start + " to " + node.name + ": " + node.distance); + } + } +} + diff --git a/Graphs/dijkstras.py b/Graphs/dijkstras.py new file mode 100644 index 00000000..8fb69c3d --- /dev/null +++ b/Graphs/dijkstras.py @@ -0,0 +1,133 @@ + +''' + + Write a function that computes the lengths of the shortest paths between start and all of the other vertices in the graph using Dijkstra's algorithm and returns them in an array. + + Sample Input: + Start: 0 + Edges : = [ + [[1, 7]], + [[2, 6], [3, 20], [4, 3]], + [[3, 14]], + [[4, 2]], + [], + [], + ] + Output: [0, 7, 13, 27, 10, -1] + + Dijkstras Algorithm + + Explanation: + + The code snippet is an implementation of Dijkstra's algorithm for finding the shortest path from a given starting vertex to all other vertices in a graph. Here's a breakdown of the code: + + 1. The `DijkstrasAlgorithm` function takes the starting vertex (`start`) and the graph represented by the adjacency list (`edges`) as input and returns a list of minimum distances from the starting vertex to all other vertices. + + 2. It initializes `numberOfVertices` as the total number of vertices in the graph. + + 3. The `minDistances` slice is initialized with maximum integer values to represent infinity distance for all vertices. The length of `minDistances` is set to the number of vertices. + + 4. The minimum distance from the starting vertex to itself is set to 0. + + 5. The `visited` map is used to keep track of visited vertices. Initially, it is empty. + + 6. The algorithm iterates until all vertices have been visited. In each iteration, it selects the vertex with the minimum distance from the `minDistances` slice using the `getVertexWithMinDistance` function. + + 7. If the current minimum distance is infinity (i.e., no more vertices to visit), the loop breaks. + + 8. The selected vertex is marked as visited by adding it to the `visited` map. + + 9. For each neighboring vertex of the selected vertex, it calculates the new path distance and updates the `minDistances` if the new distance is smaller. + + 10. After all iterations, the `finalDistances` slice is created to convert the `minDistances` into a format where unreachable vertices are represented as -1. + + 11. The `getVertexWithMinDistance` function returns the vertex with the minimum distance from the `distances` slice and the current minimum distance. + + Overall, the code implements Dijkstra's algorithm to find the shortest path from a starting vertex to all other vertices in a graph, using an adjacency list representation. It keeps track of minimum distances, visited vertices, and updates the distances based on the neighboring vertices. + + Time Complexity: O(V^2 + e) + Space complexity: O(V) + +''' + +import "math" + +# DijkstrasAlgorithm finds the shortest path from a starting vertex to all other vertices in a graph. +func DijkstrasAlgorithm(start int, edges [][][]int) []int { + numberOfVertices := len(edges) + minDistances := make([]int, 0, len(edges)) + + # Initialize the minDistances slice with maximum integer values + for range edges { + minDistances = append(minDistances, math.MaxInt32) + } + + # Set the distance of the starting vertex to 0 + minDistances[start] = 0 + visited := map[int]bool{} + + # Iterate until all vertices have been visited + for len(visited) != numberOfVertices { + # Get the vertex with the minimum distance + vertex, currentMinDistance := getVertexWithMinDistance(minDistances, visited) + + # If the current minimum distance is infinity, break the loop + if currentMinDistance == math.MaxInt32 { + break + } + + # Mark the vertex as visited + visited[vertex] = true + + # Explore neighboring vertices + for _, edge := range edges[vertex] { + destination, distanceToDestination := edge[0], edge[1] + + # Skip if the destination vertex is already visited + if visited[destination] { + continue + } + + # Calculate the new path distance to the destination + newPathDistance := currentMinDistance + distanceToDestination + currentDestinationDistance := minDistances[destination] + + # Update the minimum distance if the new distance is smaller + if newPathDistance < currentDestinationDistance { + minDistances[destination] = newPathDistance + } + } + } + + # Convert the minDistances slice to finalDistances, representing unreachable vertices as -1 + finalDistances := make([]int, 0, len(minDistances)) + for _, distance := range minDistances { + if distance == math.MaxInt32 { + finalDistances = append(finalDistances, -1) + } else { + finalDistances = append(finalDistances, distance) + } + } + + return finalDistances +} + +# getVertexWithMinDistance returns the vertex with the minimum distance from the distances slice. +func getVertexWithMinDistance(distances []int, visited map[int]bool) (int, int) { + currentMinDistance := math.MaxInt32 + vertex := -1 + + # Find the vertex with the minimum distance among unvisited vertices + for vertexIdx, distance := range distances { + if visited[vertexIdx] { + continue + } + + if distance <= currentMinDistance { + vertex = vertexIdx + currentMinDistance = distance + } + } + + return vertex, currentMinDistance +} diff --git a/Graphs/dijkstras_heap_based.go b/Graphs/dijkstras_heap_based.go new file mode 100644 index 00000000..af9736a9 --- /dev/null +++ b/Graphs/dijkstras_heap_based.go @@ -0,0 +1,211 @@ +/* + Dijkstras heap based + + Explanation: + The given code snippet implements Dijkstra's algorithm to find the shortest path from a given start vertex to all other + vertices in a weighted directed graph. Here's how the code works: + + 1. The `DijkstrasAlgorithm` function takes the start vertex and the edges of the graph as input and returns an array of + shortest distances from the start vertex to all other vertices. + + 2. First, it initializes the `minDistances` array with the maximum integer value except for the start vertex, which is + set to 0. This array will store the minimum distances from the start vertex to each vertex. + + 3. It creates a min-heap data structure called `minDistancesHeap` to keep track of the minimum distances. Each item in + the heap represents a vertex and its distance from the start vertex. + + 4. The algorithm starts by removing the vertex with the minimum distance from the `minDistancesHeap` and explores its + outgoing edges. + + 5. For each edge, it calculates the new path distance from the start vertex to the destination vertex through the + current vertex. If the new path distance is smaller than the current distance, it updates the `minDistances` array and the `minDistancesHeap` with the new distance. + + 6. The process continues until all vertices have been visited. + + 7. Finally, it constructs the final distances array (`finalDistances`) based on the `minDistances` array. If a vertex's + distance is still set to the maximum integer value, it means there is no path from the start vertex to that vertex, so -1 + is stored instead. + + 8. The `Item` struct represents a vertex and its distance in the min-heap. + + 9. The `MinHeap` struct represents the min-heap data structure. It contains an array of `Item` structs and a vertex-to-index + map to efficiently update and access items in the heap. + + 10. The `Remove` method removes the item with the minimum distance from the heap and returns its vertex and distance. + + 11. The `Update` method updates the distance of a vertex in the heap and maintains the heap property by performing sift-up + or sift-down operations. + + 12. The `siftDown` method performs the sift-down operation to maintain the heap property by comparing the distance of the + current item with its children and swapping if necessary. + + 13. The `siftUp` method performs the sift-up operation to maintain the heap property by comparing the distance of the + current item with its parent and swapping if necessary. + + The time complexity of Dijkstra's algorithm with a min-heap implementation is typically O((V + E) log V), where V is the + number of vertices and E is the number of edges in the graph. + The space complexity is O(V) for storing the `minDistances` array and the min-heap. +*/ +package main + +import "math" + +// DijkstrasAlgorithm finds the shortest distances from the start vertex to all other vertices using Dijkstra's algorithm. +func DijkstrasAlgorithm(start int, edges [][][]int) []int { + // Get the number of vertices in the graph. + numberOfVertices := len(edges) + + // Initialize the minDistances array with maximum integer values except for the start vertex, which is set to 0. + minDistances := make([]int, 0, numberOfVertices) + for range edges { + minDistances = append(minDistances, math.MaxInt32) + } + minDistances[start] = 0 + + // Create a min-heap to store vertices and their distances from the start vertex. + minDistancePairs := make([]Item, 0, len(edges)) + for i := range edges { + minDistancePairs = append(minDistancePairs, Item{i, math.MaxInt32}) + } + minDistancesHeap := NewMinHeap(minDistancePairs) + minDistancesHeap.Update(start, 0) + + // Explore vertices in the graph using Dijkstra's algorithm until all vertices have been visited. + for !minDistancesHeap.IsEmpty() { + vertex, currentMinDistance := minDistancesHeap.Remove() + + // If the currentMinDistance is still set to the maximum integer value, there is no path from the start vertex to this vertex. + if currentMinDistance == math.MaxInt32 { + break + } + + // Explore the outgoing edges of the current vertex. + for _, edge := range edges[vertex] { + destination, distanceToDestination := edge[0], edge[1] + + // Calculate the new path distance from the start vertex to the destination vertex through the current vertex. + newPathDistance := currentMinDistance + distanceToDestination + + // Update the minDistances array and the minDistancesHeap with the new distance if it is smaller. + currentDestinationDistance := minDistances[destination] + if newPathDistance < currentDestinationDistance { + minDistances[destination] = newPathDistance + minDistancesHeap.Update(destination, newPathDistance) + } + } + } + + // Construct the finalDistances array based on the minDistances array. + finalDistances := make([]int, 0, len(minDistances)) + for _, distance := range minDistances { + // If a vertex's distance is still set to the maximum integer value, there is no path from the start vertex to that vertex. + // So, -1 is stored instead. + if distance == math.MaxInt32 { + finalDistances = append(finalDistances, -1) + } else { + finalDistances = append(finalDistances, distance) + } + } + + return finalDistances +} + +// Item represents a vertex and its distance in the min-heap. +type Item struct { + Vertex int // Vertex represents the index of the vertex. + Distance int // Distance represents the distance from the start vertex to the current vertex. +} + +// MinHeap represents a min-heap data structure. +type MinHeap struct { + array []Item // array is an array of Item structs to represent the heap. + vertexMap map[int]int // vertexMap is a map to efficiently update and access items in the heap. +} + +// NewMinHeap creates a new MinHeap instance with the given array of items. +func NewMinHeap(array []Item) *MinHeap { + // Initialize the vertexMap to store the index of each vertex in the heap. + vertexMap := map[int]int{} + for _, item := range array { + vertexMap[item.Vertex] = item.Vertex + } + + // Create the MinHeap with the array and vertexMap. + heap := &MinHeap{array: array, vertexMap: vertexMap} + heap.buildHeap() + return heap +} + +// IsEmpty checks if the min-heap is empty. +func (h *MinHeap) IsEmpty() bool { + return h.length() == 0 +} + +// Remove removes the item with the minimum distance from the heap and returns its vertex and distance. +func (h *MinHeap) Remove() (int, int) { + l := h.length() + h.swap(0, l-1) + peeked := h.array[l-1] + h.array = h.array[0:l-1] + delete(h.vertexMap, peeked.Vertex) + h.siftDown(0, l-2) + return peeked.Vertex, peeked.Distance +} + +// Update updates the distance of a vertex in the heap and maintains the heap property. +func (h *MinHeap) Update(vertex int, value int) { + h.array[h.vertexMap[vertex]] = Item{vertex, value} + h.siftUp(h.vertexMap[vertex]) +} + +// swap swaps two items in the min-heap. +func (h MinHeap) swap(i, j int) { + h.vertexMap[h.array[i].Vertex] = j + h.vertexMap[h.array[j].Vertex] = i + h.array[i], h.array[j] = h.array[j], h.array[i] +} + +// length returns the length of the min-heap. +func (h MinHeap) length() int { + return len(h.array) +} + +// buildHeap performs the build-heap operation on the min-heap. +func (h *MinHeap) buildHeap() { + first := (len(h.array) - 2) / 2 + for currentIdx := first + 1; currentIdx >= 0; currentIdx-- { + h.siftDown(currentIdx, len(h.array)-1) + } +} + +// siftDown performs the sift-down operation to maintain the heap property. +func (h *MinHeap) siftDown(currentIdx, endIdx int) { + childOneIdx := currentIdx*2 + 1 + for childOneIdx <= endIdx { + childTwoIdx := -1 + if currentIdx*2+2 <= endIdx { + childTwoIdx = currentIdx*2 + 2 + } + indexToSwap := childOneIdx + if childTwoIdx > -1 && h.array[childTwoIdx].Distance < h.array[childOneIdx].Distance { + indexToSwap = childTwoIdx + } + if h.array[indexToSwap].Distance < h.array[currentIdx].Distance { + h.swap(currentIdx, indexToSwap) + currentIdx = indexToSwap + childOneIdx = currentIdx*2 + 1 + } else { + return + } + } +} + +// siftUp performs the sift-up operation to maintain the heap property. +func (h *MinHeap) siftUp(currentIdx int) { + parentIdx := (currentIdx - 1) / 2 + for currentIdx > 0 && h.array[currentIdx].Distance < h.array[parentIdx].Distance { + h.swap(currentIdx, parentIdx) + currentIdx = parentIdx + parentIdx = (currentIdx - 1) / 2 + } +} diff --git a/Graphs/ford_fulkerson.cpp b/Graphs/ford_fulkerson.cpp new file mode 100644 index 00000000..828b243d --- /dev/null +++ b/Graphs/ford_fulkerson.cpp @@ -0,0 +1,145 @@ +/* + Name : Shruti Swarupa Dhar + Github username : Shr-reny + Repository name : data-structures-and-algorithms + Problem : Implement Ford Fulkerson algorithm in C++ + Issue Number : #1386 + Problem statement : + + Given a graph which represents a flow network where every edge has a capacity. Also, given two vertices source ‘s’ and sink ‘t’ in the graph, find the maximum possible flow from s to t with the following constraints: + Flow on an edge doesn’t exceed the given capacity of the edge. + Incoming flow is equal to outgoing flow for every vertex except s and t. + + Explanation of the below C++ code : + + The Ford-Fulkerson algorithm is a widely used algorithm to solve the maximum flow problem in a flow network. The maximum flow problem involves determining the maximum amount of flow that can be sent from a source vertex to a sink vertex in a directed weighted graph, subject to capacity constraints on the edges. + The algorithm works by iteratively finding an augmenting path, which is a path from the source to the sink in the residual graph, i.e., the graph obtained by subtracting the current flow from the capacity of each edge. The algorithm then increases the flow along this path by the maximum possible amount, which is the minimum capacity of the edges along the path. + + Time Complexity : O(|V| * E^2) ,where E is the number of edges and V is the number of vertices. + + Space Complexity :O(V) , as we created queue. +*/ + + +// C++ program for implementation of Ford Fulkerson +// algorithm +#include +#include +#include +#include +using namespace std; + +// Number of vertices in given graph +#define V 6 + +/* Returns true if there is a path from source 's' to sink +'t' in residual graph. Also fills parent[] to store the +path */ +bool bfs(int rGraph[V][V], int s, int t, int parent[]) +{ + // Create a visited array and mark all vertices as not + // visited + bool visited[V]; + memset(visited, 0, sizeof(visited)); + + // Create a queue, enqueue source vertex and mark source + // vertex as visited + queue q; + q.push(s); + visited[s] = true; + parent[s] = -1; + + // Standard BFS Loop + while (!q.empty()) { + int u = q.front(); + q.pop(); + + for (int v = 0; v < V; v++) { + if (visited[v] == false && rGraph[u][v] > 0) { + // If we find a connection to the sink node, + // then there is no point in BFS anymore We + // just have to set its parent and can return + // true + if (v == t) { + parent[v] = u; + return true; + } + q.push(v); + parent[v] = u; + visited[v] = true; + } + } + } + + // We didn't reach sink in BFS starting from source, so + // return false + return false; +} + +// Returns the maximum flow from s to t in the given graph +int fordFulkerson(int graph[V][V], int s, int t) +{ + int u, v; + + // Create a residual graph and fill the residual graph + // with given capacities in the original graph as + // residual capacities in residual graph + int rGraph[V] + [V]; // Residual graph where rGraph[i][j] + // indicates residual capacity of edge + // from i to j (if there is an edge. If + // rGraph[i][j] is 0, then there is not) + for (u = 0; u < V; u++) + for (v = 0; v < V; v++) + rGraph[u][v] = graph[u][v]; + + int parent[V]; // This array is filled by BFS and to + // store path + + int max_flow = 0; // There is no flow initially + + // Augment the flow while there is path from source to + // sink + while (bfs(rGraph, s, t, parent)) { + // Find minimum residual capacity of the edges along + // the path filled by BFS. Or we can say find the + // maximum flow through the path found. + int path_flow = INT_MAX; + for (v = t; v != s; v = parent[v]) { + u = parent[v]; + path_flow = min(path_flow, rGraph[u][v]); + } + + // update residual capacities of the edges and + // reverse edges along the path + for (v = t; v != s; v = parent[v]) { + u = parent[v]; + rGraph[u][v] -= path_flow; + rGraph[v][u] += path_flow; + } + + // Add path flow to overall flow + max_flow += path_flow; + } + + // Return the overall flow + return max_flow; +} + +// Driver program to test above functions +int main() +{ + // Let us create a graph shown in the above example + int graph[V][V] + = { { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 }, + { 0, 4, 0, 0, 14, 0 }, { 0, 0, 9, 0, 0, 20 }, + { 0, 0, 0, 7, 0, 4 }, { 0, 0, 0, 0, 0, 0 } }; + + cout << "The maximum possible flow is " + << fordFulkerson(graph, 0, 5); + + return 0; +} + +/*Sample Output: +The maximum possible flow is 23. */ diff --git a/Graphs/Graphs_adjacency_list_implementation.cpp b/Graphs/graphs_adjacency_list.cpp similarity index 96% rename from Graphs/Graphs_adjacency_list_implementation.cpp rename to Graphs/graphs_adjacency_list.cpp index 2fefe15c..216b5f4f 100644 --- a/Graphs/Graphs_adjacency_list_implementation.cpp +++ b/Graphs/graphs_adjacency_list.cpp @@ -1,43 +1,43 @@ -// Graphs Adjacency List implementation -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; - -class Graph{ -public: - int V; - list *L; // a pointer to an array of linkedlist - Graph(int v){ - V = v; - L = new list[V]; // Array of LL - // there is a pointer to an array whose size is v and every object is list of integer - } - void addEdge(int u, int v, bool bidir = true){ - L[u].push_back(v); - if(bidir){ - L[v].push_back(u); - } - } - void printAdjacencyList(){ - for(int i = 0; i < V; i++){ - cout << i << "-> "; - for(auto vertex : L[i]){ - cout << vertex << ", "; - } - cout << endl; - } - } -}; -int main(){ - //graph has 5 vertices numbered from 0-4 - Graph g(5); - g.addEdge(0, 1); - g.addEdge(0, 4); - g.addEdge(1, 4); - g.addEdge(1, 3); - g.addEdge(1, 2); - g.addEdge(2, 3); - g.addEdge(4, 3); - g.printAdjacencyList(); - return 0; +// Graphs Adjacency List implementation +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; + +class Graph{ +public: + int V; + list *L; // a pointer to an array of linkedlist + Graph(int v){ + V = v; + L = new list[V]; // Array of LL + // there is a pointer to an array whose size is v and every object is list of integer + } + void addEdge(int u, int v, bool bidir = true){ + L[u].push_back(v); + if(bidir){ + L[v].push_back(u); + } + } + void printAdjacencyList(){ + for(int i = 0; i < V; i++){ + cout << i << "-> "; + for(auto vertex : L[i]){ + cout << vertex << ", "; + } + cout << endl; + } + } +}; +int main(){ + //graph has 5 vertices numbered from 0-4 + Graph g(5); + g.addEdge(0, 1); + g.addEdge(0, 4); + g.addEdge(1, 4); + g.addEdge(1, 3); + g.addEdge(1, 2); + g.addEdge(2, 3); + g.addEdge(4, 3); + g.printAdjacencyList(); + return 0; } \ No newline at end of file diff --git a/Graphs/Graphs_adjacency_list_implementation_generic.cpp b/Graphs/graphs_adjacency_list_generic.cpp similarity index 96% rename from Graphs/Graphs_adjacency_list_implementation_generic.cpp rename to Graphs/graphs_adjacency_list_generic.cpp index 1ba71bfe..2dd0efd2 100644 --- a/Graphs/Graphs_adjacency_list_implementation_generic.cpp +++ b/Graphs/graphs_adjacency_list_generic.cpp @@ -1,40 +1,40 @@ -// Graphs Adjacency List implementation for Generic Data -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; - -template -class Graph{ - map > adjList; - public: - Graph(){ - - } - void addEdge(T u, T v, bool bidir = true){ - adjList[u].push_back(v); - if(bidir){ - adjList[v].push_back(u); - } - } - void printAdjList(){ - for(auto obj : adjList){ - cout << obj.first << "->"; - for(auto element : obj.second){ - cout << element << ","; - } - cout << endl; - } - } -}; -int main(){ - Graph g; - g.addEdge("Ashish", "Asif", false); - g.addEdge("Ashish", "Abhisek", false); - g.addEdge("Ashish", "Amir", false); - g.addEdge("Abhisek", "Asif", true); - g.addEdge("Abhisek", "Anvesh", true); - g.addEdge("Anvesh", "Sai", false); - g.addEdge("Sai", "Abhisek", false); - g.printAdjList(); - return 0; +// Graphs Adjacency List implementation for Generic Data +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; + +template +class Graph{ + map > adjList; + public: + Graph(){ + + } + void addEdge(T u, T v, bool bidir = true){ + adjList[u].push_back(v); + if(bidir){ + adjList[v].push_back(u); + } + } + void printAdjList(){ + for(auto obj : adjList){ + cout << obj.first << "->"; + for(auto element : obj.second){ + cout << element << ","; + } + cout << endl; + } + } +}; +int main(){ + Graph g; + g.addEdge("Ashish", "Asif", false); + g.addEdge("Ashish", "Abhisek", false); + g.addEdge("Ashish", "Amir", false); + g.addEdge("Abhisek", "Asif", true); + g.addEdge("Abhisek", "Anvesh", true); + g.addEdge("Anvesh", "Sai", false); + g.addEdge("Sai", "Abhisek", false); + g.printAdjList(); + return 0; } \ No newline at end of file diff --git a/Graphs/graphs_bfs.go b/Graphs/graphs_bfs.go new file mode 100644 index 00000000..a48bab3b --- /dev/null +++ b/Graphs/graphs_bfs.go @@ -0,0 +1,91 @@ +// Breadth First Search +/* + In this implementation, we define a Graph struct that represents a graph with a given number of vertices, + edges, and a visited array to keep track of visited vertices. We also define two methods - addEdge to + add an edge to the graph and BFS to perform the BFS algorithm. + + In the BFS method, we start by creating a queue to store the vertices to visit. We mark the start node + as visited and enqueue it. Then, while the queue is not empty, we dequeue a vertex from the queue, + print it out, and get all its adjacent vertices. For each adjacent vertex, if it hasn't been visited, + we mark it as visited and enqueue it. This continues until all reachable vertices have been visited. + + In the main function, we create a new Graph with 5 vertices and add edges to it. We then perform + BFS starting from vertex 2, and print out the visited vertices. The output of this program will be: + BFS starting from vertex 2: + 2 0 3 1 + + The time complexity of BFS (Breadth-First Search) algorithm is O(V + E), where V is the number of + vertices and E is the number of edges in the graph. This is because BFS traverses all the vertices + and edges of the graph exactly once, and the time taken to visit each vertex and edge is constant. + Therefore, the time complexity of BFS is proportional to the size of the graph. + + The space complexity of BFS is O(|V|), where |V| is the number of vertices in the graph. + This is because in the worst case scenario, we would need to store all vertices in the queue + before we finish traversing the graph. +*/ +package main + +import "fmt" + +// Define a Graph struct to represent a graph +type Graph struct { + vertices int // Number of vertices in the graph + edges [][]int // Adjacency list to store edges + visited []bool // Track if a vertex is visited or not +} + +// Function to add an edge to the graph +func (g *Graph) addEdge(u, v int) { + // Add edge from u to v + g.edges[u] = append(g.edges[u], v) + // Add edge from v to u + g.edges[v] = append(g.edges[v], u) +} + +// Function to perform Breadth First Search +func BFS(g *Graph, start int) { + // Create a queue for BFS + queue := []int{} + // Mark the start node as visited and enqueue it + g.visited[start] = true + queue = append(queue, start) + + for len(queue) != 0 { + // Dequeue a vertex from the queue + currVertex := queue[0] + queue = queue[1:] + fmt.Printf("%d ", currVertex) + + // Get all adjacent vertices of the dequeued vertex currVertex + // If an adjacent vertex has not been visited, mark it as visited and enqueue it + for _, adjVertex := range g.edges[currVertex] { + if !g.visited[adjVertex] { + g.visited[adjVertex] = true + queue = append(queue, adjVertex) + } + } + } +} + +func main() { + // Create a new graph with 5 vertices + g := Graph{ + vertices: 5, + edges: make([][]int, 5), + // Initially all vertices are unvisited + visited: make([]bool, 5), + } + + // Add edges to the graph + g.addEdge(0, 1) + g.addEdge(0, 2) + g.addEdge(1, 2) + g.addEdge(2, 0) + g.addEdge(2, 3) + g.addEdge(3, 3) + + // Perform BFS starting from vertex 2 + fmt.Println("BFS starting from vertex 2:") + BFS(&g, 2) +} + diff --git a/Graphs/graphs_dfs.go b/Graphs/graphs_dfs.go new file mode 100644 index 00000000..48e7adf0 --- /dev/null +++ b/Graphs/graphs_dfs.go @@ -0,0 +1,73 @@ +/* + This implementation represents a graph as a collection of nodes, where each node has a value and a list of + neighboring nodes. The DFS algorithm is implemented recursively by starting from the given node, marking + it as visited, and printing its value. Then, the algorithm recursively visits each of the neighboring + nodes that have not been visited yet. The time complexity of DFS is O(V + E), where V is the number of + vertices (nodes) in the graph and E is the number of edges between the vertices. + + Sample Input : graph = { + 'A': ['B', 'C'], + 'B': ['D', 'E'], + 'C': ['F'], + 'D': [], + 'E': ['F'], + 'F': [] + } + Output : DFS Traversal: A B D E F C + Here, we have a graph represented as an adjacency list, with nodes A, B, C, D, E, and F and their respective neighbors. + The DFS traversal starts at node A and visits each node in the graph, outputting the final order in which the nodes were visited. + + The time complexity of the DFS algorithm is O(V + E), where V is the number of vertices (nodes) and E is the number of + edges in the graph. This is because the algorithm visits every vertex and every edge once. + + The space complexity of DFS depends on the maximum depth of the recursion stack. In the worst case, where the tree or + graph is completely unbalanced, the recursion stack can reach a depth of O(n), where n is the number of nodes in the graph. + Therefore, the space complexity of DFS is O(n). + +*/ +package main + +import "fmt" + +// Node represents a single node in the graph +type Node struct { + value int + visited bool + neighbors []*Node +} + +// DFS traverses the graph starting from the given node using Depth First Search +func DFS(node *Node) { + // Mark the current node as visited + node.visited = true + + // Print the value of the current node + fmt.Printf("%d ", node.value) + + // Visit each of the neighboring nodes + for _, neighbor := range node.neighbors { + // If the neighbor has not been visited yet, visit it recursively + if !neighbor.visited { + DFS(neighbor) + } + } +} + +func main() { + // Create the nodes of the graph + node1 := &Node{value: 1} + node2 := &Node{value: 2} + node3 := &Node{value: 3} + node4 := &Node{value: 4} + node5 := &Node{value: 5} + + // Add the neighbors for each node + node1.neighbors = []*Node{node2, node3} + node2.neighbors = []*Node{node1, node4, node5} + node3.neighbors = []*Node{node1, node5} + node4.neighbors = []*Node{node2} + node5.neighbors = []*Node{node2, node3} + + // Start the DFS traversal from node1 + DFS(node1) +} diff --git a/Graphs/kruskals_algorithm.java b/Graphs/kruskals_algorithm.java new file mode 100644 index 00000000..397acfd5 --- /dev/null +++ b/Graphs/kruskals_algorithm.java @@ -0,0 +1,162 @@ +/* +What is a minimum spanning tree? +A minimum spanning tree (MST) or minimum weight spanning tree for a weighted, connected, undirected graph is a spanning tree with a weight less than or equal to the weight of every other spanning tree. + +What is Kruskal’s algorithm? +In Kruskal’s algorithm, sort all edges of the given graph in increasing order. Then it keeps on adding new edges and nodes in the MST if the newly added edge does not form a cycle. It picks the minimum weighted edge at first at the maximum weighted edge at last. Thus we can say that it makes a locally optimal choice in each step in order to find the optimal solution. + +How Kruskal's algorithm works +It falls under a class of algorithms called greedy algorithms that find the local optimum in the hopes of finding a global optimum. We start from the edges with the lowest weight and keep adding edges until we reach our goal. +The steps for implementing Kruskal's algorithm are as follows: +1. Sort all the edges from low weight to high +2. Take the edge with the lowest weight and add it to the spanning tree. If adding the edge created a cycle, then reject this edge. +3. Keep adding edges until we reach all vertices. + +Time Complexity: O(E * logE) or O(E * logV) +Auxiliary Space: O(V + E), where V is the number of vertices and E is the number of edges in the graph. +*/ + +// Java program for Kruskal's algorithm + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class KruskalsMST { + + // Defines edge structure + static class Edge { + int src, dest, weight; + + public Edge(int src, int dest, int weight) + { + this.src = src; + this.dest = dest; + this.weight = weight; + } + } + + // Defines subset element structure + static class Subset { + int parent, rank; + + public Subset(int parent, int rank) + { + this.parent = parent; + this.rank = rank; + } + } + + // Starting point of program execution + public static void main(String[] args) + { + int V = 4; + List graphEdges = new ArrayList( + List.of(new Edge(0, 1, 10), new Edge(0, 2, 6), + new Edge(0, 3, 5), new Edge(1, 3, 15), + new Edge(2, 3, 4))); + + // Sort the edges in non-decreasing order + // (increasing with repetition allowed) + graphEdges.sort(new Comparator() { + @Override public int compare(Edge o1, Edge o2) + { + return o1.weight - o2.weight; + } + }); + + kruskals(V, graphEdges); + } + + // Function to find the MST + private static void kruskals(int V, List edges) + { + int j = 0; + int noOfEdges = 0; + + // Allocate memory for creating V subsets + Subset subsets[] = new Subset[V]; + + // Allocate memory for results + Edge results[] = new Edge[V]; + + // Create V subsets with single elements + for (int i = 0; i < V; i++) { + subsets[i] = new Subset(i, 0); + } + + // Number of edges to be taken is equal to V-1 + while (noOfEdges < V - 1) { + + // Pick the smallest edge. And increment + // the index for next iteration + Edge nextEdge = edges.get(j); + int x = findRoot(subsets, nextEdge.src); + int y = findRoot(subsets, nextEdge.dest); + + // If including this edge doesn't cause cycle, + // include it in result and increment the index + // of result for next edge + if (x != y) { + results[noOfEdges] = nextEdge; + union(subsets, x, y); + noOfEdges++; + } + + j++; + } + + // Print the contents of result[] to display the + // built MST + System.out.println( + "Following are the edges of the constructed MST:"); + int minCost = 0; + for (int i = 0; i < noOfEdges; i++) { + System.out.println(results[i].src + " -- " + + results[i].dest + " == " + + results[i].weight); + minCost += results[i].weight; + } + System.out.println("Total cost of MST: " + minCost); + } + + // Function to unite two disjoint sets + private static void union(Subset[] subsets, int x, + int y) + { + int rootX = findRoot(subsets, x); + int rootY = findRoot(subsets, y); + + if (subsets[rootY].rank < subsets[rootX].rank) { + subsets[rootY].parent = rootX; + } + else if (subsets[rootX].rank + < subsets[rootY].rank) { + subsets[rootX].parent = rootY; + } + else { + subsets[rootY].parent = rootX; + subsets[rootX].rank++; + } + } + + // Function to find parent of a set + private static int findRoot(Subset[] subsets, int i) + { + if (subsets[i].parent == i) + return subsets[i].parent; + + subsets[i].parent + = findRoot(subsets, subsets[i].parent); + return subsets[i].parent; + } +} + +/* Output +Following are the edges in the constructed MST +2 -- 3 == 4 +0 -- 3 == 5 +0 -- 1 == 10 +Minimum Cost Spanning Tree: 19 +*/ + diff --git a/Graphs/kruskals_algorithm.py b/Graphs/kruskals_algorithm.py new file mode 100644 index 00000000..b6b155ec --- /dev/null +++ b/Graphs/kruskals_algorithm.py @@ -0,0 +1,68 @@ +#Question: Implement Kruskal's algorithm in Python to find the minimum spanning tree of a given graph. +#Intuition: +# Kruskal's algorithm is a greedy algorithm that aims to find the minimum spanning tree (MST) of a connected, weighted graph. +# The MST is a subset of the graph's edges that connects all the vertices with the minimum total weight. +# The algorithm works by iteratively selecting the edges in ascending order of their weights, while ensuring that adding an edge to the growing MST does not create a cycle. +#It uses a disjoint set data structure, typically implemented with the Union-Find algorithm, to efficiently keep track of the connected components and +# detect cycles. + +#Implementing Kruskal's algorithm with the help of a Union-Find data structure allows for efficient detection of cycles and union operations, +#resulting in a time complexity of O(E log E), where E is the number of edges in the graph. + +# Initialize a Union-Find data structure to keep track of the connected components. +class UnionFind: + def __init__(self, n): + # Initialize the parent and rank lists + self.parent = list(range(n)) + self.rank = [0] * n + + def find(self, x): + # Find the root of the set to which x belongs + if self.parent[x] != x: + self.parent[x] = self.find(self.parent[x]) # Path compression + return self.parent[x] + + def union(self, x, y): + # Perform the union of two sets + root_x = self.find(x) + root_y = self.find(y) + + if root_x != root_y: + if self.rank[root_x] < self.rank[root_y]: + self.parent[root_x] = root_y + elif self.rank[root_x] > self.rank[root_y]: + self.parent[root_y] = root_x + else: + self.parent[root_y] = root_x + self.rank[root_x] += 1 + +# Define the Kruskal function +def kruskal(graph): + edges = [] + for u in range(len(graph)): + for v, weight in graph[u]: + edges.append((weight, u, v)) + + edges.sort() + # Create an empty list to store the MST + mst = [] + uf = UnionFind(len(graph)) + + for weight, u, v in edges: + if uf.find(u) != uf.find(v): + uf.union(u, v) + mst.append((u, v, weight)) + + return mst + +# Example usage: Create a list of all the edges in the graph, along with their weights. +graph = [ + [(1, 1), (2, 2)], # Node 0 + [(0, 1), (2, 3), (3, 4)], # Node 1 + [(0, 2), (1, 3), (3, 5)], # Node 2 + [(1, 4), (2, 5)] # Node 3 +] +#Sort the edges in ascending order based on their weights. +mst = kruskal(graph) +for u, v, weight in mst: + print(f"Edge ({u}, {v}) with weight {weight}") diff --git a/Graphs/remove_island.go b/Graphs/remove_island.go new file mode 100644 index 00000000..e2ded215 --- /dev/null +++ b/Graphs/remove_island.go @@ -0,0 +1,204 @@ +/* + Remoev island that are not connected to Border + + Sample Input: + + 1 0 0 0 0 0 + 0 1 0 1 0 1 + 0 0 1 0 1 1 + 1 1 0 0 1 0 + 1 0 1 1 0 0 + + Output: + 1 0 0 0 0 0 + 0 0 0 0 0 1 + 0 0 0 0 1 1 + 1 1 0 0 1 0 + 1 0 1 1 0 0 + + Explanation: + The provided code snippet is an implementation of the "Remove Islands" algorithm. This algorithm aims to identify and remove + islands in a binary matrix. An island is a connected region of 1s surrounded by 0s. The algorithm marks islands connected + to the border of the matrix as non-islands and returns the modified matrix. + + Let's break down the code snippet and explain each part: + + 1. Initialization: + + onesConnectedToBorder := make([][]bool, len(matrix)) + for i := range matrix { + onesConnectedToBorder[i] = make([]bool, len(matrix[0])) + } + + This part initializes a 2D boolean array `onesConnectedToBorder`, which has the same dimensions as the input matrix. It is used to mark cells that are connected to the border. + + 2. Marking Ones Connected to Border: + + for row := 0; row < len(matrix); row++ { + for col := 0; col < len(matrix[row]); col++ { + rowIsBorder := row == 0 || row == len(matrix)-1 + colIsBorder := col == 0 || col == len(matrix[row])-1 + isBorder := rowIsBorder || colIsBorder + if !isBorder { + continue + } + if matrix[row][col] != 1 { + continue + } + findOnesConnectedToBorder(matrix, row, col, onesConnectedToBorder) + } + } + + This part iterates through the matrix and checks if each cell is on the border. If a cell is on the border and contains a 1, it calls the `findOnesConnectedToBorder` function to mark the connected 1s using a depth-first search approach. The connected 1s are marked in the `onesConnectedToBorder` array. + + 3. Removing Non-Border Islands: + + for row := 0; row < len(matrix)-1; row++ { + for col := 0; col < len(matrix[row])-1; col++ { + if onesConnectedToBorder[row][col] { + continue + } + matrix[row][col] = 0 + } + } + + This part iterates through the matrix again, excluding the border cells. If a cell is not marked as connected to the border (an island cell), it is set to 0, effectively removing the island. + + 4. Utility Functions: + The code also includes two utility functions: + - `findOnesConnectedToBorder`: This function performs a depth-first search (DFS) to mark all the 1s connected to a border cell. It uses a stack to keep track of the cells to visit next. + - `getNeighbors`: This function returns the valid neighboring cells (up, down, left, right) for a given cell in the matrix. + + Finally, the modified matrix is returned as the result. + + O(wh) time | O(wh) space - where w and h are the width and height of the input matrix + + Note: The code snippet provided assumes that the matrix is a 2D integer slice (`[][]int`) and uses Go syntax.. +*/ +package main + +import "fmt" + +// Function to remove islands in a binary matrix +func RemoveIslands(matrix [][]int) [][]int { + // Create a boolean matrix to track if each cell is connected to the border + onesConnectedToBorder := make([][]bool, len(matrix)) + for i := range matrix { + onesConnectedToBorder[i] = make([]bool, len(matrix[0])) + } + + // Mark 1s connected to the border + for row := 0; row < len(matrix); row++ { + for col := 0; col < len(matrix[row]); col++ { + rowIsBorder := row == 0 || row == len(matrix)-1 + colIsBorder := col == 0 || col == len(matrix[row])-1 + isBorder := rowIsBorder || colIsBorder + + if !isBorder { + continue // Skip if not a border cell + } + + if matrix[row][col] != 1 { + continue // Skip if not a 1 + } + + findOnesConnectedToBorder(matrix, row, col, onesConnectedToBorder) + } + } + + // Remove non-border islands + for row := 0; row < len(matrix)-1; row++ { + for col := 0; col < len(matrix[row])-1; col++ { + if onesConnectedToBorder[row][col] { + continue // Skip if connected to the border + } + + matrix[row][col] = 0 // Set non-border island to 0 + } + } + + return matrix +} + +// Function to perform DFS and mark 1s connected to the border +func findOnesConnectedToBorder(matrix [][]int, startRow, startCol int, onesConnectedToBorder [][]bool) { + stack := [][]int{{startRow, startCol}} + var currentPosition []int + + for len(stack) > 0 { + currentPosition, stack = stack[len(stack)-1], stack[:len(stack)-1] + currentRow, currentCol := currentPosition[0], currentPosition[1] + alreadyVisited := onesConnectedToBorder[currentRow][currentCol] + + if alreadyVisited { + continue // Skip if already visited + } + + onesConnectedToBorder[currentRow][currentCol] = true // Mark cell as connected to the border + + neighbors := getNeighbors(matrix, currentRow, currentCol) + for _, neighbor := range neighbors { + row, col := neighbor[0], neighbor[1] + if matrix[row][col] != 1 { + continue // Skip if not a 1 + } + stack = append(stack, neighbor) // Add neighbor to the stack + } + } +} + +// Function to get valid neighboring cells +func getNeighbors(matrix [][]int, row, col int) [][]int { + neighbors := make([][]int, 0) + numRows := len(matrix) + numCols := len(matrix[row]) + + if row-1 >= 0 { + neighbors = append(neighbors, []int{row - 1, col}) // Top neighbor + } + if row+1 < numRows { + neighbors = append(neighbors, []int{row + 1, col}) // Bottom neighbor + } + if col-1 >= 0 { + neighbors = append(neighbors, []int{row, col - 1}) // Left neighbor + } + if col+1 < numCols { + neighbors = append(neighbors, []int{row, col + 1}) // Right neighbor + } + + return neighbors +} + +func main() { + // Example usage + matrix := [][]int{ + {1, 0, 0, 0, 0, 0}, + {0, 1, 0, 1, 0, 1}, + {0, 0, 1, 0, 1, 1}, + {1, 1, 0, 0, 1, 0}, + {1, 0, 1, 1, 0, 0}, + } + + fmt.Println("Original Matrix:") + printMatrix(matrix) + + updatedMatrix := RemoveIslands(matrix) + + fmt.Println("Updated Matrix:") + printMatrix(updatedMatrix) +} + +// Helper function to print the matrix +func printMatrix(matrix [][]int) { + for _, row := range matrix { + for _, val := range row { + fmt.Printf("%d ", val) + } + fmt.Println() + } +} + + + + + diff --git a/Graphs/river_sizes.cpp b/Graphs/river_sizes.cpp new file mode 100644 index 00000000..4171d438 --- /dev/null +++ b/Graphs/river_sizes.cpp @@ -0,0 +1,186 @@ +/* + You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only + 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's + that are either horizontally or vertically adjacent (but not diagonally adjacent). + The number of adjacent 1's forming a river determine its size. + + Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight + horizontal line; it can be L-shaped, for example. + + Write a function that returns an array of the sizes of all rivers represented in the input matrix. + The sizes don't need to be in any particular order. + + Sample Input:[ + [1, 0, 0, 1, 0], + [1, 0, 1, 0, 0], + [0, 0, 1, 0, 1], + [1, 0, 1, 0, 1], + [1, 0, 1, 1, 0], + ] + Output: [1, 2, 2, 2, 5] + + Explanation: + + 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. + + 2. The function then iterates over each cell in the matrix using nested loops. + + 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. + + 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. + + 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. + + 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. + + 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). + + 8. The function enters a loop that continues until there are no more nodes to explore in the queue. + + 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. + + 10. If the current node has already been visited, the code continues to the next iteration. + + 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. + + 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. + + 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. + + 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. + + 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. + + 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. + + 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. + + 18. The function returns the `unvisitedNeighbors` slice. + + The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. +*/ +#include +#include + +using namespace std; + +vector riverSizes(vector>& matrix) { + // Vector to store the sizes of rivers + vector sizes; + + // Create a visited matrix to keep track of visited nodes + vector> visited(matrix.size(), vector(matrix[0].size(), false)); + + // Iterate over each cell in the matrix + for (int i = 0; i < matrix.size(); i++) { + for (int j = 0; j < matrix[i].size(); j++) { + // If the cell has already been visited, continue to the next iteration + if (visited[i][j]) { + continue; + } + + // Explore the river connected to the current cell and update sizes + sizes = traverseNode(i, j, matrix, visited, sizes); + } + } + + return sizes; +} + +vector traverseNode(int i, int j, vector>& matrix, vector>& visited, vector& sizes) { + // Variable to track the size of the current river + int currentRiverSize = 0; + + // Queue to store nodes that need to be visited + vector> nodesToExplore{{i, j}}; + + // Loop until there are no more nodes to explore + while (!nodesToExplore.empty()) { + // Dequeue the first node from the queue and update the current position (i, j) + vector currentNode = nodesToExplore[0]; + nodesToExplore.erase(nodesToExplore.begin()); + i = currentNode[0]; + j = currentNode[1]; + + // If the current node has already been visited, continue to the next iteration + if (visited[i][j]) { + continue; + } + + // Mark the current node as visited + visited[i][j] = true; + + // If the current node is land (0), continue to the next iteration + if (matrix[i][j] == 0) { + continue; + } + + // Increment the size of the current river + currentRiverSize++; + + // Get the unvisited neighboring nodes of the current node + vector> unvisitedNeighbors = getUnvisitedNeighbors(i, j, matrix, visited); + + // Add the unvisited neighbors to the nodesToExplore queue + for (const auto& neighbor : unvisitedNeighbors) { + nodesToExplore.push_back(neighbor); + } + } + + // If the current river size is greater than 0, append it to the sizes vector + if (currentRiverSize > 0) { + sizes.push_back(currentRiverSize); + } + + return sizes; +} + +vector> getUnvisitedNeighbors(int i, int j, vector>& matrix, vector>& visited) { + // Vector to store unvisited neighboring nodes + vector> unvisitedNeighbors; + + // Check the four neighboring cells (up, down, left, right) of the current cell + // and add unvisited neighbors to the unvisitedNeighbors vector + + // Up neighbor + if (i > 0 && !visited[i - 1][j]) { + unvisitedNeighbors.push_back({i - 1, j}); + } + + // Down neighbor + if (i < matrix.size() - 1 && !visited[i + 1][j]) { + unvisitedNeighbors.push_back({i + 1, j}); + } + + // Left neighbor + if (j > 0 && !visited[i][j - 1]) { + unvisitedNeighbors.push_back({i, j - 1}); + } + + // Right neighbor + if (j < matrix[0].size() - 1 && !visited[i][j + 1]) { + unvisitedNeighbors.push_back({i, j + 1}); + } + + return unvisitedNeighbors; +} + +int main() { + // Test the riverSizes function + vector> matrix = { + {1, 0, 0, 1, 0}, + {1, 0, 1, 0, 0}, + {0, 0, 1, 0, 1}, + {1, 0, 1, 0, 1}, + {1, 0, 1, 1, 0} + }; + + vector sizes = riverSizes(matrix); + + // Print the sizes of the rivers + for (const auto& size : sizes) { + cout << size << " "; + } + + return 0; +} diff --git a/Graphs/river_sizes.go b/Graphs/river_sizes.go new file mode 100644 index 00000000..b21baa34 --- /dev/null +++ b/Graphs/river_sizes.go @@ -0,0 +1,166 @@ +/* + + You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only + 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's + that are either horizontally or vertically adjacent (but not diagonally adjacent). + The number of adjacent 1's forming a river determine its size. + + Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight + horizontal line; it can be L-shaped, for example. + + Write a function that returns an array of the sizes of all rivers represented in the input matrix. + The sizes don't need to be in any particular order. + + Sample Input:[ + [1, 0, 0, 1, 0], + [1, 0, 1, 0, 0], + [0, 0, 1, 0, 1], + [1, 0, 1, 0, 1], + [1, 0, 1, 1, 0], + ] + Output: [1, 2, 2, 2, 5] + + Explanation: + + 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. + + 2. The function then iterates over each cell in the matrix using nested loops. + + 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. + + 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. + + 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. + + 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. + + 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). + + 8. The function enters a loop that continues until there are no more nodes to explore in the queue. + + 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. + + 10. If the current node has already been visited, the code continues to the next iteration. + + 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. + + 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. + + 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. + + 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. + + 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. + + 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. + + 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. + + 18. The function returns the `unvisitedNeighbors` slice. + + The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. +*/ +package main + +func RiverSizes(matrix [][]int) []int { + // Slice to store the sizes of rivers + sizes := []int{} + + // Create a visited matrix to keep track of visited nodes + visited := make([][]bool, len(matrix)) + for i := range visited { + visited[i] = make([]bool, len(matrix[i])) + } + + // Iterate over each cell in the matrix + for i := range matrix { + for j := range matrix[i] { + // If the cell has already been visited, continue to the next iteration + if visited[i][j] { + continue + } + + // Explore the river connected to the current cell and update sizes + sizes = traverseNode(i, j, matrix, visited, sizes) + } + } + + return sizes +} + +func traverseNode(i, j int, matrix [][]int, visited [][]bool, sizes []int) []int { + // Variable to track the size of the current river + currentRiverSize := 0 + + // Queue to store nodes that need to be visited + nodesToExplore := [][]int{{i, j}} + + // Loop until there are no more nodes to explore + for len(nodesToExplore) > 0 { + // Dequeue the first node from the queue and update the current position (i, j) + currentNode := nodesToExplore[0] + nodesToExplore = nodesToExplore[1:] + i, j := currentNode[0], currentNode[1] + + // If the current node has already been visited, continue to the next iteration + if visited[i][j] { + continue + } + + // Mark the current node as visited + visited[i][j] = true + + // If the current node is land (0), continue to the next iteration + if matrix[i][j] == 0 { + continue + } + + // Increment the size of the current river + currentRiverSize++ + + // Get the unvisited neighboring nodes of the current node + unvisitedNeighbors := getUnvisitedNeighbors(i, j, matrix, visited) + + // Add the unvisited neighbors to the nodesToExplore queue + for _, neighbor := range unvisitedNeighbors { + nodesToExplore = append(nodesToExplore, neighbor) + } + } + + // If the current river size is greater than 0, append it to the sizes slice + if currentRiverSize > 0 { + sizes = append(sizes, currentRiverSize) + } + + return sizes +} + +func getUnvisitedNeighbors(i, j int, matrix [][]int, visited [][]bool) [][]int { + // Slice to store unvisited neighboring nodes + unvisitedNeighbors := [][]int{} + + // Check the four neighboring cells (up, down, left, right) of the current cell + // and add unvisited neighbors to the unvisitedNeighbors slice + + // Up neighbor + if i > 0 && !visited[i-1][j] { + unvisitedNeighbors = append(unvisitedNeighbors, []int{i - 1, j}) + } + + // Down neighbor + if i < len(matrix)-1 && !visited[i+1][j] { + unvisitedNeighbors = append(unvisitedNeighbors, []int{i + 1, j}) + } + + // Left neighbor + if j > 0 && !visited[i][j-1] { + unvisitedNeighbors = append(unvisitedNeighbors, []int{i, j - 1}) + } + + // Right neighbor + if j < len(matrix[0])-1 && !visited[i][j+1] { + unvisitedNeighbors = append(unvisitedNeighbors, []int{i, j + 1}) + } + + return unvisitedNeighbors +} diff --git a/Graphs/river_sizes.java b/Graphs/river_sizes.java new file mode 100644 index 00000000..92d15dc7 --- /dev/null +++ b/Graphs/river_sizes.java @@ -0,0 +1,176 @@ +/* + + You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only + 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's + that are either horizontally or vertically adjacent (but not diagonally adjacent). + The number of adjacent 1's forming a river determine its size. + + Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight + horizontal line; it can be L-shaped, for example. + + Write a function that returns an array of the sizes of all rivers represented in the input matrix. + The sizes don't need to be in any particular order. + + Sample Input:[ + [1, 0, 0, 1, 0], + [1, 0, 1, 0, 0], + [0, 0, 1, 0, 1], + [1, 0, 1, 0, 1], + [1, 0, 1, 1, 0], + ] + Output: [1, 2, 2, 2, 5] + + Explanation: + + 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. + + 2. The function then iterates over each cell in the matrix using nested loops. + + 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. + + 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. + + 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. + + 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. + + 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). + + 8. The function enters a loop that continues until there are no more nodes to explore in the queue. + + 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. + + 10. If the current node has already been visited, the code continues to the next iteration. + + 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. + + 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. + + 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. + + 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. + + 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. + + 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. + + 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. + + 18. The function returns the `unvisitedNeighbors` slice. + + The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. +*/ +import java.util.ArrayList; +import java.util.List; + +public class RiverSizes { + + public static List riverSizes(int[][] matrix) { + List sizes = new ArrayList<>(); + int numRows = matrix.length; + int numCols = matrix[0].length; + boolean[][] visited = new boolean[numRows][numCols]; + + // Iterate over each cell in the matrix + for (int i = 0; i < numRows; i++) { + for (int j = 0; j < numCols; j++) { + // If the cell has already been visited, continue to the next iteration + if (visited[i][j]) { + continue; + } + + // Explore the river connected to the current cell and update sizes + sizes = traverseNode(i, j, matrix, visited, sizes); + } + } + + return sizes; + } + + private static List traverseNode(int i, int j, int[][] matrix, boolean[][] visited, List sizes) { + int currentRiverSize = 0; + List nodesToExplore = new ArrayList<>(); + nodesToExplore.add(new int[]{i, j}); + + while (!nodesToExplore.isEmpty()) { + int[] currentNode = nodesToExplore.remove(0); + i = currentNode[0]; + j = currentNode[1]; + + // If the current node has already been visited, continue to the next iteration + if (visited[i][j]) { + continue; + } + + // Mark the current node as visited + visited[i][j] = true; + + // If the current node is land (0), continue to the next iteration + if (matrix[i][j] == 0) { + continue; + } + + // Increment the size of the current river + currentRiverSize++; + + // Get the unvisited neighboring nodes of the current node + List unvisitedNeighbors = getUnvisitedNeighbors(i, j, matrix, visited); + + // Add the unvisited neighbors to the nodesToExplore list + nodesToExplore.addAll(unvisitedNeighbors); + } + + // If the current river size is greater than 0, add it to the sizes list + if (currentRiverSize > 0) { + sizes.add(currentRiverSize); + } + + return sizes; + } + + private static List getUnvisitedNeighbors(int i, int j, int[][] matrix, boolean[][] visited) { + List unvisitedNeighbors = new ArrayList<>(); + + // Check the four neighboring cells (up, down, left, right) of the current cell + // and add unvisited neighbors to the unvisitedNeighbors list + + // Up neighbor + if (i > 0 && !visited[i - 1][j]) { + unvisitedNeighbors.add(new int[]{i - 1, j}); + } + + // Down neighbor + if (i < matrix.length - 1 && !visited[i + 1][j]) { + unvisitedNeighbors.add(new int[]{i + 1, j}); + } + + // Left neighbor + if (j > 0 && !visited[i][j - 1]) { + unvisitedNeighbors.add(new int[]{i, j - 1}); + } + + // Right neighbor + if (j < matrix[0].length - 1 && !visited[i][j + 1]) { + unvisitedNeighbors.add(new int[]{i, j + 1}); + } + + return unvisitedNeighbors; + } + + public static void main(String[] args) { + int[][] matrix = { + {1, 0, 0, 1, 0}, + {1, 0, 1, 0, 0}, + {0, 0, 1, 0, 1}, + {1, 0, 1, 0, 1}, + {1, 0, 1, 1, 0} + }; + + List sizes = riverSizes(matrix); + + // Printing the sizes of rivers + for (int size : sizes) { + System.out.print(size + " "); + } + } +} diff --git a/Graphs/river_sizes.js b/Graphs/river_sizes.js new file mode 100644 index 00000000..18ea1f1d --- /dev/null +++ b/Graphs/river_sizes.js @@ -0,0 +1,172 @@ +/* + You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only + 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's + that are either horizontally or vertically adjacent (but not diagonally adjacent). + The number of adjacent 1's forming a river determine its size. + + Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight + horizontal line; it can be L-shaped, for example. + + Write a function that returns an array of the sizes of all rivers represented in the input matrix. + The sizes don't need to be in any particular order. + + Sample Input:[ + [1, 0, 0, 1, 0], + [1, 0, 1, 0, 0], + [0, 0, 1, 0, 1], + [1, 0, 1, 0, 1], + [1, 0, 1, 1, 0], + ] + Output: [1, 2, 2, 2, 5] + + Explanation: + + 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. + + 2. The function then iterates over each cell in the matrix using nested loops. + + 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. + + 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. + + 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. + + 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. + + 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). + + 8. The function enters a loop that continues until there are no more nodes to explore in the queue. + + 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. + + 10. If the current node has already been visited, the code continues to the next iteration. + + 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. + + 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. + + 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. + + 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. + + 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. + + 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. + + 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. + + 18. The function returns the `unvisitedNeighbors` slice. + + The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. +*/ +function riverSizes(matrix) { + // Array to store the sizes of rivers + const sizes = []; + + // Create a visited matrix to keep track of visited nodes + const visited = Array.from({ length: matrix.length }, () => + new Array(matrix[0].length).fill(false) + ); + + // Iterate over each cell in the matrix + for (let i = 0; i < matrix.length; i++) { + for (let j = 0; j < matrix[i].length; j++) { + // If the cell has already been visited, continue to the next iteration + if (visited[i][j]) { + continue; + } + + // Explore the river connected to the current cell and update sizes + sizes.push(traverseNode(i, j, matrix, visited)); + } + } + + return sizes; +} + +function traverseNode(i, j, matrix, visited) { + // Variable to track the size of the current river + let currentRiverSize = 0; + + // Queue to store nodes that need to be visited + const nodesToExplore = [[i, j]]; + + // Loop until there are no more nodes to explore + while (nodesToExplore.length) { + // Dequeue the first node from the queue and update the current position (i, j) + const [currentI, currentJ] = nodesToExplore.shift(); + + // If the current node has already been visited, continue to the next iteration + if (visited[currentI][currentJ]) { + continue; + } + + // Mark the current node as visited + visited[currentI][currentJ] = true; + + // If the current node is land (0), continue to the next iteration + if (matrix[currentI][currentJ] === 0) { + continue; + } + + // Increment the size of the current river + currentRiverSize++; + + // Get the unvisited neighboring nodes of the current node + const unvisitedNeighbors = getUnvisitedNeighbors( + currentI, + currentJ, + matrix, + visited + ); + + // Add the unvisited neighbors to the nodesToExplore queue + nodesToExplore.push(...unvisitedNeighbors); + } + + return currentRiverSize; +} + +function getUnvisitedNeighbors(i, j, matrix, visited) { + // Array to store unvisited neighboring nodes + const unvisitedNeighbors = []; + + // Check the four neighboring cells (up, down, left, right) of the current cell + // and add unvisited neighbors to the unvisitedNeighbors array + + // Up neighbor + if (i > 0 && !visited[i - 1][j]) { + unvisitedNeighbors.push([i - 1, j]); + } + + // Down neighbor + if (i < matrix.length - 1 && !visited[i + 1][j]) { + unvisitedNeighbors.push([i + 1, j]); + } + + // Left neighbor + if (j > 0 && !visited[i][j - 1]) { + unvisitedNeighbors.push([i, j - 1]); + } + + // Right neighbor + if (j < matrix[0].length - 1 && !visited[i][j + 1]) { + unvisitedNeighbors.push([i, j + 1]); + } + + return unvisitedNeighbors; +} + +// Test the riverSizes function +const matrix = [ + [1, 0, 0, 1, 0], + [1, 0, 1, 0, 0], + [0, 0, 1, 0, 1], + [1, 0, 1, 0, 1], + [1, 0, 1, 1, 0], +]; + +const sizes = riverSizes(matrix); + +// Print the sizes of the rivers +console.log(sizes); diff --git a/Graphs/river_sizes.py b/Graphs/river_sizes.py new file mode 100644 index 00000000..247c9bfc --- /dev/null +++ b/Graphs/river_sizes.py @@ -0,0 +1,147 @@ +''' + + You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only + 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's + that are either horizontally or vertically adjacent (but not diagonally adjacent). + The number of adjacent 1's forming a river determine its size. + + Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight + horizontal line; it can be L-shaped, for example. + + Write a function that returns an array of the sizes of all rivers represented in the input matrix. + The sizes don't need to be in any particular order. + + Sample Input:[ + [1, 0, 0, 1, 0], + [1, 0, 1, 0, 0], + [0, 0, 1, 0, 1], + [1, 0, 1, 0, 1], + [1, 0, 1, 1, 0], + ] + Output: [1, 2, 2, 2, 5] + + Explanation: + + 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. + + 2. The function then iterates over each cell in the matrix using nested loops. + + 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. + + 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. + + 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. + + 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. + + 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). + + 8. The function enters a loop that continues until there are no more nodes to explore in the queue. + + 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. + + 10. If the current node has already been visited, the code continues to the next iteration. + + 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. + + 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. + + 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. + + 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. + + 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. + + 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. + + 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. + + 18. The function returns the `unvisitedNeighbors` slice. + + The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. + +''' +def riverSizes(matrix): + # List to store the sizes of rivers + sizes = [] + + # Create a visited matrix to keep track of visited nodes + visited = [[False for _ in range(len(matrix[0]))] for _ in range(len(matrix))] + + # Iterate over each cell in the matrix + for i in range(len(matrix)): + for j in range(len(matrix[i])): + # If the cell has already been visited, continue to the next iteration + if visited[i][j]: + continue + + # Explore the river connected to the current cell and update sizes + sizes = traverseNode(i, j, matrix, visited, sizes) + + return sizes + + +def traverseNode(i, j, matrix, visited, sizes): + # Variable to track the size of the current river + currentRiverSize = 0 + + # Queue to store nodes that need to be visited + nodesToExplore = [[i, j]] + + # Loop until there are no more nodes to explore + while nodesToExplore: + # Dequeue the first node from the queue and update the current position (i, j) + currentNode = nodesToExplore.pop(0) + i, j = currentNode[0], currentNode[1] + + # If the current node has already been visited, continue to the next iteration + if visited[i][j]: + continue + + # Mark the current node as visited + visited[i][j] = True + + # If the current node is land (0), continue to the next iteration + if matrix[i][j] == 0: + continue + + # Increment the size of the current river + currentRiverSize += 1 + + # Get the unvisited neighboring nodes of the current node + unvisitedNeighbors = getUnvisitedNeighbors(i, j, matrix, visited) + + # Add the unvisited neighbors to the nodesToExplore queue + nodesToExplore.extend(unvisitedNeighbors) + + # If the current river size is greater than 0, append it to the sizes list + if currentRiverSize > 0: + sizes.append(currentRiverSize) + + return sizes + + +def getUnvisitedNeighbors(i, j, matrix, visited): + # List to store unvisited neighboring nodes + unvisitedNeighbors = [] + + # Check the four neighboring cells (up, down, left, right) of the current cell + # and add unvisited neighbors to the unvisitedNeighbors list + + # Up neighbor + if i > 0 and not visited[i - 1][j]: + unvisitedNeighbors.append([i - 1, j]) + + # Down neighbor + if i < len(matrix) - 1 and not visited[i + 1][j]: + unvisitedNeighbors.append([i + 1, j]) + + # Left neighbor + if j > 0 and not visited[i][j - 1]: + unvisitedNeighbors.append([i, j - 1]) + + # Right neighbor + if j < len(matrix[0]) - 1 and not visited[i][j + 1]: + unvisitedNeighbors.append([i, j + 1]) + + return unvisitedNeighbors diff --git a/Graphs/single_cycle_check.cpp b/Graphs/single_cycle_check.cpp new file mode 100644 index 00000000..72e0e002 --- /dev/null +++ b/Graphs/single_cycle_check.cpp @@ -0,0 +1,94 @@ +/* + You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer + 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. + If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the + last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 + + Write a function that returns a boolean representing whether the jumps in the array form a single + cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every + element in the array is visited exactly once before landing back on the starting index. + + Sample Input: [2, 3, 1, -4, -4, 2] + Output: True + + Explanation: + + The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array + has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and + currIdx to track the current index in the array. + + The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is + greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means + the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns + false. + + Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we + will examine next. + + The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in + the cycle. + + It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take + from the current element. + + The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of + the array. This ensures that the index stays within the valid range of the array. + + Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to + the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. + + Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the + starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. + Otherwise, it returns false. + + Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices + and jumps between elements. + + Time Complexity : O(n) where n is the length of the input array + Space Complexity : O(1) +*/ +#include +#include + +using namespace std; + +// Function to check if the given array has a single cycle +bool hasSingleCycle(vector& array) { + int nextElementVisited = 0; + int currIdx = 0; + + while (nextElementVisited < array.size()) { + // Check if more than one element has been visited and current index is back to the starting index (0) + if (nextElementVisited > 0 && currIdx == 0) { + return false; // Multiple cycles detected, return false + } + + nextElementVisited++; // Increment the count of visited elements + currIdx = getNextIdx(array, currIdx); // Get the index of the next element + } + + return currIdx == 0; // Return true if all elements have been visited in a single cycle +} + +// Function to get the index of the next element in the cycle +int getNextIdx(vector& array, int currIdx) { + int jump = array[currIdx]; // Get the jump value from the current index + int nextIdx = (currIdx + jump) % array.size(); // Calculate the index of the next element + + if (nextIdx >= 0) { + return nextIdx; // Return the next index if it is non-negative + } + + return nextIdx + array.size(); // Adjust the next index if it is negative (wrapped around to the beginning) +} + +int main() { + // Test cases + vector array1 = {2, 3, 1, -4, -4, 2}; + cout << "Array 1: " << (hasSingleCycle(array1) ? "true" : "false") << endl; + + vector array2 = {2, 2, -1}; + cout << "Array 2: " << (hasSingleCycle(array2) ? "true" : "false") << endl; + + return 0; +} diff --git a/Graphs/single_cycle_check.go b/Graphs/single_cycle_check.go new file mode 100644 index 00000000..e38cb304 --- /dev/null +++ b/Graphs/single_cycle_check.go @@ -0,0 +1,91 @@ +/* + You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer + 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. + If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the + last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 + + Write a function that returns a boolean representing whether the jumps in the array form a single + cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every + element in the array is visited exactly once before landing back on the starting index. + + Sample Input: [2, 3, 1, -4, -4, 2] + Output: True + + Explanation: + + The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array + has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and + currIdx to track the current index in the array. + + The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is + greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means + the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns + false. + + Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we + will examine next. + + The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in + the cycle. + + It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take + from the current element. + + The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of + the array. This ensures that the index stays within the valid range of the array. + + Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to + the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. + + Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the + starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. + Otherwise, it returns false. + + Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices + and jumps between elements. + + Time Complexity : O(n) where n is the length of the input array + Space Complexity : O(1) +*/ +package main + +import "fmt" + + +func HasSingleCycle(array []int) bool { + nextElementVisited := 0 + currIdx := 0 + + for nextElementVisited < len(array) { + // Check if more than one element has been visited and current index is back to the starting index (0) + if nextElementVisited > 0 && currIdx == 0 { + return false // Multiple cycles detected, return false + } + + nextElementVisited += 1 // Increment the count of visited elements + currIdx = getNextIdx(array, currIdx) // Get the index of the next element + } + + return currIdx == 0 // Return true if all elements have been visited in a single cycle +} + +func getNextIdx(array []int, currIdx int) int { + jump := array[currIdx] // Get the jump value from the current index + nextIdx := (currIdx + jump) % len(array) // Calculate the index of the next element + + if nextIdx >= 0 { + return nextIdx // Return the next index if it is non-negative + } + + return nextIdx + len(array) // Adjust the next index if it is negative (wrapped around to the beginning) +} + + +func main() { + // Test cases + array1 := []int{2, 3, 1, -4, -4, 2} // has a single cycle + fmt.Println("Array 1:", HasSingleCycle(array1)) // Output: true + + array2 := []int{2, 2, -1} // does have a single cycle + fmt.Println("Array 2:", HasSingleCycle(array2)) // Output: true +} \ No newline at end of file diff --git a/Graphs/single_cycle_check.java b/Graphs/single_cycle_check.java new file mode 100644 index 00000000..a53e0771 --- /dev/null +++ b/Graphs/single_cycle_check.java @@ -0,0 +1,91 @@ +/* + You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer + 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. + If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the + last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 + + Write a function that returns a boolean representing whether the jumps in the array form a single + cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every + element in the array is visited exactly once before landing back on the starting index. + + Sample Input: [2, 3, 1, -4, -4, 2] + Output: True + + Explanation: + + The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array + has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and + currIdx to track the current index in the array. + + The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is + greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means + the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns + false. + + Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we + will examine next. + + The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in + the cycle. + + It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take + from the current element. + + The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of + the array. This ensures that the index stays within the valid range of the array. + + Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to + the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. + + Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the + starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. + Otherwise, it returns false. + + Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices + and jumps between elements. + + Time Complexity : O(n) where n is the length of the input array + Space Complexity : O(1) +*/ +import java.util.*; + +public class Main { + // Function to check if the given array has a single cycle + public static boolean hasSingleCycle(int[] array) { + int nextElementVisited = 0; + int currIdx = 0; + + while (nextElementVisited < array.length) { + // Check if more than one element has been visited and current index is back to the starting index (0) + if (nextElementVisited > 0 && currIdx == 0) { + return false; // Multiple cycles detected, return false + } + + nextElementVisited++; // Increment the count of visited elements + currIdx = getNextIdx(array, currIdx); // Get the index of the next element + } + + return currIdx == 0; // Return true if all elements have been visited in a single cycle + } + + // Function to get the index of the next element in the cycle + public static int getNextIdx(int[] array, int currIdx) { + int jump = array[currIdx]; // Get the jump value from the current index + int nextIdx = (currIdx + jump) % array.length; // Calculate the index of the next element + + if (nextIdx >= 0) { + return nextIdx; // Return the next index if it is non-negative + } + + return nextIdx + array.length; // Adjust the next index if it is negative (wrapped around to the beginning) + } + + public static void main(String[] args) { + // Test cases + int[] array1 = {2, 3, 1, -4, -4, 2}; + System.out.println("Array 1: " + (hasSingleCycle(array1) ? "true" : "false")); + + int[] array2 = {2, 2, -1}; + System.out.println("Array 2: " + (hasSingleCycle(array2) ? "true" : "false")); + } +} diff --git a/Graphs/single_cycle_check.js b/Graphs/single_cycle_check.js new file mode 100644 index 00000000..670de1d3 --- /dev/null +++ b/Graphs/single_cycle_check.js @@ -0,0 +1,83 @@ +/* + You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer + 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. + If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the + last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 + + Write a function that returns a boolean representing whether the jumps in the array form a single + cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every + element in the array is visited exactly once before landing back on the starting index. + + Sample Input: [2, 3, 1, -4, -4, 2] + Output: True + + Explanation: + + The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array + has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and + currIdx to track the current index in the array. + + The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is + greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means + the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns + false. + + Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we + will examine next. + + The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in + the cycle. + + It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take + from the current element. + + The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of + the array. This ensures that the index stays within the valid range of the array. + + Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to + the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. + + Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the + starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. + Otherwise, it returns false. + + Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices + and jumps between elements. + + Time Complexity : O(n) where n is the length of the input array + Space Complexity : O(1) +*/ +function hasSingleCycle(array) { + let nextElementVisited = 0; + let currIdx = 0; + + while (nextElementVisited < array.length) { + // Check if more than one element has been visited and current index is back to the starting index (0) + if (nextElementVisited > 0 && currIdx === 0) { + return false; // Multiple cycles detected, return false + } + + nextElementVisited += 1; // Increment the count of visited elements + currIdx = getNextIdx(array, currIdx); // Get the index of the next element + } + + return currIdx === 0; // Return true if all elements have been visited in a single cycle +} + +function getNextIdx(array, currIdx) { + const jump = array[currIdx]; // Get the jump value from the current index + const nextIdx = (currIdx + jump) % array.length; // Calculate the index of the next element + + if (nextIdx >= 0) { + return nextIdx; // Return the next index if it is non-negative + } + + return nextIdx + array.length; // Adjust the next index if it is negative (wrapped around to the beginning) +} + +// Test cases +const array1 = [2, 3, 1, -4, -4, 2]; +console.log("Array 1:", hasSingleCycle(array1)); + +const array2 = [2, 2, -1]; +console.log("Array 2:", hasSingleCycle(array2)); diff --git a/Graphs/single_cycle_check.py b/Graphs/single_cycle_check.py new file mode 100644 index 00000000..444f7fff --- /dev/null +++ b/Graphs/single_cycle_check.py @@ -0,0 +1,81 @@ +''' + You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer + 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. + If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the + last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 + + Write a function that returns a boolean representing whether the jumps in the array form a single + cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every + element in the array is visited exactly once before landing back on the starting index. + + Sample Input: [2, 3, 1, -4, -4, 2] + Output: True + + Explanation: + + The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array + has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and + currIdx to track the current index in the array. + + The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is + greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means + the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns + false. + + Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we + will examine next. + + The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in + the cycle. + + It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take + from the current element. + + The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of + the array. This ensures that the index stays within the valid range of the array. + + Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to + the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. + + Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the + starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. + Otherwise, it returns false. + + Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices + and jumps between elements. + + Time Complexity : O(n) where n is the length of the input array + Space Complexity : O(1) +''' + +def has_single_cycle(array): + next_element_visited = 0 + curr_idx = 0 + + while next_element_visited < len(array): + # Check if more than one element has been visited and current index is back to the starting index (0) + if next_element_visited > 0 and curr_idx == 0: + return False # Multiple cycles detected, return False + + next_element_visited += 1 # Increment the count of visited elements + curr_idx = get_next_idx(array, curr_idx) # Get the index of the next element + + return curr_idx == 0 # Return True if all elements have been visited in a single cycle + + +def get_next_idx(array, curr_idx): + jump = array[curr_idx] # Get the jump value from the current index + next_idx = (curr_idx + jump) % len(array) # Calculate the index of the next element + + if next_idx >= 0: + return next_idx # Return the next index if it is non-negative + + return next_idx + len(array) # Adjust the next index if it is negative (wrapped around to the beginning) + + +# Test cases +array1 = [2, 3, 1, -4, -4, 2] +print("Array 1:", has_single_cycle(array1)) + +array2 = [2, 2, -1] +print("Array 2:", has_single_cycle(array2)) diff --git a/Graphs/Graphs_bfs_snack_ladders.cpp b/Graphs/snack_ladders.cpp similarity index 50% rename from Graphs/Graphs_bfs_snack_ladders.cpp rename to Graphs/snack_ladders.cpp index a6dd988d..b1cf8e99 100644 --- a/Graphs/Graphs_bfs_snack_ladders.cpp +++ b/Graphs/snack_ladders.cpp @@ -1,87 +1,129 @@ -// Graphs Adjacency List implementation for Generic Data -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; - -template -class Graph{ - map > adjList; - public: - Graph(){ - - } - void addEdge(T u, T v, bool bidir = true){ - adjList[u].push_back(v); - if(bidir){ - adjList[v].push_back(u); - } - } - void printAdjList(){ - for(auto obj : adjList){ - cout << obj.first << "->"; - for(auto element : obj.second){ - cout << element << ","; - } - cout << endl; - } - } - int bfs_sssp(int n, int m){ - queue q; - map dist; - map parent; - for(auto i : adjList){ - dist[i.first] = INT_MAX; - } - q.push(n); - dist[n] = 0; - parent[n] = n; - while(!q.empty()){ - int node_element = q.front(); - q.pop(); - for(int neighbour : adjList[node_element]){ - if(dist[neighbour] == INT_MAX){ - q.push(neighbour); - dist[neighbour] = dist[node_element] + 1; - parent[neighbour] = node_element; - } - } - } - cout << endl; - int temp = m; - while(temp != n){ - cout << temp << "-->"; - temp = parent[temp]; - } - cout << n << endl; - /* - for(auto i : adjList){ - int node = i.first; - cout << "Distance of " << node << " is " << dist[node] << endl; - } - */ - return dist[m]; - - } -}; -int main(){ - Graph g; - int board[50] = {0}; - board[2] = 13; - board[5] = 2; - board[9] = 18; - board[18] = 11; - board[17] = -13; - board[20] = -14; - board[24] = -8; - board[25] = -10; - board[32] = -2; - board[34] = -22; - for(int u = 0; u <= 36; u++){ - for(int dice = 1; dice <= 6; dice++){ - int v = u + dice + board[u + dice]; - g.addEdge(u, v, false); - } - } - cout << "The shortest distance is " << g.bfs_sssp(1, 36) << endl; - return 0; +/* +You are given an n x n integer matrix board where the cells are labeled from 1 to n2 in a Boustrophedon style starting from the bottom left of the board (i.e. board[n - 1][0]) and alternating direction each row. + +You start on square 1 of the board. In each move, starting from square curr, do the following: + +Choose a destination square next with a label in the range [curr + 1, min(curr + 6, n2)]. +This choice simulates the result of a standard 6-sided die roll: i.e., there are always at most 6 destinations, regardless of the size of the board. +If next has a snake or ladder, you must move to the destination of that snake or ladder. Otherwise, you move to next. +The game ends when you reach the square n2. +A board square on row r and column c has a snake or ladder if board[r][c] != -1. The destination of that snake or ladder is board[r][c]. Squares 1 and n2 do not have a snake or ladder. + +Note that you only take a snake or ladder at most once per move. If the destination to a snake or ladder is the start of another snake or ladder, you do not follow the subsequent snake or ladder. + +For example, suppose the board is [[-1,4],[-1,3]], and on the first move, your destination square is 2. You follow the ladder to square 3, but do not follow the subsequent ladder to 4. +Return the least number of moves required to reach the square n2. If it is not possible to reach the square, return -1. + + + +Example 1: + + +Input: board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]] +Output: 4 +Explanation: +In the beginning, you start at square 1 (at row 5, column 0). +You decide to move to square 2 and must take the ladder to square 15. +You then decide to move to square 17 and must take the snake to square 13. +You then decide to move to square 14 and must take the ladder to square 35. +You then decide to move to square 36, ending the game. +This is the lowest possible number of moves to reach the last square, so return 4. +Example 2: + +Input: board = [[-1,-1],[-1,3]] +Output: 1 + + +Constraints: + +n == board.length == board[i].length +2 <= n <= 20 +board[i][j] is either -1 or in the range [1, n2]. +The squares labeled 1 and n2 do not have any ladders or snakes. +*/ + +#include +using namespace std; + +template +class Graph{ + map > adjList; + public: + Graph(){ + + } + void addEdge(T u, T v, bool bidir = true){ + adjList[u].push_back(v); + if(bidir){ + adjList[v].push_back(u); + } + } + void printAdjList(){ + for(auto obj : adjList){ + cout << obj.first << "->"; + for(auto element : obj.second){ + cout << element << ","; + } + cout << endl; + } + } + int bfs_sssp(int n, int m){ + queue q; + map dist; + map parent; + for(auto i : adjList){ + dist[i.first] = INT_MAX; + } + q.push(n); + dist[n] = 0; + parent[n] = n; + while(!q.empty()){ + int node_element = q.front(); + q.pop(); + for(int neighbour : adjList[node_element]){ + if(dist[neighbour] == INT_MAX){ + q.push(neighbour); + dist[neighbour] = dist[node_element] + 1; + parent[neighbour] = node_element; + } + } + } + cout << endl; + int temp = m; + while(temp != n){ + cout << temp << "-->"; + temp = parent[temp]; + } + cout << n << endl; + /* + for(auto i : adjList){ + int node = i.first; + cout << "Distance of " << node << " is " << dist[node] << endl; + } + */ + return dist[m]; + + } +}; +int main(){ + Graph g; + int board[50] = {0}; + board[2] = 13; + board[5] = 2; + board[9] = 18; + board[18] = 11; + board[17] = -13; + board[20] = -14; + board[24] = -8; + board[25] = -10; + board[32] = -2; + board[34] = -22; + for(int u = 0; u <= 36; u++){ + for(int dice = 1; dice <= 6; dice++){ + int v = u + dice + board[u + dice]; + g.addEdge(u, v, false); + } + } + cout << "The shortest distance is " << g.bfs_sssp(1, 36) << endl; + return 0; } \ No newline at end of file diff --git a/Graphs/snack_ladders.js b/Graphs/snack_ladders.js new file mode 100644 index 00000000..8b100dd1 --- /dev/null +++ b/Graphs/snack_ladders.js @@ -0,0 +1,133 @@ +// Define the number of squares on the board + +const BOARD_SIZE = 100; + +// Define the start and end points of the snakes and ladders + +const snakesAndLadders = { + + 14: 4, 19: 8, 22: 20, 41: 38, 50: 39, 54: 34, 66: 53, 68: 63, 79: 67, 83: 72, + + 92: 88, 97: 76, 8: 15, 13: 23, 37: 43, 40: 48, 57: 63, 61: 69, 65: 72, 75: 83, + + 78: 87, 80: 92, 88: 94 + +}; + +// Define a function to build the game graph + +function buildGraph() { + + const graph = {}; + + for (let i = 1; i <= BOARD_SIZE; i++) { + + graph[i] = []; + + for (let j = i + 1; j <= i + 6 && j <= BOARD_SIZE; j++) { + + if (snakesAndLadders[j]) { + + graph[i].push(snakesAndLadders[j]); + + } else { + + graph[i].push(j); + + } + + } + + } + + return graph; + +} + +// Define the main function of the game + +function playGame() { + + // Build the game graph + + const graph = buildGraph(); + + // Define the starting node and the goal node + + const startNode = 1; + + const goalNode = BOARD_SIZE; + + // Define a queue for BFS + + const queue = [startNode]; + + // Define a visited set for BFS + + const visited = new Set(); + + visited.add(startNode); + + // Define a map for BFS to keep track of the parent node + + const parentMap = new Map(); + + parentMap.set(startNode, null); + + // Loop until the goal node is found or the queue is empty + + while (queue.length > 0) { + + // Dequeue the next node from the queue + + const currentNode = queue.shift(); + + // If the goal node is found, reconstruct the path and return it + + if (currentNode === goalNode) { + + const path = []; + + let node = currentNode; + + while (node !== null) { + + path.unshift(node); + + node = parentMap.get(node); + + } + + return path; + + } + + // Loop through the neighbors of the current node + + for (const neighbor of graph[currentNode]) { + + // If the neighbor has not been visited, add it to the queue + + if (!visited.has(neighbor)) { + + queue.push(neighbor); + + visited.add(neighbor); + + parentMap.set(neighbor, currentNode); + + } + + } + + } + + // If the goal node is not found, return null + + return null; + +} + +// Play the game and print the result + +console.log(playGame()); diff --git a/Graphs/snack_ladders.py b/Graphs/snack_ladders.py new file mode 100644 index 00000000..3aef1e48 --- /dev/null +++ b/Graphs/snack_ladders.py @@ -0,0 +1,70 @@ +''' + You are given an n x n integer matrix board where the cells are labeled from 1 to n2 in a Boustrophedon style starting from the bottom left of the board (i.e. board[n - 1][0]) and alternating direction each row. + + You start on square 1 of the board. In each move, starting from square curr, do the following: + + Choose a destination square next with a label in the range [curr + 1, min(curr + 6, n2)]. + This choice simulates the result of a standard 6-sided die roll: i.e., there are always at most 6 destinations, regardless of the size of the board. + If next has a snake or ladder, you must move to the destination of that snake or ladder. Otherwise, you move to next. + The game ends when you reach the square n2. + A board square on row r and column c has a snake or ladder if board[r][c] != -1. The destination of that snake or ladder is board[r][c]. Squares 1 and n2 do not have a snake or ladder. + + Note that you only take a snake or ladder at most once per move. If the destination to a snake or ladder is the start of another snake or ladder, you do not follow the subsequent snake or ladder. + + For example, suppose the board is [[-1,4],[-1,3]], and on the first move, your destination square is 2. You follow the ladder to square 3, but do not follow the subsequent ladder to 4. + Return the least number of moves required to reach the square n2. If it is not possible to reach the square, return -1. + + Example 1: + Input: board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]] + Output: 4 + + Explanation: + In the beginning, you start at square 1 (at row 5, column 0). + You decide to move to square 2 and must take the ladder to square 15. + You then decide to move to square 17 and must take the snake to square 13. + You then decide to move to square 14 and must take the ladder to square 35. + You then decide to move to square 36, ending the game. + This is the lowest possible number of moves to reach the last square, so return 4. + + Example 2: + Input: board = [[-1,-1],[-1,3]] + Output: 1 + + Constraints: + n == board.length == board[i].length + 2 <= n <= 20 + board[i][j] is either -1 or in the range [1, n2]. + The squares labeled 1 and n2 do not have any ladders or snakes. +''' +from collections import deque + +def snakesAndLadders(board): + n = len(board) + target = n*n + moves = {1: 0} + queue = deque([1]) + + while queue: + curr = queue.popleft() + if curr == target: + return moves[curr] + for i in range(1, 7): + next = curr + i + if next > target: + break + row, col = getRowCol(next, n) + if board[row][col] != -1: + next = board[row][col] + if next not in moves: + moves[next] = moves[curr] + 1 + queue.append(next) + + return -1 + +def getRowCol(idx, n): + row = (idx - 1) // n + col = (idx - 1) % n + if row % 2 == 1: + col = n - col - 1 + row = n - row - 1 + return row, col diff --git a/Graphs/snakes_ladders.java b/Graphs/snakes_ladders.java new file mode 100644 index 00000000..f8608137 --- /dev/null +++ b/Graphs/snakes_ladders.java @@ -0,0 +1,123 @@ +/*You are given an n x n integer matrix board where the cells are labeled from 1 to n2 in a Boustrophedon style starting from the bottom left of the board (i.e. board[n - 1][0]) and alternating direction each row. + +You start on square 1 of the board. In each move, starting from square curr, do the following: + +Choose a destination square next with a label in the range [curr + 1, min(curr + 6, n2)]. +This choice simulates the result of a standard 6-sided die roll: i.e., there are always at most 6 destinations, regardless of the size of the board. +If next has a snake or ladder, you must move to the destination of that snake or ladder. Otherwise, you move to next. +The game ends when you reach the square n2. +A board square on row r and column c has a snake or ladder if board[r][c] != -1. The destination of that snake or ladder is board[r][c]. Squares 1 and n2 do not have a snake or ladder. + +Note that you only take a snake or ladder at most once per move. If the destination to a snake or ladder is the start of another snake or ladder, you do not follow the subsequent snake or ladder. + +For example, suppose the board is [[-1,4],[-1,3]], and on the first move, your destination square is 2. You follow the ladder to square 3, but do not follow the subsequent ladder to 4. +Return the least number of moves required to reach the square n2. If it is not possible to reach the square, return -1. + +Explanation: + +We start by importing the necessary classes and defining the class SnakesAndLadders. +We declare the constant BOARD_SIZE to represent the number of cells on the board. +We declare two maps: snakes to store the snake positions, and ladders to store the ladder positions. +The SnakesAndLadders class has a constructor that initializes the snakes and ladders maps. +The addSnake method is used to add a snake to the game by providing the start and end positions. +The addLadder method is used to add a ladder to the game by providing the start and end positions. +The playGame method simulates the game. It starts with the player's position at 0 and the number of dice rolls at 0. +Inside the while loop, a dice is rolled using the rollDice method, and the player's position is updated. +After each move, we check if the player has landed on a ladder or a snake. If so, we update the player's position accordingly. +The rollDice method generates a random number between 1 and 6, simulating a dice roll. +In the main method, we create an instance of SnakesAndLadders and add snakes and ladders to the game. +Finally, we call the playGame method to start the game and print the number of dice rolls required to reach or exceed the BOARD_SIZE. +Below is an implementation of the Snakes and Ladders game in Java, along with comments:*/ + +import java.util.HashMap; +import java.util.Map; + +public class SnakesAndLadders { + private static final int BOARD_SIZE = 100; // Number of cells in the board + private Map snakes; // Map to store snake positions + private Map ladders; // Map to store ladder positions + + public SnakesAndLadders() { + // Initialize the snake and ladder positions + snakes = new HashMap<>(); + ladders = new HashMap<>(); + } + + public void addSnake(int start, int end) { + snakes.put(start, end); + } + + public void addLadder(int start, int end) { + ladders.put(start, end); + } + + public int playGame() { + int currentPlayerPosition = 0; // Player's current position + int diceRolls = 0; // Number of dice rolls + + while (currentPlayerPosition < BOARD_SIZE) { + // Roll a dice + int dice = rollDice(); + + // Move the player + currentPlayerPosition += dice; + + // Check if the player has landed on a ladder + if (ladders.containsKey(currentPlayerPosition)) { + // Climb the ladder + currentPlayerPosition = ladders.get(currentPlayerPosition); + } + + // Check if the player has landed on a snake + if (snakes.containsKey(currentPlayerPosition)) { + // Go down the snake + currentPlayerPosition = snakes.get(currentPlayerPosition); + } + + diceRolls++; // Increment the number of dice rolls + } + + return diceRolls; + } + + private int rollDice() { + // Generates a random number between 1 and 6 (inclusive) + return (int) (Math.random() * 6) + 1; + } + + public static void main(String[] args) { + SnakesAndLadders game = new SnakesAndLadders(); + + // Add snakes to the game + game.addSnake(16, 6); + game.addSnake(47, 26); + game.addSnake(49, 11); + game.addSnake(56, 53); + game.addSnake(62, 19); + game.addSnake(64, 60); + game.addSnake(87, 24); + game.addSnake(93, 73); + game.addSnake(95, 75); + game.addSnake(98, 78); + + // Add ladders to the game + game.addLadder(1, 38); + game.addLadder(4, 14); + game.addLadder(9, 31); + game.addLadder(21, 42); + game.addLadder(28, 84); + game.addLadder(36, 44); + game.addLadder(51, 67); + game.addLadder(71, 91); + game.addLadder(80, 100); + + // Play the game + int diceRolls = game.playGame(); + System.out.println("Number of dice rolls: " + diceRolls); + } +} + +Time Complexity: the average generalized time complexity of the code can be expressed as O(N), where N represents the average number of dice rolls required to + complete the game over a large number of instances. +Space Complexity: The average generalized space complexity of the code can be expressed as O(M), where M represents the average number of snakes and ladders add- + -ed to the game. diff --git a/Graphs/topological_sort.cpp b/Graphs/topological_sort.cpp new file mode 100644 index 00000000..95115b3f --- /dev/null +++ b/Graphs/topological_sort.cpp @@ -0,0 +1,213 @@ +/* + You're given a list of arbitrary jobs that need to be completed; these jobs + are represented by distinct integers. You're also given a list of dependencies. A + dependency is represented as a pair of jobs where the first job is a + prerequisite of the second one. In other words, the second job depends on the + first one; it can only be completed once the first job is completed. + + Write a function that takes in a list of jobs and a list of dependencies and + returns a list containing a valid order in which the given jobs can be + completed. If no such order exists, the function should return an empty array. + + Sample INput: + jobs: [1, 2, 3, 4] + deps: = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]] + Output: [1, 4, 3, 2] or [4, 1, 3, 2] + + Explanation: + The provided code implements a topological sorting algorithm to find the order in which jobs can be executed given their dependencies. It uses a directed acyclic graph (DAG) representation to represent the jobs and their dependencies. + + Let's go through each part of the code in detail: + + 1. `Dep` struct: + - This is a simple struct that represents a dependency between two jobs. + - `Prereq` field indicates the prerequisite job. + - `Job` field indicates the job that depends on the prerequisite. + + 2. `TopologicalSort` function: + - This is the main function that performs the topological sorting of jobs. + - It takes two input parameters: `jobs`, a list of job IDs, and `deps`, a list of dependencies. + - It first creates a job graph using the `createJobGraph` function and then gets the ordered jobs using the `getOrderedJobs` function. + + 3. `createJobGraph` function: + - This function creates a job graph from the list of jobs and dependencies. + - It creates a new instance of the `JobGraph` struct and iterates through the dependencies. + - For each dependency, it adds the prerequisite job to the dependent job's list of prerequisites in the job graph. + + 4. `getOrderedJobs` function: + - This function performs a depth-first traversal of the job graph to get the ordered jobs. + - It iterates through the nodes of the graph until all nodes have been visited. + - For each node, it calls the `depthFirstTraverse` function to perform the depth-first traversal and get the ordered jobs. + - If a cycle is detected in the graph during the traversal, it means that there is a circular dependency, and the function returns an empty list. + + 5. `depthFirstTraverse` function: + - This function performs the depth-first traversal of the graph starting from a given node. + - It checks if the current node has been visited (i.e., it is not part of any cycle). + - If the node is currently being visited (marked as `Visiting`), it means that there is a cycle, and the function returns `true`. + - Otherwise, it marks the node as `Visiting`, recursively traverses through its prerequisites, and marks it as `Visited` after the traversal. + - It appends the job to the `orderedJobs` list in the correct order. + + 6. `JobGraph` struct: + - This struct represents the job graph. + - `Nodes` is a list of all the job nodes in the graph. + - `Graph` is a map where the key is the job ID, and the value is a pointer to the corresponding `JobNode` in the graph. + + 7. `NewJobGraph` function: + - This function creates a new instance of the `JobGraph` struct and initializes its `Graph` field with an empty map. + - It also adds individual job nodes to the `Nodes` list. + + 8. `Addprereq`, `AddNode`, and `GetNode` functions: + - These are helper functions to add prerequisites to a job, add a new job node to the graph, and get an existing job node, respectively. + + 9. `JobNode` struct: + - This struct represents a node in the job graph. + - `Job` is the job ID. + - `Prereqs` is a list of pointers to other job nodes that are prerequisites for the current job. + - `Visited` and `Visiting` are boolean flags used during the depth-first traversal to track the traversal status of each node. + + Overall, the code implements the topological sorting algorithm using depth-first traversal to find the order in which jobs can be executed without violating their dependencies. It efficiently handles circular dependencies and returns the ordered jobs or an empty list if a circular dependency is detected. + + O(j + d) time | O(j + d) space - where j is the number of jobs and d is the number of dependencies +*/ +#include +#include + +using namespace std; + +struct Dep { + int Prereq; + int Job; +}; + +class JobGraph; + +// Function prototypes +JobGraph createJobGraph(vector& jobs, vector& deps); +vector getOrderedJobs(JobGraph& graph); +bool depthFirstTraverse(JobGraph& graph, int jobId, vector& orderedJobs); + +class JobGraph { +public: + // Constructor to create a job graph from a list of jobs + JobGraph(vector& jobs) { + for (int job : jobs) { + // Add each job as a node to the graph + addNode(job); + } + } + + // Function to add a prerequisite to a job node + void addPrereq(int job, int prereq) { + JobNode* jobNode = getNode(job); + JobNode* prereqNode = getNode(prereq); + // Add the prerequisite node to the list of prerequisites for the job node + jobNode->prereqs.push_back(prereqNode); + } + + // Function to add a new job node to the graph + void addNode(int job) { + // Create a new job node and add it to the graph and nodes list + graph[job] = new JobNode(job); + nodes.push_back(graph[job]); + } + + // Function to get a job node from the graph by its ID + JobNode* getNode(int job) { + // If the node does not exist in the graph, create a new node and add it to the graph + if (graph.find(job) == graph.end()) { + addNode(job); + } + return graph[job]; + } + + // Data members + vector nodes; // List of all job nodes in the graph + unordered_map graph; // Map to store the job nodes by their IDs +}; + +// Function to perform topological sorting and return the ordered jobs +vector TopologicalSort(vector& jobs, vector& deps) { + // Create a job graph from the list of jobs and dependencies + JobGraph graph = createJobGraph(jobs, deps); + // Get the ordered jobs using depth-first traversal + return getOrderedJobs(graph); +} + +// Function to create a job graph from a list of jobs and dependencies +JobGraph createJobGraph(vector& jobs, vector& deps) { + // Initialize an empty graph + JobGraph graph(jobs); + // Add each dependency as a prerequisite to the corresponding job node in the graph + for (Dep dep : deps) { + graph.addPrereq(dep.Job, dep.Prereq); + } + return graph; +} + +// Function to perform depth-first traversal and get the ordered jobs +vector getOrderedJobs(JobGraph& graph) { + vector orderedJobs; + vector& nodes = graph.nodes; + + // Continue the traversal until all nodes have been visited + while (!nodes.empty()) { + // Get the last node from the list of nodes and remove it from the list + JobNode* node = nodes.back(); + nodes.pop_back(); + + // Perform depth-first traversal starting from the current node + if (!depthFirstTraverse(graph, node->job, orderedJobs)) { + return {}; // If a cycle is detected during traversal, return an empty list + } + } + return orderedJobs; +} + +// Function to perform depth-first traversal starting from a specific job node +bool depthFirstTraverse(JobGraph& graph, int jobId, vector& orderedJobs) { + // Get the job node from the graph + JobNode* node = graph.getNode(jobId); + + // If the node has been visited, it means it is not part of any cycle, so return false + if (node->visited) { + return false; + } + + // If the node is currently being visited, it means there is a cycle, so return true + if (node->visiting) { + return true; + } + + // Mark the node as currently being visited + node->visiting = true; + + // Recursively traverse through each prerequisite of the current node + for (JobNode* prereqNode : node->prereqs) { + // If a cycle is detected during the traversal, return true + if (depthFirstTraverse(graph, prereqNode->job, orderedJobs)) { + return true; + } + } + + // Mark the node as visited and no longer being visited + node->visited = true; + node->visiting = false; + + // Append the job to the orderedJobs list in the correct order + orderedJobs.push_back(node->job); + + // Return false, as no cycle was detected during the traversal + return false; +} + +// Definition of the JobNode class +class JobNode { +public: + int job; // Job ID + vector prereqs; // List of prerequisite job nodes + bool visited; // Flag to mark if the node has been visited + bool visiting; // Flag to mark if the node is currently being visited + + // Constructor to initialize a job node + JobNode(int jobId) : job(jobId), visited(false), visiting(false) {} +}; diff --git a/Graphs/topological_sort.go b/Graphs/topological_sort.go new file mode 100644 index 00000000..b88bed00 --- /dev/null +++ b/Graphs/topological_sort.go @@ -0,0 +1,202 @@ +/* + You're given a list of arbitrary jobs that need to be completed; these jobs + are represented by distinct integers. You're also given a list of dependencies. A + dependency is represented as a pair of jobs where the first job is a + prerequisite of the second one. In other words, the second job depends on the + first one; it can only be completed once the first job is completed. + + Write a function that takes in a list of jobs and a list of dependencies and + returns a list containing a valid order in which the given jobs can be + completed. If no such order exists, the function should return an empty array. + + Sample INput: + jobs: [1, 2, 3, 4] + deps: = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]] + Output: [1, 4, 3, 2] or [4, 1, 3, 2] + + Explanation: + The provided code implements a topological sorting algorithm to find the order in which jobs can be executed given their dependencies. It uses a directed acyclic graph (DAG) representation to represent the jobs and their dependencies. + + Let's go through each part of the code in detail: + + 1. `Dep` struct: + - This is a simple struct that represents a dependency between two jobs. + - `Prereq` field indicates the prerequisite job. + - `Job` field indicates the job that depends on the prerequisite. + + 2. `TopologicalSort` function: + - This is the main function that performs the topological sorting of jobs. + - It takes two input parameters: `jobs`, a list of job IDs, and `deps`, a list of dependencies. + - It first creates a job graph using the `createJobGraph` function and then gets the ordered jobs using the `getOrderedJobs` function. + + 3. `createJobGraph` function: + - This function creates a job graph from the list of jobs and dependencies. + - It creates a new instance of the `JobGraph` struct and iterates through the dependencies. + - For each dependency, it adds the prerequisite job to the dependent job's list of prerequisites in the job graph. + + 4. `getOrderedJobs` function: + - This function performs a depth-first traversal of the job graph to get the ordered jobs. + - It iterates through the nodes of the graph until all nodes have been visited. + - For each node, it calls the `depthFirstTraverse` function to perform the depth-first traversal and get the ordered jobs. + - If a cycle is detected in the graph during the traversal, it means that there is a circular dependency, and the function returns an empty list. + + 5. `depthFirstTraverse` function: + - This function performs the depth-first traversal of the graph starting from a given node. + - It checks if the current node has been visited (i.e., it is not part of any cycle). + - If the node is currently being visited (marked as `Visiting`), it means that there is a cycle, and the function returns `true`. + - Otherwise, it marks the node as `Visiting`, recursively traverses through its prerequisites, and marks it as `Visited` after the traversal. + - It appends the job to the `orderedJobs` list in the correct order. + + 6. `JobGraph` struct: + - This struct represents the job graph. + - `Nodes` is a list of all the job nodes in the graph. + - `Graph` is a map where the key is the job ID, and the value is a pointer to the corresponding `JobNode` in the graph. + + 7. `NewJobGraph` function: + - This function creates a new instance of the `JobGraph` struct and initializes its `Graph` field with an empty map. + - It also adds individual job nodes to the `Nodes` list. + + 8. `Addprereq`, `AddNode`, and `GetNode` functions: + - These are helper functions to add prerequisites to a job, add a new job node to the graph, and get an existing job node, respectively. + + 9. `JobNode` struct: + - This struct represents a node in the job graph. + - `Job` is the job ID. + - `Prereqs` is a list of pointers to other job nodes that are prerequisites for the current job. + - `Visited` and `Visiting` are boolean flags used during the depth-first traversal to track the traversal status of each node. + + Overall, the code implements the topological sorting algorithm using depth-first traversal to find the order in which jobs can be executed without violating their dependencies. It efficiently handles circular dependencies and returns the ordered jobs or an empty list if a circular dependency is detected. + + O(j + d) time | O(j + d) space - where j is the number of jobs and d is the number of dependencies +*/ +package main + +type Dep struct { + Prereq int + Job int +} + +// TopologicalSort performs topological sorting of jobs given their dependencies. +func TopologicalSort(jobs []int, deps []Dep) []int { + // Create a job graph from the list of jobs and dependencies. + jobGraph := createJobGraph(jobs, deps) + + // Get the ordered jobs using depth-first traversal. + return getOrderedJobs(jobGraph) +} + +// createJobGraph creates a job graph from the list of jobs and dependencies. +func createJobGraph(jobs []int, deps []Dep) *JobGraph { + graph := NewJobGraph(jobs) + for _, dep := range deps { + // Add each dependency as a prerequisite to the corresponding job node in the graph. + graph.Addprereq(dep.Job, dep.Prereq) + } + return graph +} + +// getOrderedJobs performs a depth-first traversal of the job graph to get the ordered jobs. +func getOrderedJobs(graph *JobGraph) []int { + orderedJobs := []int{} + nodes := graph.Nodes + + // Continue the traversal until all nodes have been visited. + for len(nodes) != 0 { + // Get the last node from the list of nodes and remove it from the list. + node := nodes[len(nodes)-1] + nodes = nodes[:len(nodes)-1] + + // Perform depth-first traversal starting from the current node. + // If a cycle is detected, return an empty list. + containsCycle := depthFirstTraverse(node, &orderedJobs) + if containsCycle { + return []int{} + } + } + return orderedJobs +} + +// depthFirstTraverse performs depth-first traversal starting from the given node. +// It returns true if a cycle is detected, otherwise, it returns false. +func depthFirstTraverse(node *JobNode, orderedJobs *[]int) bool { + // If the node has been visited, it means it is not part of any cycle, so return false. + if node.Visited { + return false + } + // If the node is currently being visited it means there is a cycle, so return true. + if node.Visiting { + return true + } + + // Mark the node as currently being visited. + node.Visiting = true + + // Recursively traverse through each prerequisite of the current node. + for _, prereqNode := range node.Prereqs { + // If a cycle is detected during the traversal, return true. + containsCycle := depthFirstTraverse(prereqNode, orderedJobs) + if containsCycle { + return true + } + } + + // Mark the node as visited and no longer being visited. + node.Visited = true + node.Visiting = false + + // Append the job to the orderedJobs list in the correct order. + *orderedJobs = append(*orderedJobs, node.Job) + + // Return false, as no cycle was detected during the traversal. + return false +} + +// JobGraph represents the job graph. +type JobGraph struct { + Nodes []*JobNode + Graph map[int]*JobNode +} + +// NewJobGraph creates a new instance of the JobGraph and initializes its Graph field. +func NewJobGraph(jobs []int) *JobGraph { + g := &JobGraph{ + Graph: map[int]*JobNode{}, + } + for _, job := range jobs { + // Add individual job nodes to the Nodes list. + g.AddNode(job) + } + return g +} + +// Addprereq adds a prerequisite to a job in the job graph. +func (g *JobGraph) Addprereq(job, prereq int) { + jobNode := g.GetNode(job) + prereqNode := g.GetNode(prereq) + // Add the prerequisite node to the list of prerequisites for the job node. + jobNode.Prereqs = append(jobNode.Prereqs, prereqNode) +} + +// AddNode adds a new job node to the job graph. +func (g *JobGraph) AddNode(job int) { + // Create a new job node and add it to the graph and nodes list. + g.Graph[job] = &JobNode{Job: job} + g.Nodes = append(g.Nodes, g.Graph[job]) +} + +// GetNode gets an existing job node from the job graph. +func (g *JobGraph) GetNode(job int) *JobNode { + // If the node does not exist in the graph, create a new node and add it to the graph. + if _, found := g.Graph[job]; !found { + g.AddNode(job) + } + return g.Graph[job] +} + +// JobNode represents a node in the job graph. +type JobNode struct { + Job int + Prereqs []*JobNode + Visited bool + Visiting bool +} diff --git a/Graphs/topological_sort.java b/Graphs/topological_sort.java new file mode 100644 index 00000000..350e54e3 --- /dev/null +++ b/Graphs/topological_sort.java @@ -0,0 +1,204 @@ +/* + You're given a list of arbitrary jobs that need to be completed; these jobs + are represented by distinct integers. You're also given a list of dependencies. A + dependency is represented as a pair of jobs where the first job is a + prerequisite of the second one. In other words, the second job depends on the + first one; it can only be completed once the first job is completed. + + Write a function that takes in a list of jobs and a list of dependencies and + returns a list containing a valid order in which the given jobs can be + completed. If no such order exists, the function should return an empty array. + + Sample INput: + jobs: [1, 2, 3, 4] + deps: = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]] + Output: [1, 4, 3, 2] or [4, 1, 3, 2] + + Explanation: + The provided code implements a topological sorting algorithm to find the order in which jobs can be executed given their dependencies. It uses a directed acyclic graph (DAG) representation to represent the jobs and their dependencies. + + Let's go through each part of the code in detail: + + 1. `Dep` struct: + - This is a simple struct that represents a dependency between two jobs. + - `Prereq` field indicates the prerequisite job. + - `Job` field indicates the job that depends on the prerequisite. + + 2. `TopologicalSort` function: + - This is the main function that performs the topological sorting of jobs. + - It takes two input parameters: `jobs`, a list of job IDs, and `deps`, a list of dependencies. + - It first creates a job graph using the `createJobGraph` function and then gets the ordered jobs using the `getOrderedJobs` function. + + 3. `createJobGraph` function: + - This function creates a job graph from the list of jobs and dependencies. + - It creates a new instance of the `JobGraph` struct and iterates through the dependencies. + - For each dependency, it adds the prerequisite job to the dependent job's list of prerequisites in the job graph. + + 4. `getOrderedJobs` function: + - This function performs a depth-first traversal of the job graph to get the ordered jobs. + - It iterates through the nodes of the graph until all nodes have been visited. + - For each node, it calls the `depthFirstTraverse` function to perform the depth-first traversal and get the ordered jobs. + - If a cycle is detected in the graph during the traversal, it means that there is a circular dependency, and the function returns an empty list. + + 5. `depthFirstTraverse` function: + - This function performs the depth-first traversal of the graph starting from a given node. + - It checks if the current node has been visited (i.e., it is not part of any cycle). + - If the node is currently being visited (marked as `Visiting`), it means that there is a cycle, and the function returns `true`. + - Otherwise, it marks the node as `Visiting`, recursively traverses through its prerequisites, and marks it as `Visited` after the traversal. + - It appends the job to the `orderedJobs` list in the correct order. + + 6. `JobGraph` struct: + - This struct represents the job graph. + - `Nodes` is a list of all the job nodes in the graph. + - `Graph` is a map where the key is the job ID, and the value is a pointer to the corresponding `JobNode` in the graph. + + 7. `NewJobGraph` function: + - This function creates a new instance of the `JobGraph` struct and initializes its `Graph` field with an empty map. + - It also adds individual job nodes to the `Nodes` list. + + 8. `Addprereq`, `AddNode`, and `GetNode` functions: + - These are helper functions to add prerequisites to a job, add a new job node to the graph, and get an existing job node, respectively. + + 9. `JobNode` struct: + - This struct represents a node in the job graph. + - `Job` is the job ID. + - `Prereqs` is a list of pointers to other job nodes that are prerequisites for the current job. + - `Visited` and `Visiting` are boolean flags used during the depth-first traversal to track the traversal status of each node. + + Overall, the code implements the topological sorting algorithm using depth-first traversal to find the order in which jobs can be executed without violating their dependencies. It efficiently handles circular dependencies and returns the ordered jobs or an empty list if a circular dependency is detected. + + O(j + d) time | O(j + d) space - where j is the number of jobs and d is the number of dependencies +*/ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class Dep { + int Prereq; + int Job; + + public Dep(int Prereq, int Job) { + this.Prereq = Prereq; + this.Job = Job; + } +} + +class JobGraph { + + // Inner class representing a job node + static class JobNode { + int job; // Job ID + List prereqs; // List of prerequisite job nodes + boolean visited; // Flag to mark if the node has been visited + boolean visiting; // Flag to mark if the node is currently being visited + + // Constructor to initialize a job node + public JobNode(int job) { + this.job = job; + this.prereqs = new ArrayList<>(); + this.visited = false; + this.visiting = false; + } + } + + List nodes; // List of all job nodes in the graph + Map graph; // Map to store the job nodes by their IDs + + // Constructor to create a job graph from a list of jobs + public JobGraph(List jobs) { + this.nodes = new ArrayList<>(); + this.graph = new HashMap<>(); + for (int job : jobs) { + // Add each job as a node to the graph + addNode(job); + } + } + + // Function to add a prerequisite to a job node + public void addPrereq(int job, int prereq) { + JobNode jobNode = getNode(job); + JobNode prereqNode = getNode(prereq); + // Add the prerequisite node to the list of prerequisites for the job node + jobNode.prereqs.add(prereqNode); + } + + // Function to add a new job node to the graph + public void addNode(int job) { + // Create a new job node and add it to the graph and nodes list + JobNode node = new JobNode(job); + graph.put(job, node); + nodes.add(node); + } + + // Function to get a job node from the graph by its ID + public JobNode getNode(int job) { + // If the node does not exist in the graph, create a new node and add it to the graph + if (!graph.containsKey(job)) { + addNode(job); + } + return graph.get(job); + } +} + +public class Main { + + // Function to perform topological sorting and return the ordered jobs + public static List TopologicalSort(List jobs, List deps) { + // Create a job graph from the list of jobs and dependencies + JobGraph graph = createJobGraph(jobs, deps); + // Get the ordered jobs using depth-first traversal + return getOrderedJobs(graph); + } + + // Function to create a job graph from a list of jobs and dependencies + public static JobGraph createJobGraph(List jobs, List deps) { + // Initialize an empty graph + JobGraph graph = new JobGraph(jobs); + // Add each dependency as a prerequisite to the corresponding job node in the graph + for (Dep dep : deps) { + graph.addPrereq(dep.Job, dep.Prereq); + } + return graph; + } + + // Function to perform depth-first traversal and get the ordered jobs + public static List getOrderedJobs(JobGraph graph) { + List orderedJobs = new ArrayList<>(); + List nodes = graph.nodes; + + // Continue the traversal until all nodes have been visited + while (!nodes.isEmpty()) { + // Get the last node from the list of nodes and remove it from the list + JobGraph.JobNode node = nodes.remove(nodes.size() - 1); + + // Perform depth-first traversal starting from the current node + if (!depthFirstTraverse(graph, node.job, orderedJobs)) { + return new ArrayList<>(); // If a cycle is detected during traversal, return an empty list + } + } + return orderedJobs; + } + + // Function to perform depth-first traversal starting from a specific job node + public static boolean depthFirstTraverse(JobGraph graph, int jobId, List orderedJobs) { + // Get the job node from the graph + JobGraph.JobNode node = graph.getNode(jobId); + + // If the node has been visited, it means it is not part of any cycle, so return false + if (node.visited) { + return false; + } + + // If the node is currently being visited, it means there is a cycle, so return true + if (node.visiting) { + return true; + } + + // Mark the node as currently being visited + node.visiting = true; + + // Recursively traverse through each prerequisite of the current node + for (JobGraph.JobNode prereqNode : node.prereqs) { + // If a cycle is detected during the traversal, return true + if (depth diff --git a/Graphs/topological_sort.js b/Graphs/topological_sort.js new file mode 100644 index 00000000..28398eb8 --- /dev/null +++ b/Graphs/topological_sort.js @@ -0,0 +1,180 @@ +/* + You're given a list of arbitrary jobs that need to be completed; these jobs + are represented by distinct integers. You're also given a list of dependencies. A + dependency is represented as a pair of jobs where the first job is a + prerequisite of the second one. In other words, the second job depends on the + first one; it can only be completed once the first job is completed. + + Write a function that takes in a list of jobs and a list of dependencies and + returns a list containing a valid order in which the given jobs can be + completed. If no such order exists, the function should return an empty array. + + Sample INput: + jobs: [1, 2, 3, 4] + deps: = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]] + Output: [1, 4, 3, 2] or [4, 1, 3, 2] + + Explanation: + The provided code implements a topological sorting algorithm to find the order in which jobs can be executed given their dependencies. It uses a directed acyclic graph (DAG) representation to represent the jobs and their dependencies. + + Let's go through each part of the code in detail: + + 1. `Dep` struct: + - This is a simple struct that represents a dependency between two jobs. + - `Prereq` field indicates the prerequisite job. + - `Job` field indicates the job that depends on the prerequisite. + + 2. `TopologicalSort` function: + - This is the main function that performs the topological sorting of jobs. + - It takes two input parameters: `jobs`, a list of job IDs, and `deps`, a list of dependencies. + - It first creates a job graph using the `createJobGraph` function and then gets the ordered jobs using the `getOrderedJobs` function. + + 3. `createJobGraph` function: + - This function creates a job graph from the list of jobs and dependencies. + - It creates a new instance of the `JobGraph` struct and iterates through the dependencies. + - For each dependency, it adds the prerequisite job to the dependent job's list of prerequisites in the job graph. + + 4. `getOrderedJobs` function: + - This function performs a depth-first traversal of the job graph to get the ordered jobs. + - It iterates through the nodes of the graph until all nodes have been visited. + - For each node, it calls the `depthFirstTraverse` function to perform the depth-first traversal and get the ordered jobs. + - If a cycle is detected in the graph during the traversal, it means that there is a circular dependency, and the function returns an empty list. + + 5. `depthFirstTraverse` function: + - This function performs the depth-first traversal of the graph starting from a given node. + - It checks if the current node has been visited (i.e., it is not part of any cycle). + - If the node is currently being visited (marked as `Visiting`), it means that there is a cycle, and the function returns `true`. + - Otherwise, it marks the node as `Visiting`, recursively traverses through its prerequisites, and marks it as `Visited` after the traversal. + - It appends the job to the `orderedJobs` list in the correct order. + + 6. `JobGraph` struct: + - This struct represents the job graph. + - `Nodes` is a list of all the job nodes in the graph. + - `Graph` is a map where the key is the job ID, and the value is a pointer to the corresponding `JobNode` in the graph. + + 7. `NewJobGraph` function: + - This function creates a new instance of the `JobGraph` struct and initializes its `Graph` field with an empty map. + - It also adds individual job nodes to the `Nodes` list. + + 8. `Addprereq`, `AddNode`, and `GetNode` functions: + - These are helper functions to add prerequisites to a job, add a new job node to the graph, and get an existing job node, respectively. + + 9. `JobNode` struct: + - This struct represents a node in the job graph. + - `Job` is the job ID. + - `Prereqs` is a list of pointers to other job nodes that are prerequisites for the current job. + - `Visited` and `Visiting` are boolean flags used during the depth-first traversal to track the traversal status of each node. + + Overall, the code implements the topological sorting algorithm using depth-first traversal to find the order in which jobs can be executed without violating their dependencies. It efficiently handles circular dependencies and returns the ordered jobs or an empty list if a circular dependency is detected. + + O(j + d) time | O(j + d) space - where j is the number of jobs and d is the number of dependencies +*/ +class Dep { + constructor(Prereq, Job) { + this.Prereq = Prereq; + this.Job = Job; + } +} + +class JobGraph { + constructor(jobs) { + this.nodes = []; + this.graph = new Map(); + for (const job of jobs) { + // Add each job as a node to the graph + this.addNode(job); + } + } + + addPrereq(job, prereq) { + const jobNode = this.getNode(job); + const prereqNode = this.getNode(prereq); + // Add the prerequisite node to the list of prerequisites for the job node + jobNode.prereqs.push(prereqNode); + } + + addNode(job) { + // Create a new job node and add it to the graph and nodes list + const node = { job, prereqs: [], visited: false, visiting: false }; + this.graph.set(job, node); + this.nodes.push(node); + } + + getNode(job) { + // If the node does not exist in the graph, create a new node and add it to the graph + if (!this.graph.has(job)) { + this.addNode(job); + } + return this.graph.get(job); + } +} + +function TopologicalSort(jobs, deps) { + // Create a job graph from the list of jobs and dependencies + const graph = createJobGraph(jobs, deps); + // Get the ordered jobs using depth-first traversal + return getOrderedJobs(graph); +} + +function createJobGraph(jobs, deps) { + // Initialize an empty graph + const graph = new JobGraph(jobs); + // Add each dependency as a prerequisite to the corresponding job node in the graph + for (const dep of deps) { + graph.addPrereq(dep.Job, dep.Prereq); + } + return graph; +} + +function getOrderedJobs(graph) { + const orderedJobs = []; + const nodes = graph.nodes; + + // Continue the traversal until all nodes have been visited + while (nodes.length > 0) { + // Get the last node from the list of nodes and remove it from the list + const node = nodes.pop(); + + // Perform depth-first traversal starting from the current node + if (!depthFirstTraverse(graph, node.job, orderedJobs)) { + return []; // If a cycle is detected during traversal, return an empty list + } + } + return orderedJobs; +} + +function depthFirstTraverse(graph, jobId, orderedJobs) { + // Get the job node from the graph + const node = graph.getNode(jobId); + + // If the node has been visited, it means it is not part of any cycle, so return false + if (node.visited) { + return false; + } + + // If the node is currently being visited, it means there is a cycle, so return true + if (node.visiting) { + return true; + } + + // Mark the node as currently being visited + node.visiting = true; + + // Recursively traverse through each prerequisite of the current node + for (const prereqNode of node.prereqs) { + // If a cycle is detected during the traversal, return true + if (depthFirstTraverse(graph, prereqNode.job, orderedJobs)) { + return true; + } + } + + // Mark the node as visited and not currently being visited + node.visited = true; + node.visiting = false; + + // Add the job to the ordered jobs list + orderedJobs.push(jobId); + + // Return false to indicate that no cycle was detected + return false; +} diff --git a/Graphs/topological_sort.py b/Graphs/topological_sort.py new file mode 100644 index 00000000..bba78dfc --- /dev/null +++ b/Graphs/topological_sort.py @@ -0,0 +1,47 @@ +""" +Implementation of Topological sort using DFS +According to Introduction to Algorithms, given a directed acyclic graph (DAG), +a topological sort is a linear ordering of all vertices such that for any edge +(u, v), u comes before v. Another way to describe it is that when you put all +vertices horizontally on a line, all of the edges are pointing from left to right. + +Time complexity O(Vertices + Edges) +""" + +def main(): + adj_list = { + 5: [2, 0], + 4: [0, 1], + 3: [1], + 2: [3], + 1: [], + 0: [] + } + + dfs(adj_list, set(), []) + + + +def dfs(adj_list, visited, stack): + for vertex in range(6): + if vertex in visited: + continue + dfs_visit(adj_list, visited, vertex, stack) + + print(stack[::-1]) + + +def dfs_visit(adj_list, visited, node, stack): + visited.add(node) + + for neighbor in adj_list[node]: + if neighbor in visited: + continue + dfs_visit(adj_list, visited, neighbor, stack) + + stack.append(node) + + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Graphs/two_colorable.cpp b/Graphs/two_colorable.cpp new file mode 100644 index 00000000..a3443fa7 --- /dev/null +++ b/Graphs/two_colorable.cpp @@ -0,0 +1,85 @@ +/* + You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. + Write a function that returns a boolean representing whether the given graph is two-colorable. + + Explanation: + The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. + + The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. + + The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. + + Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. + + If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. + + The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. + + Here's a step-by-step explanation of the algorithm: + + 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). + 2. Initialize an empty stack and push the first vertex (0) onto it. + 3. While the stack is not empty, repeat the following steps: + a. Pop the top vertex from the stack (denoted by `node`). + b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: + i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of + `node` (i.e., `!colors[node]`) and push it onto the stack. + ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the + graph is not two-colorable. + 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. + + Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. + If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. + + O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph + +*/ +#include +#include +#include +#include + +bool isTwoColorable(std::vector>& edges) { + // colors keeps track of the colors assigned to each vertex. + // We start by assigning the first vertex (0) the color true (denoted by 0: true). + std::unordered_map colors; + colors[0] = true; + + // stack is used for depth-first traversal of the graph. + std::stack stack; + stack.push(0); + + while (!stack.empty()) { + // Pop the top vertex from the stack (denoted by 'node'). + int node = stack.top(); + stack.pop(); + + // Explore adjacent vertices (connections) of the current vertex 'node'. + for (int connection : edges[node]) { + // If the adjacent vertex has not been colored yet (not present in the 'colors' map), + // assign it the opposite color of the current vertex (denoted by !colors[node]), + // and push it onto the stack. + if (colors.find(connection) == colors.end()) { + colors[connection] = !colors[node]; + stack.push(connection); + } else { + // If the adjacent vertex has already been colored and its color is the same as the current vertex's color, + // then the graph cannot be two-colorable, return false. + if (colors[connection] == colors[node]) { + return false; + } + } + } + } + + // If the traversal completes without any conflicts (no adjacent vertices have the same color), + // return true, indicating that the graph is two-colorable. + return true; +} + +int main() { + std::vector> edges = {{1, 2}, {0, 3}, {0, 4}, {1}, {2}}; + bool isTwoColorableResult = isTwoColorable(edges); + std::cout << "Is Two-Colorable: " << std::boolalpha << isTwoColorableResult << std::endl; + return 0; +} diff --git a/Graphs/two_colorable.go b/Graphs/two_colorable.go new file mode 100644 index 00000000..9bac7e04 --- /dev/null +++ b/Graphs/two_colorable.go @@ -0,0 +1,75 @@ +/* + You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. + Write a function that returns a boolean representing whether the given graph is two-colorable. + + Explanation: + The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. + + The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. + + The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. + + Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. + + If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. + + The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. + + Here's a step-by-step explanation of the algorithm: + + 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). + 2. Initialize an empty stack and push the first vertex (0) onto it. + 3. While the stack is not empty, repeat the following steps: + a. Pop the top vertex from the stack (denoted by `node`). + b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: + i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of + `node` (i.e., `!colors[node]`) and push it onto the stack. + ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the + graph is not two-colorable. + 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. + + Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. + If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. + + O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph + +*/ +package main + +func TwoColorable(edges [][]int) bool { + // colors keeps track of the colors assigned to each vertex. + // We start by assigning the first vertex (0) the color true (denoted by 0: true). + colors := map[int]bool{ + 0: true, + } + + // stack is used for depth-first traversal of the graph. + stack := []int{0} + + for len(stack) > 0 { + // Pop the top vertex from the stack (denoted by 'node'). + node := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + // Explore adjacent vertices (connections) of the current vertex 'node'. + for _, connection := range edges[node] { + // If the adjacent vertex has not been colored yet (not present in the 'colors' map), + // assign it the opposite color of the current vertex (denoted by '!colors[node]'), + // and push it onto the stack. + if _, colorFound := colors[connection]; !colorFound { + colors[connection] = !colors[node] + stack = append(stack, connection) + } else { + // If the adjacent vertex has already been colored and its color is the same as the current vertex's color, + // then the graph cannot be two-colorable, return false. + if colors[connection] == colors[node] { + return false + } + } + } + } + + // If the traversal completes without any conflicts (no adjacent vertices have the same color), + // return true, indicating that the graph is two-colorable. + return true +} diff --git a/Graphs/two_colorable.java b/Graphs/two_colorable.java new file mode 100644 index 00000000..65399330 --- /dev/null +++ b/Graphs/two_colorable.java @@ -0,0 +1,89 @@ +/* + You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. + Write a function that returns a boolean representing whether the given graph is two-colorable. + + Explanation: + The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. + + The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. + + The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. + + Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. + + If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. + + The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. + + Here's a step-by-step explanation of the algorithm: + + 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). + 2. Initialize an empty stack and push the first vertex (0) onto it. + 3. While the stack is not empty, repeat the following steps: + a. Pop the top vertex from the stack (denoted by `node`). + b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: + i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of + `node` (i.e., `!colors[node]`) and push it onto the stack. + ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the + graph is not two-colorable. + 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. + + Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. + If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. + + O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph + +*/ +import java.util.*; + +public class TwoColorable { + + public static boolean isTwoColorable(List> edges) { + // colors keeps track of the colors assigned to each vertex. + // We start by assigning the first vertex (0) the color true (denoted by 0: true). + Map colors = new HashMap<>(); + colors.put(0, true); + + // stack is used for depth-first traversal of the graph. + Stack stack = new Stack<>(); + stack.push(0); + + while (!stack.isEmpty()) { + // Pop the top vertex from the stack (denoted by 'node'). + int node = stack.pop(); + + // Explore adjacent vertices (connections) of the current vertex 'node'. + for (int connection : edges.get(node)) { + // If the adjacent vertex has not been colored yet (not present in the 'colors' map), + // assign it the opposite color of the current vertex (denoted by '!colors.get(node)'), + // and push it onto the stack. + if (!colors.containsKey(connection)) { + colors.put(connection, !colors.get(node)); + stack.push(connection); + } else { + // If the adjacent vertex has already been colored and its color is the same as the current vertex's color, + // then the graph cannot be two-colorable, return false. + if (colors.get(connection) == colors.get(node)) { + return false; + } + } + } + } + + // If the traversal completes without any conflicts (no adjacent vertices have the same color), + // return true, indicating that the graph is two-colorable. + return true; + } + + public static void main(String[] args) { + List> edges = new ArrayList<>(); + edges.add(Arrays.asList(1, 2)); + edges.add(Arrays.asList(0, 3)); + edges.add(Arrays.asList(0, 4)); + edges.add(Arrays.asList(1)); + edges.add(Arrays.asList(2)); + + boolean isTwoColorable = isTwoColorable(edges); + System.out.println("Is Two-Colorable: " + isTwoColorable); + } +} diff --git a/Graphs/two_colorable.js b/Graphs/two_colorable.js new file mode 100644 index 00000000..f7392768 --- /dev/null +++ b/Graphs/two_colorable.js @@ -0,0 +1,76 @@ +/* + You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. + Write a function that returns a boolean representing whether the given graph is two-colorable. + + Explanation: + The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. + + The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. + + The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. + + Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. + + If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. + + The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. + + Here's a step-by-step explanation of the algorithm: + + 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). + 2. Initialize an empty stack and push the first vertex (0) onto it. + 3. While the stack is not empty, repeat the following steps: + a. Pop the top vertex from the stack (denoted by `node`). + b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: + i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of + `node` (i.e., `!colors[node]`) and push it onto the stack. + ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the + graph is not two-colorable. + 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. + + Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. + If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. + + O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph + +*/ +function isTwoColorable(edges) { + // colors keeps track of the colors assigned to each vertex. + // We start by assigning the first vertex (0) the color true (denoted by 0: true). + const colors = new Map(); + colors.set(0, true); + + // stack is used for depth-first traversal of the graph. + const stack = [0]; + + while (stack.length > 0) { + // Pop the top vertex from the stack (denoted by 'node'). + const node = stack.pop(); + + // Explore adjacent vertices (connections) of the current vertex 'node'. + for (const connection of edges[node]) { + // If the adjacent vertex has not been colored yet (not present in the 'colors' map), + // assign it the opposite color of the current vertex (denoted by !colors.get(node)), + // and push it onto the stack. + if (!colors.has(connection)) { + colors.set(connection, !colors.get(node)); + stack.push(connection); + } else { + // If the adjacent vertex has already been colored and its color is the same as the current vertex's color, + // then the graph cannot be two-colorable, return false. + if (colors.get(connection) === colors.get(node)) { + return false; + } + } + } + } + + // If the traversal completes without any conflicts (no adjacent vertices have the same color), + // return true, indicating that the graph is two-colorable. + return true; +} + +// Example usage +const edges = [[1, 2], [0, 3], [0, 4], [1], [2]]; +const isTwoColorableResult = isTwoColorable(edges); +console.log("Is Two-Colorable:", isTwoColorableResult); diff --git a/Graphs/two_colorable.py b/Graphs/two_colorable.py new file mode 100644 index 00000000..b3f9f835 --- /dev/null +++ b/Graphs/two_colorable.py @@ -0,0 +1,70 @@ +''' + You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. + Write a function that returns a boolean representing whether the given graph is two-colorable. + + Explanation: + The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. + + The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. + + The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. + + Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. + + If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. + + The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. + + Here's a step-by-step explanation of the algorithm: + + 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). + 2. Initialize an empty stack and push the first vertex (0) onto it. + 3. While the stack is not empty, repeat the following steps: + a. Pop the top vertex from the stack (denoted by `node`). + b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: + i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of + `node` (i.e., `!colors[node]`) and push it onto the stack. + ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the + graph is not two-colorable. + 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. + + Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. + If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. + + O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph + +''' +def is_two_colorable(edges): + # colors keeps track of the colors assigned to each vertex. + # We start by assigning the first vertex (0) the color True (denoted by 0: True). + colors = {0: True} + + # stack is used for depth-first traversal of the graph. + stack = [0] + + while stack: + # Pop the top vertex from the stack (denoted by 'node'). + node = stack.pop() + + # Explore adjacent vertices (connections) of the current vertex 'node'. + for connection in edges[node]: + # If the adjacent vertex has not been colored yet (not present in the 'colors' dictionary), + # assign it the opposite color of the current vertex (denoted by not colors[node]), + # and push it onto the stack. + if connection not in colors: + colors[connection] = not colors[node] + stack.append(connection) + else: + # If the adjacent vertex has already been colored and its color is the same as the current vertex's color, + # then the graph cannot be two-colorable, return False. + if colors[connection] == colors[node]: + return False + + # If the traversal completes without any conflicts (no adjacent vertices have the same color), + # return True, indicating that the graph is two-colorable. + return True + +# Example usage +edges = [[1, 2], [0, 3], [0, 4], [1], [2]] +is_two_colorable_result = is_two_colorable(edges) +print("Is Two-Colorable:", is_two_colorable_result) diff --git a/Graphs/union_find.cpp b/Graphs/union_find.cpp new file mode 100644 index 00000000..ea7d19f3 --- /dev/null +++ b/Graphs/union_find.cpp @@ -0,0 +1,132 @@ +/* + Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. + This class should support three methods: + + + The union-find data structure is similar to a traditional set data structure in that it contains a collection + of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set + can have duplicate values, and no two sets can contain the same value. + + createSet(value) : : Adds a given value in a new set containing only that value. + + union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined + into a single set. If either value is not in a set or they are in the same set, the function should have no effect. + + find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should + always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function + should return null / none + + Explanation: + The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: + + - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of + each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. + + - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for + `parents` and `ranks`. + + - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and + initializes its rank to 0. + + - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the + given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to + update the parent pointers along the path to the root. Finally, it returns a pointer to the root. + + `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist + in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds + the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: + + If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s + root to `valueTwo`'s root. + + If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s + root to `valueOne`'s root. + + If the ranks are equal, it chooses one root as the parent and increments its rank by 1. + + By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. + + The time and space complexity of the operations in the `UnionFind` data structure are as follows: + + CreateSet : O(n) time O(1) space + Find : O(log(n)) time O(1) space where n is total number of values + Union : O(log(n)) time O(1) space where n is total number of values + +*/ +#include +#include + +class UnionFind { +public: + std::unordered_map parents; // Map to store the parent of each element + std::unordered_map ranks; // Map to store the rank of each element + + void createSet(int value) { + parents[value] = value; // Set the parent of the value to itself + ranks[value] = 0; // Initialize the rank of the value to 0 + } + + int find(int value) { + if (parents.find(value) == parents.end()) { + return -1; // Return -1 if the value is not found (not part of any set) + } + + int currentParent = value; + while (currentParent != parents[currentParent]) { + currentParent = parents[currentParent]; // Traverse the parent pointers until reaching the root + } + + // Perform path compression by updating parent pointers along the path to the root + // This optimization flattens the tree structure, reducing future lookup time + while (value != currentParent) { + int nextParent = parents[value]; + parents[value] = currentParent; + value = nextParent; + } + + return currentParent; // Return the root/representative + } + + void unionSets(int valueOne, int valueTwo) { + if (parents.find(valueOne) == parents.end() || parents.find(valueTwo) == parents.end()) { + return; // Return if either value is not found (not part of any set) + } + + int valueOneRoot = find(valueOne); // Find the root of the set containing valueOne + int valueTwoRoot = find(valueTwo); // Find the root of the set containing valueTwo + + if (ranks[valueOneRoot] < ranks[valueTwoRoot]) { + parents[valueOneRoot] = valueTwoRoot; // Set the parent of valueOne's root to valueTwo's root + } else if (ranks[valueOneRoot] > ranks[valueTwoRoot]) { + parents[valueTwoRoot] = valueOneRoot; // Set the parent of valueTwo's root to valueOne's root + } else { + parents[valueOneRoot] = valueTwoRoot; // Set the parent of valueOne's root to valueTwo's root + ranks[valueTwoRoot] += 1; // Increment the rank of valueTwo's root + } + } +}; + +int main() { + UnionFind unionFind; + + // Create individual sets + unionFind.createSet(1); + unionFind.createSet(2); + unionFind.createSet(3); + + // Perform union operations + unionFind.unionSets(1, 2); + unionFind.unionSets(2, 3); + + // Find representatives of values + int representative = unionFind.find(3); + + // Check if the representative is found + if (representative != -1) { + std::cout << "The representative of 3 is: " << representative << std::endl; + } else { + std::cout << "Value 3 is not found." << std::endl; + } + + return 0; +} diff --git a/Graphs/union_find.go b/Graphs/union_find.go new file mode 100644 index 00000000..e948818e --- /dev/null +++ b/Graphs/union_find.go @@ -0,0 +1,144 @@ +/* + Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. + This class should support three methods: + + + The union-find data structure is similar to a traditional set data structure in that it contains a collection + of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set + can have duplicate values, and no two sets can contain the same value. + + createSet(value) : : Adds a given value in a new set containing only that value. + + union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined + into a single set. If either value is not in a set or they are in the same set, the function should have no effect. + + find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should + always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function + should return null / none + + Explanation: + The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: + + - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of + each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. + + - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for + `parents` and `ranks`. + + - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and + initializes its rank to 0. + + - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the + given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to + update the parent pointers along the path to the root. Finally, it returns a pointer to the root. + + `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist + in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds + the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: + + If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s + root to `valueTwo`'s root. + + If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s + root to `valueOne`'s root. + + If the ranks are equal, it chooses one root as the parent and increments its rank by 1. + + By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. + + The time and space complexity of the operations in the `UnionFind` data structure are as follows: + + CreateSet : O(n) time O(1) space + Find : O(log(n)) time O(1) space where n is total number of values + Union : O(log(n)) time O(1) space where n is total number of values + +*/ +package main + +import "fmt" + +type UnionFind struct { + parents map[int]int // Map to store the parent of each element + ranks map[int]int // Map to store the rank of each element +} + +// NewUnionFind creates a new instance of the UnionFind struct. +func NewUnionFind() *UnionFind { + return &UnionFind{ + parents: map[int]int{}, + ranks: map[int]int{}, + } +} + +// CreateSet creates a new set with the given value. +func (union *UnionFind) CreateSet(value int) { + union.parents[value] = value // Set the parent of the value to itself + union.ranks[value] = 0 // Initialize the rank of the value to 0 +} + +// Find finds the root/representative of the set to which the given value belongs. +func (union *UnionFind) Find(value int) *int { + if _, found := union.parents[value]; !found { + return nil // Return nil if the value is not found (not part of any set) + } + currentParent := value + for currentParent != union.parents[currentParent] { + currentParent = union.parents[currentParent] // Traverse the parent pointers until reaching the root + } + + // Perform path compression by updating parent pointers along the path to the root + // This optimization flattens the tree structure, reducing future lookup time + for value != currentParent { + nextParent := union.parents[value] + union.parents[value] = currentParent + value = nextParent + } + + return ¤tParent // Return a pointer to the root/representative +} + +// Union performs the union of two sets represented by the given values. +func (union *UnionFind) Union(valueOne, valueTwo int) { + _, parentFoundOne := union.parents[valueOne] + _, parentFoundTwo := union.parents[valueTwo] + if !parentFoundOne || !parentFoundTwo { + return // Return if either value is not found (not part of any set) + } + + valueOneRoot := *union.Find(valueOne) // Find the root of the set containing valueOne + valueTwoRoot := *union.Find(valueTwo) // Find the root of the set containing valueTwo + + if union.ranks[valueOneRoot] < union.ranks[valueTwoRoot] { + union.parents[valueOneRoot] = valueTwoRoot // Set the parent of valueOne's root to valueTwo's root + } else if union.ranks[valueOneRoot] > union.ranks[valueTwoRoot] { + union.parents[valueTwoRoot] = valueOneRoot // Set the parent of valueTwo's root to valueOne's root + } else { + union.parents[valueOneRoot] = valueTwoRoot // Set the parent of valueOne's root to valueTwo's root + union.ranks[valueTwoRoot] += 1 // Increment the rank of valueTwo's root + } +} + + +func main() { + // Create a new instance of UnionFind + union := NewUnionFind() + + // Create individual sets + union.CreateSet(1) + union.CreateSet(2) + union.CreateSet(3) + + // Perform union operations + union.Union(1, 2) + union.Union(2, 3) + + // Find representatives of values + representative := union.Find(3) + + // Check if the representative is found + if representative != nil { + fmt.Println("The representative of 3 is:", *representative) + } else { + fmt.Println("Value 3 is not found.") + } +} \ No newline at end of file diff --git a/Graphs/union_find.java b/Graphs/union_find.java new file mode 100644 index 00000000..0d3a4bcc --- /dev/null +++ b/Graphs/union_find.java @@ -0,0 +1,136 @@ +/* + Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. + This class should support three methods: + + + The union-find data structure is similar to a traditional set data structure in that it contains a collection + of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set + can have duplicate values, and no two sets can contain the same value. + + createSet(value) : : Adds a given value in a new set containing only that value. + + union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined + into a single set. If either value is not in a set or they are in the same set, the function should have no effect. + + find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should + always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function + should return null / none + + Explanation: + The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: + + - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of + each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. + + - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for + `parents` and `ranks`. + + - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and + initializes its rank to 0. + + - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the + given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to + update the parent pointers along the path to the root. Finally, it returns a pointer to the root. + + `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist + in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds + the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: + + If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s + root to `valueTwo`'s root. + + If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s + root to `valueOne`'s root. + + If the ranks are equal, it chooses one root as the parent and increments its rank by 1. + + By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. + + The time and space complexity of the operations in the `UnionFind` data structure are as follows: + + CreateSet : O(n) time O(1) space + Find : O(log(n)) time O(1) space where n is total number of values + Union : O(log(n)) time O(1) space where n is total number of values + +*/ +import java.util.HashMap; +import java.util.Map; + +class UnionFind { + private Map parents; // Map to store the parent of each element + private Map ranks; // Map to store the rank of each element + + public UnionFind() { + parents = new HashMap<>(); + ranks = new HashMap<>(); + } + + public void createSet(int value) { + parents.put(value, value); // Set the parent of the value to itself + ranks.put(value, 0); // Initialize the rank of the value to 0 + } + + public int find(int value) { + if (!parents.containsKey(value)) { + return -1; // Return -1 if the value is not found (not part of any set) + } + + int currentParent = value; + while (currentParent != parents.get(currentParent)) { + currentParent = parents.get(currentParent); // Traverse the parent pointers until reaching the root + } + + // Perform path compression by updating parent pointers along the path to the root + // This optimization flattens the tree structure, reducing future lookup time + while (value != currentParent) { + int nextParent = parents.get(value); + parents.put(value, currentParent); + value = nextParent; + } + + return currentParent; // Return the root/representative + } + + public void unionSets(int valueOne, int valueTwo) { + if (!parents.containsKey(valueOne) || !parents.containsKey(valueTwo)) { + return; // Return if either value is not found (not part of any set) + } + + int valueOneRoot = find(valueOne); // Find the root of the set containing valueOne + int valueTwoRoot = find(valueTwo); // Find the root of the set containing valueTwo + + if (ranks.get(valueOneRoot) < ranks.get(valueTwoRoot)) { + parents.put(valueOneRoot, valueTwoRoot); // Set the parent of valueOne's root to valueTwo's root + } else if (ranks.get(valueOneRoot) > ranks.get(valueTwoRoot)) { + parents.put(valueTwoRoot, valueOneRoot); // Set the parent of valueTwo's root to valueOne's root + } else { + parents.put(valueOneRoot, valueTwoRoot); // Set the parent of valueOne's root to valueTwo's root + ranks.put(valueTwoRoot, ranks.get(valueTwoRoot) + 1); // Increment the rank of valueTwo's root + } + } +} + +public class Main { + public static void main(String[] args) { + UnionFind unionFind = new UnionFind(); + + // Create individual sets + unionFind.createSet(1); + unionFind.createSet(2); + unionFind.createSet(3); + + // Perform union operations + unionFind.unionSets(1, 2); + unionFind.unionSets(2, 3); + + // Find representatives of values + int representative = unionFind.find(3); + + // Check if the representative is found + if (representative != -1) { + System.out.println("The representative of 3 is: " + representative); + } else { + System.out.println("Value 3 is not found."); + } + } +} diff --git a/Graphs/union_find.js b/Graphs/union_find.js new file mode 100644 index 00000000..ab5ae771 --- /dev/null +++ b/Graphs/union_find.js @@ -0,0 +1,127 @@ +/* + Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. + This class should support three methods: + + + The union-find data structure is similar to a traditional set data structure in that it contains a collection + of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set + can have duplicate values, and no two sets can contain the same value. + + createSet(value) : : Adds a given value in a new set containing only that value. + + union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined + into a single set. If either value is not in a set or they are in the same set, the function should have no effect. + + find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should + always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function + should return null / none + + Explanation: + The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: + + - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of + each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. + + - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for + `parents` and `ranks`. + + - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and + initializes its rank to 0. + + - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the + given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to + update the parent pointers along the path to the root. Finally, it returns a pointer to the root. + + `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist + in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds + the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: + + If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s + root to `valueTwo`'s root. + + If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s + root to `valueOne`'s root. + + If the ranks are equal, it chooses one root as the parent and increments its rank by 1. + + By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. + + The time and space complexity of the operations in the `UnionFind` data structure are as follows: + + CreateSet : O(n) time O(1) space + Find : O(log(n)) time O(1) space where n is total number of values + Union : O(log(n)) time O(1) space where n is total number of values + +*/ +class UnionFind { + constructor() { + this.parents = {}; // Map to store the parent of each element + this.ranks = {}; // Map to store the rank of each element + } + + createSet(value) { + this.parents[value] = value; // Set the parent of the value to itself + this.ranks[value] = 0; // Initialize the rank of the value to 0 + } + + find(value) { + if (!(value in this.parents)) { + return null; // Return null if the value is not found (not part of any set) + } + + let currentParent = value; + while (currentParent !== this.parents[currentParent]) { + currentParent = this.parents[currentParent]; // Traverse the parent pointers until reaching the root + } + + // Perform path compression by updating parent pointers along the path to the root + // This optimization flattens the tree structure, reducing future lookup time + while (value !== currentParent) { + const nextParent = this.parents[value]; + this.parents[value] = currentParent; + value = nextParent; + } + + return currentParent; // Return the root/representative + } + + union(valueOne, valueTwo) { + if (!(valueOne in this.parents) || !(valueTwo in this.parents)) { + return; // Return if either value is not found (not part of any set) + } + + const valueOneRoot = this.find(valueOne); // Find the root of the set containing valueOne + const valueTwoRoot = this.find(valueTwo); // Find the root of the set containing valueTwo + + if (this.ranks[valueOneRoot] < this.ranks[valueTwoRoot]) { + this.parents[valueOneRoot] = valueTwoRoot; // Set the parent of valueOne's root to valueTwo's root + } else if (this.ranks[valueOneRoot] > this.ranks[valueTwoRoot]) { + this.parents[valueTwoRoot] = valueOneRoot; // Set the parent of valueTwo's root to valueOne's root + } else { + this.parents[valueOneRoot] = valueTwoRoot; // Set the parent of valueOne's root to valueTwo's root + this.ranks[valueTwoRoot] += 1; // Increment the rank of valueTwo's root + } + } +} + +// Create a new instance of UnionFind +const union = new UnionFind(); + +// Create individual sets +union.createSet(1); +union.createSet(2); +union.createSet(3); + +// Perform union operations +union.union(1, 2); +union.union(2, 3); + +// Find representatives of values +const representative = union.find(3); + +// Check if the representative is found +if (representative !== null) { + console.log("The representative of 3 is:", representative); +} else { + console.log("Value 3 is not found."); +} diff --git a/Graphs/union_find.py b/Graphs/union_find.py new file mode 100644 index 00000000..83c185c4 --- /dev/null +++ b/Graphs/union_find.py @@ -0,0 +1,117 @@ +''' + Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. + This class should support three methods: + + + The union-find data structure is similar to a traditional set data structure in that it contains a collection + of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set + can have duplicate values, and no two sets can contain the same value. + + createSet(value) : : Adds a given value in a new set containing only that value. + + union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined + into a single set. If either value is not in a set or they are in the same set, the function should have no effect. + + find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should + always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function + should return null / none + + Explanation: + The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: + + - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of + each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. + + - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for + `parents` and `ranks`. + + - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and + initializes its rank to 0. + + - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the + given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to + update the parent pointers along the path to the root. Finally, it returns a pointer to the root. + + `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist + in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds + the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: + + If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s + root to `valueTwo`'s root. + + If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s + root to `valueOne`'s root. + + If the ranks are equal, it chooses one root as the parent and increments its rank by 1. + + By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. + + The time and space complexity of the operations in the `UnionFind` data structure are as follows: + + CreateSet : O(n) time O(1) space + Find : O(log(n)) time O(1) space where n is total number of values + Union : O(log(n)) time O(1) space where n is total number of values + +''' +class UnionFind: + def __init__(self): + self.parents = {} # Map to store the parent of each element + self.ranks = {} # Map to store the rank of each element + + def create_set(self, value): + self.parents[value] = value # Set the parent of the value to itself + self.ranks[value] = 0 # Initialize the rank of the value to 0 + + def find(self, value): + if value not in self.parents: + return None # Return None if the value is not found (not part of any set) + + current_parent = value + while current_parent != self.parents[current_parent]: + current_parent = self.parents[current_parent] # Traverse the parent pointers until reaching the root + + # Perform path compression by updating parent pointers along the path to the root + # This optimization flattens the tree structure, reducing future lookup time + while value != current_parent: + next_parent = self.parents[value] + self.parents[value] = current_parent + value = next_parent + + return current_parent # Return the root/representative + + def union(self, value_one, value_two): + if value_one not in self.parents or value_two not in self.parents: + return # Return if either value is not found (not part of any set) + + value_one_root = self.find(value_one) # Find the root of the set containing value_one + value_two_root = self.find(value_two) # Find the root of the set containing value_two + + if self.ranks[value_one_root] < self.ranks[value_two_root]: + self.parents[value_one_root] = value_two_root # Set the parent of value_one's root to value_two's root + elif self.ranks[value_one_root] > self.ranks[value_two_root]: + self.parents[value_two_root] = value_one_root # Set the parent of value_two's root to value_one's root + else: + self.parents[value_one_root] = value_two_root # Set the parent of value_one's root to value_two's root + self.ranks[value_two_root] += 1 # Increment the rank of value_two's root + + +# Create a new instance of UnionFind +union = UnionFind() + +# Create individual sets +union.create_set(1) +union.create_set(2) +union.create_set(3) + +# Perform union operations +union.union(1, 2) +union.union(2, 3) + +# Find representatives of values +representative = union.find(3) + +# Check if the representative is found +if representative is not None: + print("The representative of 3 is:", representative) +else: + print("Value 3 is not found.") diff --git a/Graphs/validate_bst.cpp b/Graphs/validate_bst.cpp new file mode 100644 index 00000000..2a63241a --- /dev/null +++ b/Graphs/validate_bst.cpp @@ -0,0 +1,83 @@ +/* + Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing + whether the BST is valid. + + Explanation: + + This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other + BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid + BST or not. + + The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid + BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values + that the current node's value can take in order to be a valid BST. + + The validateBST() method first checks whether the current node's value is within the valid range determined by the min and + max arguments. If not, the method returns false, indicating that the tree is not a valid BST. + + If the current node's value is within the valid range, the method then recursively calls itself on the left and right + child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. + + If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. + + O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST +*/ +#include +#include + +struct TreeNode { + int value; + TreeNode* left; + TreeNode* right; + + TreeNode(int x) : value(x), left(nullptr), right(nullptr) {} +}; + +// Function to check if the BST is valid +bool isValidBST(TreeNode* root) { + return isValidBSTHelper(root, std::numeric_limits::min(), std::numeric_limits::max()); +} + +// Recursive helper function to check if the BST is valid +bool isValidBSTHelper(TreeNode* node, long long minVal, long long maxVal) { + // Base case: if the current node's value is outside the allowed range, then the tree is invalid + if (!node || node->value <= minVal || node->value >= maxVal) { + return false; + } + + // Recursively check the left subtree, making sure all values are less than the current node's value + if (!isValidBSTHelper(node->left, minVal, node->value)) { + return false; + } + + // Recursively check the right subtree, making sure all values are greater than or equal to the current node's value + if (!isValidBSTHelper(node->right, node->value, maxVal)) { + return false; + } + + // If we reach this point, then the tree is valid + return true; +} + +int main() { + TreeNode* root = new TreeNode(10); + root->left = new TreeNode(5); + root->right = new TreeNode(15); + root->left->left = new TreeNode(2); + root->left->right = new TreeNode(7); + + if (isValidBST(root)) { + std::cout << "The BST is valid." << std::endl; + } else { + std::cout << "The BST is not valid." << std::endl; + } + + // Clean up memory + delete root->left->left; + delete root->left->right; + delete root->left; + delete root->right; + delete root; + + return 0; +} diff --git a/Graphs/validate_bst.go b/Graphs/validate_bst.go new file mode 100644 index 00000000..25d7a4fd --- /dev/null +++ b/Graphs/validate_bst.go @@ -0,0 +1,59 @@ +/* + Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing + whether the BST is valid. + + Explanation: + + This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other + BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid + BST or not. + + The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid + BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values + that the current node's value can take in order to be a valid BST. + + The validateBST() method first checks whether the current node's value is within the valid range determined by the min and + max arguments. If not, the method returns false, indicating that the tree is not a valid BST. + + If the current node's value is within the valid range, the method then recursively calls itself on the left and right + child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. + + If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. + + O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST +*/ +package main + +import "math" + +type BST struct { + Value int + + Left *BST + Right *BST +} + +// ValidateBst is a method of BST that checks if the binary search tree is valid +func (tree *BST) ValidateBst() bool { + return tree.validateBST(math.MinInt32, math.MaxInt32) +} + +// validateBST is a recursive helper function that checks if the binary search tree is valid +// min is the minimum value that a node in the subtree rooted at this node can have +// max is the maximum value that a node in the subtree rooted at this node can have +func (tree *BST) validateBST(min, max int) bool { + // if the current node's value is outside the allowed range, then the tree is invalid + if tree.Value < min || tree.Value >= max { + return false + } + // recursively check the left subtree, making sure all values are less than the current node's value + if tree.Left != nil && !tree.Left.validateBST(min, tree.Value) { + return false + } + // recursively check the right subtree, making sure all values are greater than or equal to the current node's value + if tree.Right != nil && !tree.Right.validateBST(tree.Value, max) { + return false + } + // if we reach this point, then the tree is valid + return true +} diff --git a/Graphs/validate_bst.java b/Graphs/validate_bst.java new file mode 100644 index 00000000..e213f5ae --- /dev/null +++ b/Graphs/validate_bst.java @@ -0,0 +1,80 @@ +/* + Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing + whether the BST is valid. + + Explanation: + + This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other + BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid + BST or not. + + The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid + BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values + that the current node's value can take in order to be a valid BST. + + The validateBST() method first checks whether the current node's value is within the valid range determined by the min and + max arguments. If not, the method returns false, indicating that the tree is not a valid BST. + + If the current node's value is within the valid range, the method then recursively calls itself on the left and right + child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. + + If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. + + O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST +*/ +import java.util.*; + +class TreeNode { + int value; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + value = x; + left = null; + right = null; + } +} + +public class ValidateBST { + + // Public method to check if the BST is valid + public static boolean isValidBST(TreeNode root) { + return isValidBSTHelper(root, Long.MIN_VALUE, Long.MAX_VALUE); + } + + // Private recursive helper function to check if the BST is valid + private static boolean isValidBSTHelper(TreeNode node, long minVal, long maxVal) { + // Base case: if the current node's value is outside the allowed range, then the tree is invalid + if (node == null || node.value < minVal || node.value >= maxVal) { + return false; + } + + // Recursively check the left subtree, making sure all values are less than the current node's value + if (!isValidBSTHelper(node.left, minVal, node.value)) { + return false; + } + + // Recursively check the right subtree, making sure all values are greater than or equal to the current node's value + if (!isValidBSTHelper(node.right, node.value, maxVal)) { + return false; + } + + // If we reach this point, then the tree is valid + return true; + } + + public static void main(String[] args) { + TreeNode root = new TreeNode(10); + root.left = new TreeNode(5); + root.right = new TreeNode(15); + root.left.left = new TreeNode(2); + root.left.right = new TreeNode(7); + + if (isValidBST(root)) { + System.out.println("The BST is valid."); + } else { + System.out.println("The BST is not valid."); + } + } +} diff --git a/Graphs/validate_bst.js b/Graphs/validate_bst.js new file mode 100644 index 00000000..cb93a6af --- /dev/null +++ b/Graphs/validate_bst.js @@ -0,0 +1,70 @@ +/* + Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing + whether the BST is valid. + + Explanation: + + This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other + BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid + BST or not. + + The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid + BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values + that the current node's value can take in order to be a valid BST. + + The validateBST() method first checks whether the current node's value is within the valid range determined by the min and + max arguments. If not, the method returns false, indicating that the tree is not a valid BST. + + If the current node's value is within the valid range, the method then recursively calls itself on the left and right + child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. + + If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. + + O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST +*/ +class TreeNode { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +// Function to check if the BST is valid +function isValidBST(root) { + return isValidBSTHelper(root, -Infinity, Infinity); +} + +// Recursive helper function to check if the BST is valid +function isValidBSTHelper(node, minVal, maxVal) { + // Base case: if the current node's value is outside the allowed range, then the tree is invalid + if (!node || node.value <= minVal || node.value >= maxVal) { + return false; + } + + // Recursively check the left subtree, making sure all values are less than the current node's value + if (!isValidBSTHelper(node.left, minVal, node.value)) { + return false; + } + + // Recursively check the right subtree, making sure all values are greater than or equal to the current node's value + if (!isValidBSTHelper(node.right, node.value, maxVal)) { + return false; + } + + // If we reach this point, then the tree is valid + return true; +} + +// Example usage: +const root = new TreeNode(10); +root.left = new TreeNode(5); +root.right = new TreeNode(15); +root.left.left = new TreeNode(2); +root.left.right = new TreeNode(7); + +if (isValidBST(root)) { + console.log("The BST is valid."); +} else { + console.log("The BST is not valid."); +} diff --git a/Graphs/validate_bst.py b/Graphs/validate_bst.py new file mode 100644 index 00000000..81247d3b --- /dev/null +++ b/Graphs/validate_bst.py @@ -0,0 +1,64 @@ +''' + Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing + whether the BST is valid. + + Explanation: + + This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other + BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid + BST or not. + + The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid + BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values + that the current node's value can take in order to be a valid BST. + + The validateBST() method first checks whether the current node's value is within the valid range determined by the min and + max arguments. If not, the method returns false, indicating that the tree is not a valid BST. + + If the current node's value is within the valid range, the method then recursively calls itself on the left and right + child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. + + If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. + + O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST +''' +import math + +class BST: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + + # Public method to check if the BST is valid + def validate_bst(self): + return self._validate_bst(-math.inf, math.inf) + + # Private recursive helper function to check if the BST is valid + def _validate_bst(self, min_val, max_val): + # Base case: if the current node's value is outside the allowed range, then the tree is invalid + if self.value < min_val or self.value >= max_val: + return False + + # Recursively check the left subtree, making sure all values are less than the current node's value + if self.left and not self.left._validate_bst(min_val, self.value): + return False + + # Recursively check the right subtree, making sure all values are greater than or equal to the current node's value + if self.right and not self.right._validate_bst(self.value, max_val): + return False + + # If we reach this point, then the tree is valid + return True + +# Example usage: +root = BST(10) +root.left = BST(5) +root.right = BST(15) +root.left.left = BST(2) +root.left.right = BST(7) + +if root.validate_bst(): + print("The BST is valid.") +else: + print("The BST is not valid.") diff --git a/Graphs/youngest_common_ancestor.cpp b/Graphs/youngest_common_ancestor.cpp new file mode 100644 index 00000000..c7ffde84 --- /dev/null +++ b/Graphs/youngest_common_ancestor.cpp @@ -0,0 +1,147 @@ +/* + + Write a function that returns the youngest common ancestor to the two descendants. + + Sample Input: + Top ancestor: node A + descendantOne: node e + descandantTwo: node I + + Output: node B + A + / \ + B C + / \ / \ + D E F G + / \ +H I + Explanation: + + This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. + + - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a + reference to its ancestor node. + + - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor + in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. + + - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the + depth and traverses through the ancestors until reaching the top ancestor. + + - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant + until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. + + The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. + The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. + + To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. + You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common + ancestor of two descendants. + + O(d) time | O(1) space - where d is the depth (height) of the ancestral tree +*/ +#include +#include +#include + +using namespace std; + +class AncestralTree { +public: + string name; + AncestralTree* ancestor; + + AncestralTree(string name) { + this->name = name; + this->ancestor = NULL; + } +}; + +// Function to calculate the depth of a descendant from the top ancestor +int getDescendantDepth(AncestralTree* descendant, AncestralTree* topAncestor) { + int depth = 0; + while (descendant != topAncestor) { + depth++; + descendant = descendant->ancestor; + } + return depth; +} + +// Function to backtrack and find the youngest common ancestor +AncestralTree* backTrackAncestralTree(AncestralTree* lowestDescendant, AncestralTree* higherDescendant, int diff) { + while (diff > 0) { + lowestDescendant = lowestDescendant->ancestor; + diff--; + } + while (lowestDescendant != higherDescendant) { + lowestDescendant = lowestDescendant->ancestor; + higherDescendant = higherDescendant->ancestor; + } + return lowestDescendant; +} + +// Function to find the youngest common ancestor of two descendants +AncestralTree* getYoungestCommonAncestor(AncestralTree* topAncestor, AncestralTree* descendantOne, AncestralTree* descendantTwo) { + int depthOne = getDescendantDepth(descendantOne, topAncestor); + int depthTwo = getDescendantDepth(descendantTwo, topAncestor); + + if (depthOne > depthTwo) { + return backTrackAncestralTree(descendantOne, descendantTwo, depthOne - depthTwo); + } + return backTrackAncestralTree(descendantTwo, descendantOne, depthTwo - depthOne); +} + +int main() { + // Create the ancestral tree + AncestralTree* topAncestor = new AncestralTree("A"); + AncestralTree* B = new AncestralTree("B"); + AncestralTree* C = new AncestralTree("C"); + AncestralTree* D = new AncestralTree("D"); + AncestralTree* E = new AncestralTree("E"); + AncestralTree* F = new AncestralTree("F"); + AncestralTree* G = new AncestralTree("G"); + AncestralTree* H = new AncestralTree("H"); + AncestralTree* I = new AncestralTree("I"); + + // Set up the ancestral relationships + + // A + // / \ + // B C + // / \ / \ + // D E F G + // / + // H + // / + // I + + topAncestor->ancestor = NULL; + B->ancestor = topAncestor; + C->ancestor = topAncestor; + D->ancestor = B; + E->ancestor = B; + F->ancestor = C; + G->ancestor = C; + H->ancestor = D; + I->ancestor = H; + + // Find the youngest common ancestor of two descendants + AncestralTree* descendantOne = F; + AncestralTree* descendantTwo = I; + AncestralTree* yca = getYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo); + + cout << "The youngest common ancestor of " << descendantOne->name << " and " << descendantTwo->name << " is " << yca->name << "." << endl; + + // Clean up memory + delete topAncestor; + delete B; + delete C; + delete D; + delete E; + delete F; + delete G; + delete H; + delete I; + + return 0; +} diff --git a/Graphs/youngest_common_ancestor.go b/Graphs/youngest_common_ancestor.go new file mode 100644 index 00000000..4d84149e --- /dev/null +++ b/Graphs/youngest_common_ancestor.go @@ -0,0 +1,110 @@ +/* + + Write a function that returns the youngest common ancestor to the two descendants. + + Sample Input: + Top ancestor: node A + descendantOne: node e + descandantTwo: node I + + Output: node B + A + / \ + B C + / \ / \ + D E F G + / \ +H I + Explanation: + + This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. + + - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a + reference to its ancestor node. + + - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor + in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. + + - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the + depth and traverses through the ancestors until reaching the top ancestor. + + - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant + until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. + + The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. + The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. + + To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. + You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common + ancestor of two descendants. + + O(d) time | O(1) space - where d is the depth (height) of the ancestral tree +*/ +package main + +import "fmt" + +type AncestralTree struct { + Name string + Ancestor *AncestralTree +} + +// GetYoungestCommonAncestor finds the youngest common ancestor of two descendants in an ancestral tree. +func GetYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo *AncestralTree) *AncestralTree { + // Calculate the depths of the two descendants from the top ancestor + depthOne := getDescendantDepth(descendantOne, topAncestor) + depthTwo := getDescendantDepth(descendantTwo, topAncestor) + + if depthOne > depthTwo { + // If depthOne is greater, backtrack the ancestral tree from descendantOne to align the depths + return backTrackAncestralTree(descendantOne, descendantTwo, depthOne-depthTwo) + } + + // If depthTwo is greater or both depths are equal, backtrack the ancestral tree from descendantTwo + // to align the depths + return backTrackAncestralTree(descendantTwo, descendantOne, depthTwo-depthOne) +} + +// getDescendantDepth calculates the depth of a descendant node from the top ancestor. +func getDescendantDepth(descendant, topAncestor *AncestralTree) int { + depth := 0 + for descendant != topAncestor { + depth++ + descendant = descendant.Ancestor + } + return depth +} + +// backTrackAncestralTree backtracks the ancestral tree to find the youngest common ancestor. +func backTrackAncestralTree(lowestDescendant, higherDescendant *AncestralTree, diff int) *AncestralTree { + // Adjust the position of the lowest descendant based on the depth difference + for diff > 0 { + lowestDescendant = lowestDescendant.Ancestor + diff-- + } + + // Move both descendants up the tree until they reach the same ancestor node + for lowestDescendant != higherDescendant { + lowestDescendant = lowestDescendant.Ancestor + higherDescendant = higherDescendant.Ancestor + } + + return lowestDescendant +} + + +func main() { + // Create the ancestral tree + topAncestor := &AncestralTree{Name: "A"} + B := &AncestralTree{Name: "B", Ancestor: topAncestor} + C := &AncestralTree{Name: "C", Ancestor: topAncestor} + D := &AncestralTree{Name: "D", Ancestor: B} + F := &AncestralTree{Name: "F", Ancestor: C} + I := &AncestralTree{Name: "I", Ancestor: D} + + descendantOne := F + descendantTwo := I + yca := GetYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo) + + fmt.Printf("The youngest common ancestor of %s and %s is %s.\n", descendantOne.Name, descendantTwo.Name, yca.Name) +} \ No newline at end of file diff --git a/Graphs/youngest_common_ancestor.java b/Graphs/youngest_common_ancestor.java new file mode 100644 index 00000000..6326cb9c --- /dev/null +++ b/Graphs/youngest_common_ancestor.java @@ -0,0 +1,129 @@ +/* + + Write a function that returns the youngest common ancestor to the two descendants. + + Sample Input: + Top ancestor: node A + descendantOne: node e + descandantTwo: node I + + Output: node B + A + / \ + B C + / \ / \ + D E F G + / \ +H I + Explanation: + + This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. + + - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a + reference to its ancestor node. + + - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor + in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. + + - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the + depth and traverses through the ancestors until reaching the top ancestor. + + - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant + until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. + + The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. + The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. + + To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. + You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common + ancestor of two descendants. + + O(d) time | O(1) space - where d is the depth (height) of the ancestral tree +*/ +class AncestralTree { + String name; + AncestralTree ancestor; + + public AncestralTree(String name) { + this.name = name; + this.ancestor = null; + } +} + +public class Main { + // Function to calculate the depth of a descendant from the top ancestor + public static int getDescendantDepth(AncestralTree descendant, AncestralTree topAncestor) { + int depth = 0; + while (descendant != topAncestor) { + depth++; + descendant = descendant.ancestor; + } + return depth; + } + + // Function to backtrack and find the youngest common ancestor + public static AncestralTree backTrackAncestralTree(AncestralTree lowestDescendant, AncestralTree higherDescendant, int diff) { + while (diff > 0) { + lowestDescendant = lowestDescendant.ancestor; + diff--; + } + while (lowestDescendant != higherDescendant) { + lowestDescendant = lowestDescendant.ancestor; + higherDescendant = higherDescendant.ancestor; + } + return lowestDescendant; + } + + // Function to find the youngest common ancestor of two descendants + public static AncestralTree getYoungestCommonAncestor(AncestralTree topAncestor, AncestralTree descendantOne, AncestralTree descendantTwo) { + int depthOne = getDescendantDepth(descendantOne, topAncestor); + int depthTwo = getDescendantDepth(descendantTwo, topAncestor); + + if (depthOne > depthTwo) { + return backTrackAncestralTree(descendantOne, descendantTwo, depthOne - depthTwo); + } + return backTrackAncestralTree(descendantTwo, descendantOne, depthTwo - depthOne); + } + + public static void main(String[] args) { + // Create the ancestral tree + AncestralTree topAncestor = new AncestralTree("A"); + AncestralTree B = new AncestralTree("B"); + AncestralTree C = new AncestralTree("C"); + AncestralTree D = new AncestralTree("D"); + AncestralTree E = new AncestralTree("E"); + AncestralTree F = new AncestralTree("F"); + AncestralTree G = new AncestralTree("G"); + AncestralTree H = new AncestralTree("H"); + AncestralTree I = new AncestralTree("I"); + + // Set up the ancestral relationships + + // A + // / \ + // B C + // / \ / \ + // D E F G + // / + // H + // / + // I + + topAncestor.ancestor = null; + B.ancestor = topAncestor; + C.ancestor = topAncestor; + D.ancestor = B; + E.ancestor = B; + F.ancestor = C; + G.ancestor = C; + H.ancestor = D; + I.ancestor = H; + + // Find the youngest common ancestor of two descendants + AncestralTree descendantOne = F; + AncestralTree descendantTwo = I; + AncestralTree yca = getYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo); + + System.out.println("The youngest common ancestor of " + descendantOne.name + " and " + descendantTwo.name + " is " + yca.name); + } +} diff --git a/Graphs/youngest_common_ancestor.js b/Graphs/youngest_common_ancestor.js new file mode 100644 index 00000000..9ce55c83 --- /dev/null +++ b/Graphs/youngest_common_ancestor.js @@ -0,0 +1,141 @@ +/* + + Write a function that returns the youngest common ancestor to the two descendants. + + Sample Input: + Top ancestor: node A + descendantOne: node e + descandantTwo: node I + + Output: node B + A + / \ + B C + / \ / \ + D E F G + / \ +H I + Explanation: + + This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. + + - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a + reference to its ancestor node. + + - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor + in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. + + - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the + depth and traverses through the ancestors until reaching the top ancestor. + + - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant + until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. + + The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. + The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. + + To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. + You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common + ancestor of two descendants. + + O(d) time | O(1) space - where d is the depth (height) of the ancestral tree +*/ +class AncestralTree { + constructor(name) { + this.name = name; + this.ancestor = null; + } +} + +// Function to calculate the depth of a descendant from the top ancestor +function getDescendantDepth(descendant, topAncestor) { + let depth = 0; + while (descendant !== topAncestor) { + depth += 1; + descendant = descendant.ancestor; + } + return depth; +} + +// Function to backtrack and find the youngest common ancestor +function backTrackAncestralTree(lowestDescendant, higherDescendant, diff) { + while (diff > 0) { + lowestDescendant = lowestDescendant.ancestor; + diff -= 1; + } + while (lowestDescendant !== higherDescendant) { + lowestDescendant = lowestDescendant.ancestor; + higherDescendant = higherDescendant.ancestor; + } + return lowestDescendant; +} + +// Function to find the youngest common ancestor of two descendants +function getYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo) { + const depthOne = getDescendantDepth(descendantOne, topAncestor); + const depthTwo = getDescendantDepth(descendantTwo, topAncestor); + + if (depthOne > depthTwo) { + return backTrackAncestralTree( + descendantOne, + descendantTwo, + depthOne - depthTwo + ); + } + return backTrackAncestralTree( + descendantTwo, + descendantOne, + depthTwo - depthOne + ); +} + +// Create the ancestral tree +const topAncestor = new AncestralTree("A"); +const B = new AncestralTree("B"); +const C = new AncestralTree("C"); +const D = new AncestralTree("D"); +const E = new AncestralTree("E"); +const F = new AncestralTree("F"); +const G = new AncestralTree("G"); +const H = new AncestralTree("H"); +const I = new AncestralTree("I"); + +// Set up the ancestral relationships + +// A +// / \ +// B C +// / \ / \ +// D E F G +// / +// H +// / +// I + +topAncestor.ancestor = null; +B.ancestor = topAncestor; +C.ancestor = topAncestor; +D.ancestor = B; +E.ancestor = B; +F.ancestor = C; +G.ancestor = C; +H.ancestor = D; +I.ancestor = H; + +// Find the youngest common ancestor of two descendants +const descendantOne = F; +const descendantTwo = I; +const yca = getYoungestCommonAncestor( + topAncestor, + descendantOne, + descendantTwo +); + +console.log( + "The youngest common ancestor of", + descendantOne.name, + "and", + descendantTwo.name, + "is", + yca.name +); diff --git a/Graphs/youngest_common_ancestor.py b/Graphs/youngest_common_ancestor.py new file mode 100644 index 00000000..f48cf6c6 --- /dev/null +++ b/Graphs/youngest_common_ancestor.py @@ -0,0 +1,112 @@ +''' + Write a function that returns the youngest common ancestor to the two descendants. + + Sample Input: + Top ancestor: node A + descendantOne: node e + descandantTwo: node I + + Output: node B + A + / \ + B C + / \ / \ + D E F G + / \ + H I + Explanation: + + This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. + + - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a + reference to its ancestor node. + + - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor + in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. + + - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the + depth and traverses through the ancestors until reaching the top ancestor. + + - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant + until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. + + The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. + The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. + + To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. + You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common + ancestor of two descendants. + + O(d) time | O(1) space - where d is the depth (height) of the ancestral tree +''' +class AncestralTree: + def __init__(self, name): + self.name = name + self.ancestor = None + +# Function to calculate the depth of a descendant from the top ancestor +def get_descendant_depth(descendant, top_ancestor): + depth = 0 + while descendant != top_ancestor: + depth += 1 + descendant = descendant.ancestor + return depth + +# Function to backtrack and find the youngest common ancestor +def back_track_ancestral_tree(lowest_descendant, higher_descendant, diff): + while diff > 0: + lowest_descendant = lowest_descendant.ancestor + diff -= 1 + while lowest_descendant != higher_descendant: + lowest_descendant = lowest_descendant.ancestor + higher_descendant = higher_descendant.ancestor + return lowest_descendant + +# Function to find the youngest common ancestor of two descendants +def get_youngest_common_ancestor(top_ancestor, descendant_one, descendant_two): + depth_one = get_descendant_depth(descendant_one, top_ancestor) + depth_two = get_descendant_depth(descendant_two, top_ancestor) + + if depth_one > depth_two: + return back_track_ancestral_tree(descendant_one, descendant_two, depth_one - depth_two) + return back_track_ancestral_tree(descendant_two, descendant_one, depth_two - depth_one) + +# Create the ancestral tree +top_ancestor = AncestralTree("A") +B = AncestralTree("B") +C = AncestralTree("C") +D = AncestralTree("D") +E = AncestralTree("E") +F = AncestralTree("F") +G = AncestralTree("G") +H = AncestralTree("H") +I = AncestralTree("I") + +# Set up the ancestral relationships + +# A +# / \ +# B C +# / \ / \ +# D E F G +# / +# H +# / +# I + +top_ancestor.ancestor = None +B.ancestor = top_ancestor +C.ancestor = top_ancestor +D.ancestor = B +E.ancestor = B +F.ancestor = C +G.ancestor = C +H.ancestor = D +I.ancestor = H + +# Find the youngest common ancestor of two descendants +descendant_one = F +descendant_two = I +yca = get_youngest_common_ancestor(top_ancestor, descendant_one, descendant_two) + +print("The youngest common ancestor of", descendant_one.name, "and", descendant_two.name, "is", yca.name) diff --git a/Greedy/coin_change.go b/Greedy/coin_change.go new file mode 100644 index 00000000..c91cc9d9 --- /dev/null +++ b/Greedy/coin_change.go @@ -0,0 +1,37 @@ +/* + The coin change problem is a classic algorithmic problem, where given a target amount and a set of coin + denominations, we need to find the minimum number of coins required to make up that amount. + + In this implementation, we start by sorting the array of coin denominations in descending order. + We then iterate through the array from largest to smallest denomination, and for each denomination, + we repeatedly subtract the largest possible multiple of that denomination from the target amount + until we reach 0. + + The time complexity of this algorithm is O(n log n) due to the sorting step, where n is the number + of coin denominations. However, the space complexity is O(1) since we are only using a constant + amount of extra space. + + For the sample input coins := []int{1, 5, 10, 25} and target := 36, the output should be Minimum + number of coins required to make 36 cents: 3. +*/ +package main + +import "fmt" + +func coinChangeGreedy(coins []int, target int) int { + count := 0 + for i := len(coins) - 1; i >= 0; i-- { + for target >= coins[i] { + target -= coins[i] + count++ + } + } + return count +} + +func main() { + coins := []int{1, 5, 10, 25} + target := 36 + result := coinChangeGreedy(coins, target) + fmt.Printf("Minimum number of coins required to make %d cents: %d\n", target, result) +} diff --git a/Greedy/coin_change.java b/Greedy/coin_change.java new file mode 100644 index 00000000..b8a7d4a8 --- /dev/null +++ b/Greedy/coin_change.java @@ -0,0 +1,60 @@ +package Greedy; + + /* + * write a code in java with comments foor As a first example, + * we consider a problem where we are given a set of coins and our task is to + * form a sum of money n using the coins. The values of the coins are coins = {c1, c2,..., ck}, + * and each coin can be used as many times we want. What is the minimum number of coins needed? + * For example, if the coins are the euro coins (in cents) {1,2,5,10,20,50,100,200} and n = 520, + * we need at least four coins. The optimal solution is to select coins 200 + 200 + 100 + 20 whose sum is 520 + */ +import java.util.Arrays; + +public class CoinChange { + + /** + * This method finds the minimum number of coins required to make change for a given amount of money using a given set of coins. + * + * coins The set of coins that can be used to make change. + * n The amount of money to make change for. + * @return The minimum number of coins required to make change for n. + */ + public static int minCoins(int[] coins, int n) { + // Create an array to store the minimum number of coins required to make change for each amount of money. + int[] dp = new int[n + 1]; + + // Initialize the array to Integer.MAX_VALUE. This means that we do not know the minimum number of coins required to make change for any amount of money yet. + Arrays.fill(dp, Integer.MAX_VALUE); + + // Set the minimum number of coins required to make change for 0 to 0. + dp[0] = 0; + + // Iterate over the coins array. + for (int i = 0; i < coins.length; i++) { + // Iterate over the dp array. + for (int j = coins[i]; j <= n; j++) { + // Update the dp[j] element to be the minimum of dp[j] and dp[j - coins[i]] + 1. + dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); + } + } + + // Return the minimum number of coins required to make change for n. + return dp[n]; + } + + public static void main(String[] args) { + // Create a set of coins. + int[] coins = {1, 2, 5, 10, 20, 50, 100, 200}; + + // The amount of money to make change for. + int n = 520; + + // Find the minimum number of coins required to make change for n. + int minCoins = minCoins(coins, n); + + // Print the minimum number of coins. + System.out.println("The minimum number of coins required to make change for " + n + " cents is " + minCoins); + } +} + +// The minimum number of coins required to make change for 520 cents is 4 diff --git a/Greedy/coin_change.js b/Greedy/coin_change.js new file mode 100644 index 00000000..399b3e67 --- /dev/null +++ b/Greedy/coin_change.js @@ -0,0 +1,38 @@ + +function coinChangeGreedy(s, l) { + // First step: sort l in descending order + l.sort((a, b) => b - a); + + let r = []; // Result list + let sumr = 0; // Keep track of the current sum of r to avoid iterating over r to calculate the sum + + for (let coin of l) { + if (sumr === s) { // The sum of r is the target sum s + break; + } + + let n = Math.floor((s - sumr) / coin); // Calculate the max n with sum(r) + l[i] * n <= s + + for (let i = 0; i < n; i++) { // Append n elements of this coin to r + r.push(coin); + } + + sumr += coin * n; // Update the sum of r + } + + if (sumr !== s) { // The target sum s was not reached + return false; + } + + return r; + } + + // SAMPLE 1 (optimal) + console.log(coinChangeGreedy(60, [5, 10, 25])); + + // SAMPLE 2 (not optimal) + console.log(coinChangeGreedy(50, [25, 10, 30, 5])); + + // SAMPLE 3 (fails) + console.log(coinChangeGreedy(50, [25, 30])); + \ No newline at end of file diff --git a/Greedy/coin_change.py b/Greedy/coin_change.py new file mode 100644 index 00000000..1c848418 --- /dev/null +++ b/Greedy/coin_change.py @@ -0,0 +1,149 @@ +"""TASK + Greedy: provide a greedy solution for the coin change problem + A greedy algorithm is trying to make the best local decisions, without caring about future decisions. + That means that a greedy algorithm is faster than better approaches but it has not always the best solution + In some case a greedy approach does not find a solution at all. + + The coin change problem: + -> given sum s + -> given list of possible coin types l + + The output has to be a list r, which's elements represent coins from coin types from l. + r[i] is in l for every i with [0 <= i < length r]; + The sum of r has to be s; + Furthermore the length of r has to be minimal; + + In other words we need to calculate a way to reach the sum s with minimal coins from coin types l + + +SAMPLE DATA + Take a look on the following Samples + Sample A (good for greedy): + s = 60 + l = [25, 10, 5]; + Best output r = [25,25,10] (take 2 coins from type 25 and one 10, we get 60 with only three coins) + + Sample B (bad for greedy): + s = 50; + l = [30, 25, 10, 5] + Best output r = [25,25] (take 2 coins from type 25, we get 50 with only two coins) + + Sample C (greedy fails) + s = 50 + l = [30, 25] + Best output r = [25,25] (take 2 coins from type 25, we get 50 with only two coins)decisions + +APPROACH + The greedy approach is very simple + 1. We have to sort l in descendig order + 2. Iterate over the sorted list, in each step: + 1. if the sum of r is equal to s, break + 2. add n coins of type l[i] to r where n is maximal and sum(r) + l[i]*n <= s (add as many as we can from this coins type) + 3. if the sum of r is equal to s, return r; otherwise the greedy algorithm has failed + +SAMPLE A + 1. l in descending order l = [25,10,5] + 2. loop over l (element 25, r=[]) + 3. take 2 25 coins because 0+25*2 <= 60 but not 0+25*3 <= 60 (sum(r)+l[i]*n <= s) + 4. next iteration (elment 10, r=[25,25]) + 5. take 1 10 coin because 50+10*1 <= 60 (r=[25,25,10]) + 6. sumr == s => break + 7. return r + +SAMPLE B + the greedy takes one 30 coin, a 25 coin cannot be taken (30+25 > 25), but 2 10 coins + r = [30, 10, 10] + the number of coins is 3, but the optimal solution is 2 [25,25]. + A greedy algorithm does not provide the optimnal solution + +SAMPLE C + the greedy takes the 30 coin. But this is a bad decision for the future, because the 25 coin cannot be taken (30+25 > 50) + that means that the greedy does not provide any solution because a bad decision was made. + To get this solution you have to do a dynamic programmic approach + +COMPLEXITY + With sorting (random list l): + sorting takes log(l)*l time. + looping over l takes O(l) time + looping over n takes O(n) time + thats a time complexity of O(l*logl + l*n) + n is dependend on s and l (for a big s and small l values n gets big, for heavily skewed l values too [1000000,1] for s = 1999999) + that means that time complexity class will be O(n ^ 2) in worst case (because n is linear to s) + If it is assumed that n is always in a certain range (i.e below 10), the time complexity class will be O(n*logn) + + sorting has O(l) space complexity + the space complexity for the whole algorithm is O(l+r) with r being the length of r. + Best case space complexity class is O(n) where r is assumed very small (linear or better) + Otherwise the space complexity is O(r). r is dependend on s (linear): + s = 9 + l = [10, 1] + r = [1,1,1,1,1,1,1,1,1] which is O(s) + + that means that space complexity class is O(n) + + Without sorting (sorted list l): + looping over l takes O(l) time + looping over n takes O(n) time + thats a time complexity of O(l*n) + n is dependend on s and l (linear) + that means that time complexity class will be O(n ^ 2) in worst case + If it is assumed that n is always in a certain range (i.e below 10), the time complexity class will be O(n) + + the space complexity for the whole algorithm is O(r) with r being the length of r. + Best case space complexity class is O(1) where r is assumed very small (linear or better) + Otherwise the space complexity is O(n) r is dependend on s (linear) + + that means that space complexity class is O(n) (or O(1) in best case) +""" + +def coinChangeGreedy(s:int, l:list[int]): + #first step: sort l descending + l.sort(reverse=True) #uses some log(n)*n algorithm + r = [] #return list + sumr:int = 0 #keep track of the current sum of r to avoid iterating over r to calculate the sum + for coin in l: + if(sumr == s): #the sum of r is the target sum s + break + n = int((s - sumr)/coin) #calculate the max n with sum(r) + l[i]*n <= s + for i in range(n): #append n elements of this coin to r + r.append(coin) + sumr += coin*n #update the r sum + if(sumr != s): #the target sum s was not reached + return False + return r + +#without sorting +def coinChangeGreedySorted(s:int, l:list[int]): + r = [] #return list + sumr:int = 0 #keep track of the current sum of r to avoid iterating over r to calculate the sum + for coin in l: + if(sumr == s): #the sum of r is the target sum s + break + n = int((s - sumr)/coin) #calculate the max n with sum(r) + l[i]*n <= s + for i in range(n): #append n elements of this coin to r + r.append(coin) + sumr += coin*n #update the r sum + if(sumr != s): #the target sum s was not reached + return False + return r + + +#SAMPLE 1 (optimal) +print(coinChangeGreedy(60, [5, 10, 25])) + +#SAMPLE 2 (not optimal) +print(coinChangeGreedy(50, [25, 10, 30, 5])) + +#SAMPLE 3 (fails) +print(coinChangeGreedy(50, [25, 30])) + +print() + +#SAMPLE 1 (optimal) +print(coinChangeGreedySorted(60, [25, 10, 5])) + +#SAMPLE 2 (not optimal) +print(coinChangeGreedySorted(50, [30, 25, 10, 5])) + +#SAMPLE 3 (fails) +print(coinChangeGreedySorted(50, [30, 25])) \ No newline at end of file diff --git a/Greedy/task_assignment.cpp b/Greedy/task_assignment.cpp new file mode 100644 index 00000000..8a436ebb --- /dev/null +++ b/Greedy/task_assignment.cpp @@ -0,0 +1,100 @@ +/* + + You're given an integer k representing a number of workers and an array of positive integers representing durations of + tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work + on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks + to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their + assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete + the longest pair of tasks (see the sample output for an explanation). + + + Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast + as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should + be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and + task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any + correct answer will be accepted. + + Sample Input: K = 3 + tasks : [1, 3, 5, 3, 1, 4] + + Output: [ + [0, 2], + [4, 5], + [1, 3] + ] + + Explanation: + The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. + + - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). + - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is + a map that will store the indices of each task duration. + - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task + duration to a list of indices. + - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and + longest durations together efficiently. + - The loop runs `k` times to pair tasks. In each iteration: + - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. + - The index of the selected task is removed from the list of indices to ensure it is not paired again. + - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. + - The paired task indices are added to `pairedTasks`. + - Finally, `pairedTasks` is returned as the result. + + + The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each + task duration to a list of indices where tasks with that duration occur in the `tasks` list. + + Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. + The result is a list of paired task indices. + + O(nlog(n)) time | O(n) space - where n is the number of tasks +*/ +#include +#include +#include + +using namespace std; + +// Function to create a map that maps each task duration to a list of indices +// where tasks with that duration occur in the given tasks list. +unordered_map> getTaskDurationToIndices(vector& tasks) { + unordered_map> taskDurationToIndices; + + for (int idx = 0; idx < tasks.size(); idx++) { + int taskDuration = tasks[idx]; + taskDurationToIndices[taskDuration].push_back(idx); + } + + return taskDurationToIndices; +} + +// Function to pair tasks from the given list based on their durations. +// It returns a vector of paired task indices. +vector> TaskAssignment(int k, vector& tasks) { + // Initialize variables + vector> pairedTasks; + unordered_map> taskDurationToIndices = getTaskDurationToIndices(tasks); + sort(tasks.begin(), tasks.end()); + int task1Idx, task2Idx; + + // Pair tasks + for (int idx = 0; idx < k; idx++) { + // Get the shortest duration task + int task1Duration = tasks[idx]; + vector& indicesWithTask1Duration = taskDurationToIndices[task1Duration]; + task1Idx = indicesWithTask1Duration.back(); + indicesWithTask1Duration.pop_back(); + + // Get the longest duration task + int task2SortedIndex = tasks.size() - 1 - idx; + int task2Duration = tasks[task2SortedIndex]; + vector& indicesWithTask2Duration = taskDurationToIndices[task2Duration]; + task2Idx = indicesWithTask2Duration.back(); + indicesWithTask2Duration.pop_back(); + + // Add the paired tasks to the result + pairedTasks.push_back({task1Idx, task2Idx}); + } + + return pairedTasks; +} diff --git a/Greedy/task_assignment.go b/Greedy/task_assignment.go new file mode 100644 index 00000000..1270ab1c --- /dev/null +++ b/Greedy/task_assignment.go @@ -0,0 +1,94 @@ +/* + + You're given an integer k representing a number of workers and an array of positive integers representing durations of + tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work + on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks + to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their + assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete + the longest pair of tasks (see the sample output for an explanation). + + + Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast + as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should + be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and + task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any + correct answer will be accepted. + + Sample Input: K = 3 + tasks : [1, 3, 5, 3, 1, 4] + + Output: [ + [0, 2], + [4, 5], + [1, 3] + ] + + Explanation: + The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. + + - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). + - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is + a map that will store the indices of each task duration. + - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task + duration to a list of indices. + - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and + longest durations together efficiently. + - The loop runs `k` times to pair tasks. In each iteration: + - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. + - The index of the selected task is removed from the list of indices to ensure it is not paired again. + - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. + - The paired task indices are added to `pairedTasks`. + - Finally, `pairedTasks` is returned as the result. + + + The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each + task duration to a list of indices where tasks with that duration occur in the `tasks` list. + + Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. + The result is a list of paired task indices. + + O(nlog(n)) time | O(n) space - where n is the number of tasks +*/ +import "sort" + +// TaskAssignment pairs tasks from the given list based on their durations. +// It returns a list of paired task indices. +func TaskAssignment(k int, tasks []int) [][]int { + // Initialize variables + pairedTasks := make([][]int, 0) + taskDurationToIndices := getTaskDurationToIndices(tasks) + sort.Ints(tasks) + var task1Idx, task2Idx int + + // Pair tasks + for idx := 0; idx < k; idx++ { + // Get the shortest duration task + task1Duration := tasks[idx] + indicesWithTask1Duration := taskDurationToIndices[task1Duration] + task1Idx, taskDurationToIndices[task1Duration] = indicesWithTask1Duration[len(indicesWithTask1Duration)-1], indicesWithTask1Duration[:len(indicesWithTask1Duration)-1] + + // Get the longest duration task + task2SortedIndex := len(tasks) - 1 - idx + task2Duration := tasks[task2SortedIndex] + indicesWithTask2Duration := taskDurationToIndices[task2Duration] + task2Idx, taskDurationToIndices[task2Duration] = indicesWithTask2Duration[len(indicesWithTask2Duration)-1], indicesWithTask2Duration[:len(indicesWithTask2Duration)-1] + + // Add the paired tasks to the result + pairedTasks = append(pairedTasks, []int{task1Idx, task2Idx}) + } + + return pairedTasks +} + +// getTaskDurationToIndices creates a map that maps each task duration to a list of indices +// where tasks with that duration occur in the given tasks list. +func getTaskDurationToIndices(tasks []int) map[int][]int { + taskDurationToIndices := map[int][]int{} + + for idx := range tasks { + taskDuration := tasks[idx] + taskDurationToIndices[taskDuration] = append(taskDurationToIndices[taskDuration], idx) + } + + return taskDurationToIndices +} diff --git a/Greedy/task_assignment.java b/Greedy/task_assignment.java new file mode 100644 index 00000000..4d551004 --- /dev/null +++ b/Greedy/task_assignment.java @@ -0,0 +1,110 @@ +/* + + You're given an integer k representing a number of workers and an array of positive integers representing durations of + tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work + on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks + to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their + assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete + the longest pair of tasks (see the sample output for an explanation). + + + Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast + as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should + be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and + task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any + correct answer will be accepted. + + Sample Input: K = 3 + tasks : [1, 3, 5, 3, 1, 4] + + Output: [ + [0, 2], + [4, 5], + [1, 3] + ] + + Explanation: + The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. + + - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). + - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is + a map that will store the indices of each task duration. + - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task + duration to a list of indices. + - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and + longest durations together efficiently. + - The loop runs `k` times to pair tasks. In each iteration: + - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. + - The index of the selected task is removed from the list of indices to ensure it is not paired again. + - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. + - The paired task indices are added to `pairedTasks`. + - Finally, `pairedTasks` is returned as the result. + + + The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each + task duration to a list of indices where tasks with that duration occur in the `tasks` list. + + Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. + The result is a list of paired task indices. + + O(nlog(n)) time | O(n) space - where n is the number of tasks +*/ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TaskAssignment { + + public static int[][] taskAssignment(int k, int[] tasks) { + // Initialize variables + List pairedTasks = new ArrayList<>(); + Map> taskDurationToIndices = getTaskDurationToIndices(tasks); + Arrays.sort(tasks); + int task1Idx, task2Idx; + + // Pair tasks + for (int idx = 0; idx < k; idx++) { + // Get the shortest duration task + int task1Duration = tasks[idx]; + List indicesWithTask1Duration = taskDurationToIndices.get(task1Duration); + task1Idx = indicesWithTask1Duration.remove(indicesWithTask1Duration.size() - 1); + + // Get the longest duration task + int task2SortedIndex = tasks.length - 1 - idx; + int task2Duration = tasks[task2SortedIndex]; + List indicesWithTask2Duration = taskDurationToIndices.get(task2Duration); + task2Idx = indicesWithTask2Duration.remove(indicesWithTask2Duration.size() - 1); + + // Add the paired tasks to the result + pairedTasks.add(new int[]{task1Idx, task2Idx}); + } + + return pairedTasks.toArray(new int[0][2]); + } + + private static Map> getTaskDurationToIndices(int[] tasks) { + Map> taskDurationToIndices = new HashMap<>(); + + for (int idx = 0; idx < tasks.length; idx++) { + int taskDuration = tasks[idx]; + List indices = taskDurationToIndices.getOrDefault(taskDuration, new ArrayList<>()); + indices.add(idx); + taskDurationToIndices.put(taskDuration, indices); + } + + return taskDurationToIndices; + } + + public static void main(String[] args) { + int k = 3; + int[] tasks = {1, 3, 5, 3, 1, 4}; + int[][] pairedTasks = taskAssignment(k, tasks); + + // Print the paired tasks + for (int[] pair : pairedTasks) { + System.out.println(Arrays.toString(pair)); + } + } +} diff --git a/Greedy/task_assignment.js b/Greedy/task_assignment.js new file mode 100644 index 00000000..211ef209 --- /dev/null +++ b/Greedy/task_assignment.js @@ -0,0 +1,100 @@ +/* + + You're given an integer k representing a number of workers and an array of positive integers representing durations of + tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work + on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks + to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their + assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete + the longest pair of tasks (see the sample output for an explanation). + + + Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast + as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should + be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and + task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any + correct answer will be accepted. + + Sample Input: K = 3 + tasks : [1, 3, 5, 3, 1, 4] + + Output: [ + [0, 2], + [4, 5], + [1, 3] + ] + + Explanation: + The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. + + - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). + - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is + a map that will store the indices of each task duration. + - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task + duration to a list of indices. + - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and + longest durations together efficiently. + - The loop runs `k` times to pair tasks. In each iteration: + - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. + - The index of the selected task is removed from the list of indices to ensure it is not paired again. + - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. + - The paired task indices are added to `pairedTasks`. + - Finally, `pairedTasks` is returned as the result. + + + The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each + task duration to a list of indices where tasks with that duration occur in the `tasks` list. + + Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. + The result is a list of paired task indices. + + O(nlog(n)) time | O(n) space - where n is the number of tasks +*/ +function taskAssignment(k, tasks) { + // Initialize variables + const pairedTasks = []; + const taskDurationToIndices = getTaskDurationToIndices(tasks); + tasks.sort((a, b) => a - b); + let task1Idx, task2Idx; + + // Pair tasks + for (let idx = 0; idx < k; idx++) { + // Get the shortest duration task + const task1Duration = tasks[idx]; + const indicesWithTask1Duration = taskDurationToIndices[task1Duration]; + task1Idx = indicesWithTask1Duration.pop(); + + // Get the longest duration task + const task2SortedIndex = tasks.length - 1 - idx; + const task2Duration = tasks[task2SortedIndex]; + const indicesWithTask2Duration = taskDurationToIndices[task2Duration]; + task2Idx = indicesWithTask2Duration.pop(); + + // Add the paired tasks to the result + pairedTasks.push([task1Idx, task2Idx]); + } + + return pairedTasks; +} + +function getTaskDurationToIndices(tasks) { + const taskDurationToIndices = {}; + + for (let idx = 0; idx < tasks.length; idx++) { + const taskDuration = tasks[idx]; + if (!taskDurationToIndices[taskDuration]) { + taskDurationToIndices[taskDuration] = []; + } + taskDurationToIndices[taskDuration].push(idx); + } + + return taskDurationToIndices; +} + +const k = 3; +const tasks = [1, 3, 5, 3, 1, 4]; +const pairedTasks = taskAssignment(k, tasks); + +// Print the paired tasks +pairedTasks.forEach((pair) => { + console.log(pair); +}); diff --git a/Greedy/task_assignment.py b/Greedy/task_assignment.py new file mode 100644 index 00000000..13ea0346 --- /dev/null +++ b/Greedy/task_assignment.py @@ -0,0 +1,94 @@ +''' + + You're given an integer k representing a number of workers and an array of positive integers representing durations of + tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work + on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks + to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their + assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete + the longest pair of tasks (see the sample output for an explanation). + + + Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast + as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should + be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and + task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any + correct answer will be accepted. + + Sample Input: K = 3 + tasks : [1, 3, 5, 3, 1, 4] + + Output: [ + [0, 2], + [4, 5], + [1, 3] + ] + + Explanation: + The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. + + - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). + - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is + a map that will store the indices of each task duration. + - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task + duration to a list of indices. + - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and + longest durations together efficiently. + - The loop runs `k` times to pair tasks. In each iteration: + - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. + - The index of the selected task is removed from the list of indices to ensure it is not paired again. + - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. + - The paired task indices are added to `pairedTasks`. + - Finally, `pairedTasks` is returned as the result. + + + The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each + task duration to a list of indices where tasks with that duration occur in the `tasks` list. + + Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. + The result is a list of paired task indices. + + O(nlog(n)) time | O(n) space - where n is the number of tasks +''' + +def task_assignment(k, tasks): + # Initialize variables + paired_tasks = [] + task_duration_to_indices = get_task_duration_to_indices(tasks) + tasks.sort() + + for idx in range(k): + # Get the shortest duration task + task1_duration = tasks[idx] + indices_with_task1_duration = task_duration_to_indices[task1_duration] + task1_idx = indices_with_task1_duration.pop() + + # Get the longest duration task + task2_sorted_index = len(tasks) - 1 - idx + task2_duration = tasks[task2_sorted_index] + indices_with_task2_duration = task_duration_to_indices[task2_duration] + task2_idx = indices_with_task2_duration.pop() + + # Add the paired tasks to the result + paired_tasks.append([task1_idx, task2_idx]) + + return paired_tasks + +def get_task_duration_to_indices(tasks): + task_duration_to_indices = {} + + for idx in range(len(tasks)): + task_duration = tasks[idx] + if task_duration not in task_duration_to_indices: + task_duration_to_indices[task_duration] = [] + task_duration_to_indices[task_duration].append(idx) + + return task_duration_to_indices + +# Example usage +k = 3 +tasks = [1, 3, 5, 3, 1, 4] +paired_tasks = task_assignment(k, tasks) + +# Print the paired tasks +for pair in paired_tasks: + print(pair) diff --git a/Hash Table/Bloomfilter.cpp b/Hash Table/Bloomfilter.cpp new file mode 100644 index 00000000..76de2d67 --- /dev/null +++ b/Hash Table/Bloomfilter.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +class BloomFilter { +private: + int size; // Size of the Bloom Filter bitset + std::vector> hash_functions; + std::bitset<1000000> bitset; // Bitset to represent the Bloom Filter + +public: + // Constructor to initialize the Bloom Filter with a given size and hash functions + BloomFilter(int size, std::vector> hash_functions) + : size(size), hash_functions(hash_functions) {} + + // Function to add an element to the Bloom Filter + void add(const std::string& element) { + for (const auto& hash_function : hash_functions) { + size_t index = hash_function(element) % size; + bitset.set(index); + } + } + + // Function to check if an element may exist in the Bloom Filter + bool contains(const std::string& element) { + for (const auto& hash_function : hash_functions) { + size_t index = hash_function(element) % size; + if (!bitset.test(index)) { + return false; // If any bit is not set, the element is definitely not in the filter + } + } + return true; // If all bits are set, the element may exist in the filter (false positives are possible) + } +}; + +// Example hash function using std::hash +size_t hash1(const std::string& str) { + return std::hash{}(str); +} + +// Example hash function using a custom hash function +size_t hash2(const std::string& str) { + size_t hash = 0; + for (char c : str) { + hash = (hash * 31) + c; + } + return hash; +} + +int main() { + // Create a Bloom Filter with a size of 1000000 and two hash functions + BloomFilter bloomFilter(1000000, {hash1, hash2}); + + // Add elements to the Bloom Filter + bloomFilter.add("apple"); + bloomFilter.add("banana"); + bloomFilter.add("cherry"); + + // Check if elements may exist in the Bloom Filter + std::cout << "Contains 'apple': " << bloomFilter.contains("apple") << std::endl; // Should return 1 (true) + std::cout << "Contains 'grape': " << bloomFilter.contains("grape") << std::endl; // Should return 0 (false) + std::cout << "Contains 'cherry': " << bloomFilter.contains("cherry") << std::endl; // Should return 1 (true) + + return 0; +} diff --git a/Hash Table/Convert an array to reduced form using hashing.cpp b/Hash Table/Convert an array to reduced form using hashing.cpp new file mode 100644 index 00000000..017eced5 --- /dev/null +++ b/Hash Table/Convert an array to reduced form using hashing.cpp @@ -0,0 +1,83 @@ +/*Declare an array of name ar[ ] with n elements +Create a temp[ ] array of size n +Create a hashmap for the same datatype of the array +Copy the elements of array ar [ ] to temp[ ] array +Sort the temp[ ] array in ascending order +After sorting, mapping the values from 0 to n-1 with the temp array. +Replace the ar[ ] element with the hashtable values. +Print the ar[ ] */ + + + + + + + + +#include +using namespace std; +int main() +{ + int ar[] = {12,34,5,6,8}; + + /* calculating the size of the given array */ + int size = sizeof(ar)/sizeof(ar[0]); + + /* creating temp array to sort and manipulate the array*/ + int temp[size],val=0; + + /* creating an unordered_map*/ + unordered_map mapped; + + + /* copying the elements from ar to temp array*/ + for(int i=0;i keys() { + ArrayList allKeys = new ArrayList<>(); + for (int i = 0; i < dataMap.length; i++) { + Node temp = dataMap[i]; + while (temp != null) { + allKeys.add(temp.key); + temp = temp.next; + } + } + return allKeys; + } +} diff --git a/Hash Table/Longest_substring_without_repeating_characters.py b/Hash Table/Longest_substring_without_repeating_characters.py new file mode 100644 index 00000000..d23875d1 --- /dev/null +++ b/Hash Table/Longest_substring_without_repeating_characters.py @@ -0,0 +1,61 @@ +''' + Given a string s, find the length of the longest + substring + without repeating characters. + + Example 1: + Input: s = "abcabcbb" + Output: 3 + Explanation: The answer is "abc", with the length of 3. + + Example 2: + Input: s = "bbbbb" + Output: 1 + Explanation: The answer is "b", with the length of 1. + + Example 3: + Input: s = "pwwkew" + Output: 3 + Explanation: The answer is "wke", with the length of 3. + Notice that the answer must be a substring, "pwke" is a subsequence and not a substring. + + Constraints: + 0 <= s.length <= 5 * 104 + s consists of English letters, digits, symbols and spaces. +''' +class Solution: + #Approach-1 + #Time Complexity - O(N^2), Space complexity - O(N) + #Adding brute-force values into hash_map and calculating the max_length + def lengthOfLongestSubstring(self, s: str) -> int: + if(len(s)==0 or len(s)==1): + return len(s) + max_length = 0 + for i in range(len(s)): + temp_length,temp = 0,set() + for j in range(i,len(s)): + if(temp_length int: + if(len(s)==0): + return 0 + hash_set = set() + start_ind,end_ind = 0,0 + max_length = -1 + while(end_ind> partition(String s) { + List> result = new ArrayList<>(); + List current = new ArrayList<>(); + backtrack(result, current, s, 0); + return result; + } + + private static void backtrack(List> result, List current, String s, int start) { + if (start == s.length()) { + result.add(new ArrayList<>(current)); + return; + } + + for (int end = start; end < s.length(); end++) { + if (isPalindrome(s, start, end)) { + current.add(s.substring(start, end + 1)); + backtrack(result, current, s, end + 1); + current.remove(current.size() - 1); + } + } + } + + private static boolean isPalindrome(String s, int start, int end) { + while (start < end) { + if (s.charAt(start) != s.charAt(end)) { + return false; + } + start++; + end--; + } + return true; + } + + public static void main(String[] args) { + String input = "aab"; + List> partitions = partition(input); + + for (List partition : partitions) { + System.out.println(partition); + } + } +} diff --git a/Hash Table/Partition_string.py b/Hash Table/Partition_string.py new file mode 100644 index 00000000..120bc12f --- /dev/null +++ b/Hash Table/Partition_string.py @@ -0,0 +1,39 @@ +def is_palindrome(s): + return s == s[::-1] + +def min_partition(s): + n = len(s) + + # Create a hash table to store the results of subproblems + # dp[i] represents the minimum number of partitions in s[0:i] + dp = [float('inf')] * (n + 1) + dp[0] = 0 + + # Iterate through the string + for i in range(1, n + 1): + # Check all possible substrings s[j:i] + for j in range(i): + # If s[j:i] is a palindrome, update the minimum number of partitions + if is_palindrome(s[j:i]): + dp[i] = min(dp[i], dp[j] + 1) + + # Return the minimum number of partitions for the entire string + return dp[n] - 1 + +# Example usage +string = "aabba" +optimal_partitions = min_partition(string) +print("Optimal number of partitions:", optimal_partitions) + + +''' +The min_partition function utilizes dynamic programming to find the minimum number of partitions in the given string s. It iterates through each character in the string and checks all possible substrings. If a substring is a palindrome, it updates the minimum number of partitions accordingly. + +Note that the dp list is initialized with float('inf') values, except for the first element dp[0], which is set to 0. The final result subtracts 1 from dp[n] to account for the fact that the last character does not require a partition. + + + +The time complexity of the given solution is O(n^3), where n is the length of the input string s. This is because there are two nested loops, and for each combination of i and j, the is_palindrome function is called, which has a time complexity of O(n). Hence, the overall time complexity is O(n^3). + +The space complexity of the solution is O(n), where n is the length of the input string s. This is because the dp list, used to store the minimum number of partitions for each substring, has a length of n + 1. Therefore, the space required is linearly proportional to the length of the input string. +''' diff --git a/Hash Table/add_first_missing_positive.cpp b/Hash Table/add_first_missing_positive.cpp new file mode 100644 index 00000000..cc80c5b9 --- /dev/null +++ b/Hash Table/add_first_missing_positive.cpp @@ -0,0 +1,60 @@ +/* + Given an unsorted integer array nums, return the smallest missing positive integer. + You must implement an algorithm that runs in O(n) time and uses O(1) auxiliary space. + Sample Input: [3, 4, -1, 1] + Sample Output: 2 + Explanation: + This code implements a solution to find the first missing positive integer from an unsorted array. The function first separates positive and negative numbers. + It then considers the array with only positive numbers, and marks the index corresponding to every positive number in this array. + The first index which is unmarked corresponds to the first missing positive number. + This solution uses the array itself as a pseudo-hash table where the keys are the array indices, and the values are whether or not a positive integer is present in the array. + By using the array in this way, we are able to find the solution in O(1) auxiliary space. + Time complexity: O(n) + Space complexity: O(1) +*/ + +#include +#include +#include + +int firstMissingPositive(std::vector& nums) { + int n = nums.size(); + + // Mark numbers that are out of range as 0 + for (int i = 0; i < n; i++) { + if (nums[i] <= 0 || nums[i] > n) { + nums[i] = 0; + } + } + + // Mark the index corresponding to the value of each number + for (int i = 0; i < n; i++) { + int num = std::abs(nums[i]); + + if (num > 0) { + num--; // subtract 1 because of 0-based indexing + + // Mark as negative + if (nums[num] > 0) { + nums[num] = -nums[num]; + } + } + } + + // Find the first number greater than 0 + for (int i = 0; i < n; i++) { + if (nums[i] >= 0) { + return i + 1; // add 1 because of 0-based indexing + } + } + + // If no number is missing + return n + 1; +} + +int main() { + std::vector nums = {3, 4, -1, 1}; + int result = firstMissingPositive(nums); + std::cout << "Smallest missing positive integer: " << result << std::endl; + return 0; +} diff --git a/Hash Table/add_first_missing_positive.java b/Hash Table/add_first_missing_positive.java new file mode 100644 index 00000000..d5f198ee --- /dev/null +++ b/Hash Table/add_first_missing_positive.java @@ -0,0 +1,55 @@ +/* + Given an unsorted integer array nums, return the smallest missing positive integer. + You must implement an algorithm that runs in O(n) time and uses O(1) auxiliary space. + + Sample Input: [3, 4, -1, 1] + Sample Output: 2 + + Explanation: + This code implements a solution to find the first missing positive integer from an unsorted array. The function first separates positive and negative numbers. + It then considers the array with only positive numbers, and marks the index corresponding to every positive number in this array. + The first index which is unmarked corresponds to the first missing positive number. + + This solution uses the array itself as a pseudo-hash table where the keys are the array indices, and the values are whether or not a positive integer is present in the array. + By using the array in this way, we are able to find the solution in O(1) auxiliary space. + + Time complexity: O(n) + Space complexity: O(1) +*/ + +public class AddFirstMissingPositive{ + public int firstMissingPositive(int[] nums) { + int n = nums.length; + + // Mark numbers that are out of range as 0 + for(int i = 0; i < n; i++) { + if(nums[i] <= 0 || nums[i] > n) { + nums[i] = 0; + } + } + + // Mark the index corresponding to the value of each number + for(int i = 0; i < n; i++) { + int num = Math.abs(nums[i]); + + if(num > 0) { + num--; // subtract 1 because of 0-based indexing + + // Mark as negative + if(nums[num] > 0) { + nums[num] = -nums[num]; + } + } + } + + // Find the first number greater than 0 + for(int i = 0; i < n; i++) { + if(nums[i] >= 0) { + return i + 1; // add 1 because of 0-based indexing + } + } + + // If no number is missing + return n + 1; + } +} diff --git a/Hash Table/find_optimal_partition_of_string.cpp b/Hash Table/find_optimal_partition_of_string.cpp new file mode 100644 index 00000000..d59ef89d --- /dev/null +++ b/Hash Table/find_optimal_partition_of_string.cpp @@ -0,0 +1,53 @@ +/* OPTIMAL PARTITION OF STRING */ +/* PROGRAM AUTHOR : VARUN GARG */ + +/* + + Our task is to partition the string into one or more substrings such that the characters in each substring are unique. + + Approach: + Intuitively, we can consider adding characters to a substring as long as we don't see a character + that has already been added to the current substring. When we see a character that is already + present in the substring, we start a new substring and repeat this process until we iterate over the entire string s. + we are declaring a set in which if the character is not present in the set then the character will be inserted in it + else it will break from the loop then the count of the strings will be increased . + + + sample input : abacaba + sample output : a b + a c + a b + a + The minimum number of substrings required for partition + 4 + + Time Complexity: O(n) + Space Complexity: O(n) +*/ +#include +using namespace std; + +int main(){ + string s; + cin>>s; // taking input of the string + + int cnt=0; + int n=s.size(); + for(int i=0;i sub; + // taking the unique substrings in a set by checking the character is present in the set or not + while(i 0 { + cuts[j] = min(cuts[j], cuts[i-1]+1) + } else { + cuts[j] = 0 + } + } + } + } + + return cuts[n-1] +} + +func main() { + s := "aabba" + minimumCuts := minCut(s) + fmt.Println("Minimum cuts:", minimumCuts) +} diff --git a/Hash Table/first_duplicate_value.go b/Hash Table/first_duplicate_value.go new file mode 100644 index 00000000..fa5cb47d --- /dev/null +++ b/Hash Table/first_duplicate_value.go @@ -0,0 +1,47 @@ +/* + + Given an array of integers between 1 and n, inclusive, where n is the length of the array, write a function + that returns the first integer that appears more than once (when the array is read from left to right). + + Sample Input = [2, 1, 5, 2, 3, 3, 4] + Output : 2 + + The time complexity of the `FirstDuplicateValue` function is O(n), where n is the length of the + input `array`. This is because the function iterates through the array once, performing constant + time operations (checking if a value exists in a hash map and inserting values into the hash map). + The worst case scenario is that the function iterates through the entire array without finding a + duplicate, resulting in O(n) time complexity. + + The space complexity of the given implementation is O(n), where n is the length of the input array. + This is because we are using a hash table (implemented as a map in Go) to store the elements we have + seen so far. In the worst case, where there are no duplicates in the array, we will end up storing + all n elements in the hash table, which would require O(n) space. +*/ +package main + +import "fmt" + +/* + This function takes an array of integers as input and returns the first duplicate value found in + the array. It uses a hash table, implemented as a Go map, to keep track of the numbers seen so far. + For each number in the input array, the function checks if it has already been seen before by + checking if it exists as a key in the map. If it has been seen before, the function returns + the number as it is the first duplicate value found. Otherwise, the function adds the number + to the map with a value of true to indicate that it has been seen. +*/ +func FirstDuplicateValue(array []int) int { + seenSoFar := make(map[int]bool) + for _, num := range array { + if _, valueExists := seenSoFar[num]; valueExists { + return num + } + seenSoFar[num] = true + } + return -1 +} + +func main() { + array := []int{2, 1, 5, 2, 3, 3, 4} + firstDuplicate := FirstDuplicateValue(array) + fmt.Println(firstDuplicate) +} diff --git a/Hash Table/first_missing_positve.go b/Hash Table/first_missing_positve.go new file mode 100644 index 00000000..143d535a --- /dev/null +++ b/Hash Table/first_missing_positve.go @@ -0,0 +1,68 @@ +/* + -> The firstMissingPositive function takes a slice of integers called nums as input. + + -> The function determines the length of the slice nums and assigns it to the variable n. + + -> Step 1: Move positive numbers to their correct positions + - The algorithm iterates through each element of the slice using a for loop. + - Inside the loop, it checks if the current number (nums[i]) is positive and within the range of 1 to n. + - If the number is valid, it checks whether the number at its correct position (nums[nums[i]-1]) is different + from the current number itself. This ensures that the number is not already in its correct place. + - If the numbers are different, it swaps the current number with the number at its correct position using the + Go idiomatic way of swapping values (nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]). + - The swapping process continues until the number at index i is either non-positive or already in its correct + place. This step ensures that all positive numbers are placed in their respective positions. + + -> Step 2: Find the first missing positive + After completing the first pass through the slice, the algorithm performs a second pass using another for loop. + Inside the loop, it checks if the number at index i is equal to i + 1. If not, it means that i + 1 is missing from the slice. + In this case, the algorithm returns i + 1 as the smallest missing positive integer. + + -> If all positive integers from 1 to n are present in the slice, the function reaches the end without finding a + missing positive. In this case, it returns n + 1 as the smallest missing positive integer. + +The Go implementation of the algorithm ensures that it modifies the input slice nums in-place without using any +additional data structures, meeting the O(1) auxiliary space requirement. The time complexity of the algorithm +remains O(n) as it performs two passes through the slice, visiting each element once. +*/ + +/* +Example-> + +Example 1: +Input: nums := []int{1, 2, 0} +Output: 3 +Explanation: The smallest missing positive integer in the slice is 3. + +Example 2: +Input: nums := []int{3, 4, -1, 1} +Output: 2 +Explanation: The smallest missing positive integer in the slice is 2. + +Example 3: +Input: nums := []int{7, 8, 9, 11, 12} +Output: 1 +Explanation: The smallest missing positive integer in the slice is 1. + +*/ + +func firstMissingPositive(nums []int) int { + n := len(nums) + + // Step 1: Move positive numbers to their correct positions + for i := 0; i < n; i++ { + for nums[i] > 0 && nums[i] <= n && nums[nums[i]-1] != nums[i] { + nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] + } + } + + // Step 2: Find the first missing positive + for i := 0; i < n; i++ { + if nums[i] != i+1 { + return i + 1 + } + } + + // If all positive integers are present, return n + 1 + return n + 1 +} diff --git a/Hash Table/first_missing_positve.js b/Hash Table/first_missing_positve.js new file mode 100644 index 00000000..a465ac76 --- /dev/null +++ b/Hash Table/first_missing_positve.js @@ -0,0 +1,74 @@ +// First Missing Positve +/* + -> The firstMissingPositive function takes an array of integers called nums as input. + + -> The function first determines the length of the array nums and assigns it to the variable n. + + -> Step 1: Move positive numbers to their correct positions + - The algorithm iterates through each element of the array using a for loop. + - Inside the loop, it checks if the current number (nums[i]) is positive and within the range of 1 to n. + - If the number is valid, it checks whether the number at its correct position (nums[nums[i] - 1]) is + different from the current number itself. This ensures that the number is not already in its correct place. + - If the numbers are different, it swaps the current number with the number at its correct position using + array destructuring ([nums[nums[i] - 1], nums[i]] = [nums[i], nums[nums[i] - 1]]). This moves the current + number to its correct place in the array. + - The swapping process continues until the number at index i is either non-positive or already in its correct + place. This step ensures that all positive numbers are placed in their respective positions. + + Step 2: Find the first missing positive + - After completing the first pass through the array, the algorithm performs a second pass using another for loop. + - Inside the loop, it checks if the number at index i is equal to i + 1. If not, it means that i + 1 is + missing from the array. + - In this case, the algorithm returns i + 1 as the smallest missing positive integer. + + -> If all positive integers from 1 to n are present in the array, the function reaches the end without finding a + missing positive. In this case, it returns n + 1 as the smallest missing positive integer. + + + +The algorithm meets the given time complexity requirement of O(n) because it performs two passes through the array, +and each pass visits each element once. It also satisfies the space complexity requirement of O(1) since it modifies +the input array in-place without using any additional data structures. +*/ + + +/* +Examples- + +Example 1: +Input: nums = [1, 2, 0] +Output: 3 +Explanation: The smallest missing positive integer in the array is 3. + +Example 2: +Input: nums = [3, 4, -1, 1] +Output: 2 +Explanation: The smallest missing positive integer in the array is 2. + +Example 3: +Input: nums = [7, 8, 9, 11, 12] +Output: 1 +Explanation: The smallest missing positive integer in the array is 1. +*/ + + +function firstMissingPositive(nums) { + const n = nums.length; + + // Step 1: Move positive numbers to their correct positions + for (let i = 0; i < n; i++) { + while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] !== nums[i]) { + [nums[nums[i] - 1], nums[i]] = [nums[i], nums[nums[i] - 1]]; + } + } + + // Step 2: Find the first missing positive + for (let i = 0; i < n; i++) { + if (nums[i] !== i + 1) { + return i + 1; + } + } + + // If all positive integers are present, return n + 1 + return n + 1; +} \ No newline at end of file diff --git a/Hash Table/first_missing_positve.py b/Hash Table/first_missing_positve.py new file mode 100644 index 00000000..d7284d1f --- /dev/null +++ b/Hash Table/first_missing_positve.py @@ -0,0 +1,57 @@ +# first positive missing in python +''' +To find the smallest missing positive integer in an unsorted integer array nums with the given time and space constraints, +you can utilize the concept of in-place swapping and indexing. + +Explanation:- + + -> Iterate through the array nums and ignore any non-positive numbers (i.e., negative numbers and zero). + Also, ignore numbers greater than the length of the array since they cannot be the smallest missing positive integer. + + -> For each positive number num encountered, find its correct index targetIndex as num - 1. + + -> Check if the number at index targetIndex is already in its correct place or not. If it is not, + swap the numbers at indices i and targetIndex to move num to its correct place. Continue this swapping + process until the number at index i is either non-positive or already in its correct place. + + -> After completing the iteration, perform a second pass through the array. The first index i that does not + contain the value i + 1 represents the smallest missing positive integer. Return i + 1. +''' + +# Time complexity O(n) and Space complexity O(1). + +'''Examples- + +Example 1: +Input: nums = [1, 2, 0] +Output: 3 +Explanation: The smallest missing positive integer in the array is 3. + +Example 2: +Input: nums = [3, 4, -1, 1] +Output: 2 +Explanation: The smallest missing positive integer in the array is 2. + +Example 3: +Input: nums = [7, 8, 9, 11, 12] +Output: 1 +Explanation: The smallest missing positive integer in the array is 1. + +''' + +def firstMissingPositive(nums): + n = len(nums) + + # Step 1: Move positive numbers to their correct positions + for i in range(n): + while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]: + nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1] + + # Step 2: Find the first missing positive + for i in range(n): + if nums[i] != i + 1: + return i + 1 + + # If all positive integers are present, return n + 1 + return n + 1 + diff --git a/Hash Table/first_non_repeated_character.go b/Hash Table/first_non_repeated_character.go new file mode 100644 index 00000000..28aec061 --- /dev/null +++ b/Hash Table/first_non_repeated_character.go @@ -0,0 +1,52 @@ +/* + The FirstNonRepeatingCharacter function takes a string as input and returns the index of the + first non-repeating character in the string. If all the characters are repeated, then it returns -1. + + The function works by first creating an empty map called charFreq to keep track of the frequency of + each character in the string. Then, it iterates through each character in the string using a for loop. + + Inside the loop, it checks if the current character already exists in the charFreq map. + + If it does, then it increments its frequency by 1, and if it doesn't, it adds the character to the map + with a frequency of 1. + + After the map is constructed, the function iterates through the string again using another for loop, + this time tracking the index of each character using the idx variable. + + For each character, it checks its frequency in the charFreq map. + + If the frequency is 1, then the character is non-repeating and the function returns its index. + + If no non-repeating character is found, then the function returns -1. + + Overall, the function has a time complexity of O(n) since it loops through the string twice, and a space complexity of O(k), where k is the number of unique characters in the string, since the charFreq map stores the frequency of each unique character. +*/ +package main + +import "fmt" + +// This function takes a string as input and returns the index of the first non-repeating character in the string. +func FirstNonRepeatingCharacter(str string) int { + // Create a map to store the frequency of each character in the string. + // The key of the map is a rune, which is an integer representation of a Unicode character. + // The value of the map is an integer representing the frequency of the corresponding character. + charFreq := make(map[rune]int) + // Loop through each character in the string and update its frequency in the map. + for _, char := range str { + charFreq[char] += 1 + } + // Loop through each character in the string again. + for idx, char := range str { + // If the frequency of the current character is 1, it means it is the first non-repeating character in the string. + if charFreq[char] == 1 { + // Return the index of the non-repeating character. + return idx + } + } + // If no non-repeating character is found in the string, return -1. + return -1 +} + +func main() { + fmt.Println(FirstNonRepeatingCharacter("abcdcaf")); // b index 1 +} \ No newline at end of file diff --git a/Hash Table/first_repeated_character.go b/Hash Table/first_repeated_character.go new file mode 100644 index 00000000..0c05be46 --- /dev/null +++ b/Hash Table/first_repeated_character.go @@ -0,0 +1,33 @@ +// Program to find first repeating character in a word +// Sample Input : "DataStructures" +// Output: a + +package main + +import "fmt" + +func FirstRepeatedCCharacter(word string) byte { + length := len(word) + + hMap := [256]int{} // Extended Ascii can support 256 different characters + + // initialize hMap values to 0 + for i := 0; i < 256; i++ { + hMap[i] = 0 + } + + // every time you see a character in a word increment its position in hMap + for i := 0; i < length; i++ { + // if character is already present incase "1" then return character + if hMap[word[i]] == 1 { + return word[i] + } + // increment value in position of character + hMap[word[i]]++ + } + return byte(0) +} + +func main() { + fmt.Printf("%c",FirstRepeatedCCharacter("DataStructures")) +} \ No newline at end of file diff --git a/HashTable/four_number_sum.java b/Hash Table/four_number_sum.java similarity index 100% rename from HashTable/four_number_sum.java rename to Hash Table/four_number_sum.java diff --git a/Hash Table/frequency_of_elements.java b/Hash Table/frequency_of_elements.java new file mode 100644 index 00000000..54f8f093 --- /dev/null +++ b/Hash Table/frequency_of_elements.java @@ -0,0 +1,90 @@ +/** + * Problem Description + * Given an array A. You have some integers given in the array B. + * + * For the i-th number, find the frequency of B[i] in the array A and return a list containing all the frequencies. + * + * + * + * Problem Constraints + * 1 <= |A| <= 105 + * + * 1 <= |B| <= 105 + * + * 1 <= A[i] <= 105 + * + * 1 <= B[i] <= 105 + * + * + * + * Input Format + * First argument A is an array of integers. + * + * Second argument B is an array of integers denoting the queries. + * + * + * + * Output Format + * Return an array of integers containing frequency of the each element in B. + * + * + * + * Example Input + * Input 1: + * A = [1, 2, 1, 1] + * B = [1, 2] + * Input 2: + * A = [2, 5, 9, 2, 8] + * B = [3, 2] + * + * + * Example Output + * Output 1: + * [3, 1] + * Output 2: + * [0, 2] + * + * + * Example Explanation + * For Input 1: + * The frequency of 1 in the array A is 3. + * The frequency of 2 in the array A is 1. + * For Input 2: + * The frequency of 3 in the array A is 0. + * The frequency of 2 in the array A is 2. + */ +package Hashing; + +import java.util.Arrays; +import java.util.HashMap; + +public class FrequencyOfElements { + public static void main(String[] args) { + int[] array = {2, 5, 9, 2, 8}; + int[] queries = {3, 2}; + + int[] res = solve(array, queries); + System.out.println(Arrays.toString(res)); + } + + public static int[] solve(int[] nums, int[] queries) { + // O(N) time | O(Q) space where N is length of array , Q is length of queries + + HashMap hashMap = new HashMap<>(); + + for (int num : nums) { + if (!hashMap.containsKey(num)) hashMap.put(num, 0); + + hashMap.put(num, hashMap.get(num) + 1); + } + System.out.println(hashMap); + + int[] res = new int[queries.length]; + + for (int i = 0; i < queries.length; i++) + if (hashMap.containsKey(queries[i])) + res[i] = hashMap.get(queries[i]); + + return res; + } +} diff --git a/Hash Table/group_anagrams.class b/Hash Table/group_anagrams.class new file mode 100644 index 00000000..a43f6db1 Binary files /dev/null and b/Hash Table/group_anagrams.class differ diff --git a/Hash Table/group_anagrams.cpp b/Hash Table/group_anagrams.cpp new file mode 100644 index 00000000..012e44be --- /dev/null +++ b/Hash Table/group_anagrams.cpp @@ -0,0 +1,67 @@ +// Group Anagrams +/* + The function groupAnagrams takes a vector of strings strs as input and returns a vector of vectors of strings res, + where each inner vector contains a group of anagrams. + + We create an unordered map mp to store the anagram groups. The keys of the map are the sorted versions of the anagrams, + and the values are vectors containing the original strings that belong to that anagram group. + + We iterate through each string in the input vector strs, create a copy of the string t, sort the characters in the copy t, + and add the original string s to the corresponding anagram group in the unordered map mp. + + We create a vector res to store the anagram groups and iterate through the unordered map mp. For each anagram group in the map, + we add the corresponding vector of original strings to the res vector. + + We return the result vector res containing all the anagram groups. + + In the main function, we create an example input vector strs, call the groupAnagrams function and store the result in the + output vector output, and iterate through the output vector to print each anagram group. + + The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the + length of the longest string in the array. This is due to the time complexity of sorting each string in the array. + + The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. + +*/ +#include +#include +#include +#include +#include +using namespace std; + +vector> groupAnagrams(vector& strs) { + unordered_map> mp; // create an unordered map to store anagrams + + // iterate through each string in the input vector + for (string s : strs) { + string t = s; // create a copy of the current string + sort(t.begin(), t.end()); // sort the characters in the copy + mp[t].push_back(s); // add the original string to the corresponding anagram group + } + + vector> res; // create a vector to store the anagram groups + for (auto p : mp) { // iterate through the unordered map + res.push_back(p.second); // add the anagram group to the result vector + } + + return res; // return the result vector containing all the anagram groups +} + +int main() { + // create an example input vector + vector strs = {"eat", "tea", "tan", "ate", "nat", "bat"}; + + // call the groupAnagrams function and store the result in the output vector + vector> output = groupAnagrams(strs); + + // iterate through the output vector and print each anagram group + for (vector group : output) { + for (string s : group) { + cout << s << " "; + } + cout << endl; + } + + return 0; +} diff --git a/Hash Table/group_anagrams.go b/Hash Table/group_anagrams.go new file mode 100644 index 00000000..4de5b952 --- /dev/null +++ b/Hash Table/group_anagrams.go @@ -0,0 +1,72 @@ +// Group Anagrams +/* + The program takes a slice of strings as input, where each string is an element in the slice. + The goal is to group the strings into separate groups where each group contains only anagrams of each other. + + The first step in the program is to create a map where the keys are strings and the values are slices of strings. + This map will be used to store the groups of anagrams. + + Next, the program loops through each string in the input slice. For each string, the program converts + it to a slice of bytes, sorts the bytes in ascending order, and then converts the sorted slice of bytes back into a string. + This sorted string is used as the key to the map. + + If the key already exists in the map, the string is added to the slice of values associated with that key. + If the key doesn't exist, a new key-value pair is created with the key being the sorted string and the value + being a new slice containing only the current string. + + Once all the strings have been processed, the program loops through the map and appends the slice of values + associated with each key to the output slice. This output slice contains slices of strings, where each slice + is a group of anagrams. + + Finally, the program returns the output slice. + + Overall, the program uses the fact that anagrams have the same set of characters (and thus, the same sorted order of characters) \ + to group the input strings efficiently. + + The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the length of the longest string in the array. This is due to the time complexity of sorting each string in the array. + + The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. +*/ +package main + +import ( + "fmt" + "sort" +) + +func groupAnagrams(strs []string) [][]string { + // Create a map to store anagrams and their grouped strings + anagramMap := make(map[string][]string) + + // Loop through each string in the input array + for _, str := range strs { + // Sort the string to create a unique key for each anagram + sortedStr := sortString(str) + // Add the string to its corresponding group in the anagramMap + anagramMap[sortedStr] = append(anagramMap[sortedStr], str) + } + + // Create a 2D slice to store the grouped anagrams + groups := make([][]string, 0, len(anagramMap)) + + // Loop through the anagramMap and append each group of anagrams to the groups slice + for _, group := range anagramMap { + groups = append(groups, group) + } + + return groups +} + +// Helper function to sort a string alphabetically +func sortString(s string) string { + sBytes := []byte(s) + sort.Slice(sBytes, func(i, j int) bool { return sBytes[i] < sBytes[j] }) + return string(sBytes) +} +func main() { + strs := []string{"eat", "tea", "tan", "ate", "nat", "bat"} + groups := groupAnagrams(strs) + for _, group := range groups { + fmt.Println(group) + } +} diff --git a/Hash Table/group_anagrams.java b/Hash Table/group_anagrams.java new file mode 100644 index 00000000..8114e97f --- /dev/null +++ b/Hash Table/group_anagrams.java @@ -0,0 +1,65 @@ +// Group Anagrams +/* + Explanation: + + The groupAnagrams method takes an array of strings and groups them into a list of anagram groups. To accomplish this, + the method first creates a HashMap called anagramGroups to store the groups of anagrams. + + For each string in the input array, the method converts the string to a character array, sorts the array, + and converts it back to a string. This sorted string serves as the key for the anagramGroups map. If the sorted + string is not already in the map, the method adds it with an empty list as its value. Then, the original string + is added to the list of its corresponding anagram group. + + Finally, the method returns a list of the values in the anagramGroups map, which is a list of the anagram groups. + + In the main method, we create an instance of the GroupAnagrams class and call the groupAnagrams method on an example input array. + We then print out the resulting list of anagram groups. + + The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the length of the longest string in the array. This is due to the time complexity of sorting each string in the array. + + The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. + +*/ +import java.util.*; + +public class group_anagrams{ + + public List> groupAnagrams(String[] strs) { + // Create a HashMap to store the groups of anagrams + Map> anagramGroups = new HashMap<>(); + + // Loop through each string in the input array + for (String str : strs) { + // Convert the string to a char array, sort it, and convert it back to a string + char[] chars = str.toCharArray(); + Arrays.sort(chars); + String sorted = new String(chars); + + // If the sorted string is not in the map, add it with an empty list as its value + if (!anagramGroups.containsKey(sorted)) { + anagramGroups.put(sorted, new ArrayList()); + } + + // Add the original string to the list of its anagram group + anagramGroups.get(sorted).add(str); + } + + // Return a list of the anagram groups + return new ArrayList<>(anagramGroups.values()); + } + + public static void main(String[] args) { + group_anagrams ga = new group_anagrams(); + + // Example input + String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"}; + + // Group the anagrams + List> groups = ga.groupAnagrams(strs); + + // Print the groups + for (List group : groups) { + System.out.println(group); + } + } +} diff --git a/Hash Table/group_anagrams.js b/Hash Table/group_anagrams.js new file mode 100644 index 00000000..f3092830 --- /dev/null +++ b/Hash Table/group_anagrams.js @@ -0,0 +1,53 @@ +// Group Anagrams +/* + The function groupAnagrams takes an array of strings strs as input, and returns an array of arrays of anagrams. + The approach taken is to sort the characters of each string alphabetically, and group strings with the same sorted form together. + The resulting groups are stored in an object anagramGroups whose keys are the sorted strings and whose values are arrays of the original strings. + Finally, the values of the anagramGroups object are returned as an array. + + To accomplish the sorting and grouping, the split, sort, and join methods are used. First, each string is split into an array of its characters + using the split method. Then, the sort method is used to sort the characters alphabetically. Finally, the join method is used to combine the + sorted characters back into a string. This sorted string is used as the key for grouping the anagrams in the anagramGroups object. + + The for...of loop is used to iterate over each string in the input array. Inside the loop, the sorted form of the string is computed, + and a check is made to see if a group for the sorted form already exists in the anagramGroups object. If it doesn't, a new group is + created as an empty array. Then, the original string is added to the appropriate group in the anagramGroups object using the push method. + + Finally, the Object.values method is used to extract the arrays of anagrams from the anagramGroups object, and these arrays are + returned as the final result. + + The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the length of the longest string in the array. This is due to the time complexity of sorting each string in the array. + + The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. + +*/ +/** + * Given an array of strings, group anagrams together and return them as an array of arrays. + * + * @param {string[]} strs - The input array of strings. + * @returns {string[][]} - An array of arrays of anagrams. + */ +function groupAnagrams(strs) { + // Create an empty object to store groups of anagrams. + const anagramGroups = {}; + + // Loop through each string in the input array. + for (const str of strs) { + // Sort the characters of the string alphabetically and use the sorted string as a key. + const sortedStr = str.split("").sort().join(""); + + // If a group for the sorted string doesn't exist yet, create it as an empty array. + if (!anagramGroups[sortedStr]) { + anagramGroups[sortedStr] = []; + } + + // Add the original string to the group of anagrams. + anagramGroups[sortedStr].push(str); + } + + // Return the values of the anagram groups object as an array. + return Object.values(anagramGroups); +} + +const words = ["eat", "tea", "tan", "ate", "nat", "bat"]; +console.log(groupAnagrams(words)); diff --git a/Hash Table/group_anagrams.py b/Hash Table/group_anagrams.py new file mode 100644 index 00000000..0406cf09 --- /dev/null +++ b/Hash Table/group_anagrams.py @@ -0,0 +1,47 @@ +# Group Anagrams +''' + The function groupAnagrams takes a list of strings as input and returns a list of lists, where each inner list + contains a group of anagrams from the input list. + + The function starts by creating an empty dictionary groups to store the groups of anagrams. The keys of the dictionary + will be the unique signatures of the anagrams, and the values will be lists of the actual anagrams. + + Then, the function iterates through each string s in the input list strs. For each string, it sorts the characters + of the string to get its unique signature sig. If the signature is not already in the dictionary, a new key-value pair + is added to the dictionary with the signature as the key and an empty list as the value. Finally, the current + string s is appended to the list of its corresponding signature in the dictionary. + + Once all the strings have been processed, the function returns the values of the dictionary as a list of lists, + where each inner list contains a group of anagrams from the input list. + + For example, if the input list is ["eat", "tea", "tan", "ate", "nat", "bat"], + the function will return [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']], which + represents the three groups of anagrams: ['eat', 'tea', 'ate'], ['tan', 'nat'], and ['bat']. + + The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the + length of the longest string in the array. This is due to the time complexity of sorting each string in the array. + + The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its + corresponding group of anagrams. + +''' +def groupAnagrams(strs): + # create a dictionary to store the groups of anagrams + groups = {} + + # iterate through each string in the list + for s in strs: + # sort the characters of the string to get its unique signature + # for example, "eat" and "ate" both have the same signature "aet" + sig = ''.join(sorted(s)) + + # check if the signature is already in the dictionary + # if not, add a new key-value pair with an empty list as the value + if sig not in groups: + groups[sig] = [] + + # append the current string to the list of its corresponding signature + groups[sig].append(s) + + # return the values of the dictionary as a list of lists + return list(groups.values()) diff --git a/Hash Table/integer_to_roman.cpp b/Hash Table/integer_to_roman.cpp new file mode 100644 index 00000000..eeb02c24 --- /dev/null +++ b/Hash Table/integer_to_roman.cpp @@ -0,0 +1,45 @@ +// Integer to Roman +/* + In this implementation, we use an unordered map to store the mapping between integers and Roman numerals. + We then loop through the map from the largest number to the smallest, checking if the current number + can be divided by the key of the map. If it can, we append the corresponding Roman numeral to the result + string and subtract the key from the number. We continue this process until the number is 0. + The final result string is then returned. + + The time complexity of this implementation is O(1) since the size of the map is fixed, and the + space complexity is O(1) since we only use a fixed amount of memory to store the map and the result string. +*/ +#include +#include +using namespace std; + +string intToRoman(int num) { + // Initialize the mapping between integers and Roman numerals + unordered_map map = { + {1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, + {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, + {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"} + }; + + // Initialize an empty string to store the result + string result = ""; + + // Loop through the map from the largest number to the smallest + for (auto it = map.rbegin(); it != map.rend(); ++it) { + // Check if the current number can be divided by the key of the map + while (num >= it->first) { + // Append the corresponding Roman numeral to the result string + result += it->second; + // Subtract the key from the number + num -= it->first; + } + } + + return result; +} + +int main() { + int num = 1994; + cout << intToRoman(num) << endl; // Output: "MCMXCIV" + return 0; +} diff --git a/Hash Table/integer_to_roman.go b/Hash Table/integer_to_roman.go new file mode 100644 index 00000000..f86df5bf --- /dev/null +++ b/Hash Table/integer_to_roman.go @@ -0,0 +1,35 @@ +// Integer to Roman +/* + In this implementation, we create two arrays symbols and values that hold the Roman numeral symbols and their + corresponding integer values respectively. We then create a string builder to hold our result. + + We loop through the values array, and while the current num is greater than or equal to the current values + element, we subtract the values element from num and append the corresponding symbols element to the result + string builder. We continue this process until we have gone through all the values elements. + + Finally, we return the result string. +*/ +package main + +import "strings" + +func intToRoman(num int) string { + // Create two arrays to hold the Roman numeral symbols and their corresponding values + symbols := []string{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} + values := []int{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} + + var result strings.Builder + // Loop through the values array and subtract the corresponding value from num while appending the symbol to the result + for i := 0; i < len(values); i++ { + for num >= values[i] { + num -= values[i] + result.WriteString(symbols[i]) + } + } + + return result.String() +} + +func main() { + print(intToRoman(8000)) +} \ No newline at end of file diff --git a/Hash Table/integer_to_roman.java b/Hash Table/integer_to_roman.java new file mode 100644 index 00000000..73c0fcdc --- /dev/null +++ b/Hash Table/integer_to_roman.java @@ -0,0 +1,34 @@ +// Integer to Roman +/* + Here, we create two arrays to store the Roman numerals and their corresponding values. We then use a + StringBuilder to build the resulting Roman numeral string. We use a while loop to iterate through + the values array until the number is reduced to 0. In each iteration, we check if the current value is + less than or equal to the number. If it is, we subtract the value from the number and append the + corresponding Roman numeral to the StringBuilder. If it's not, we move to the next value. Finally, + we return the resulting Roman numeral string. + + The time complexity of this implementation is O(1), since the maximum number of iterations in the + while loop is 13 (the length of the values array). The space complexity is also O(1), since we only + use a StringBuilder and two constant-size arrays to store the Roman numerals and values. +*/ +public static String intToRoman(int num) { + // Create two arrays to store the Roman numerals and their corresponding values + String[] romanNumerals = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; + int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; + + StringBuilder sb = new StringBuilder(); + int i = 0; + + // Iterate through the values array until the number is reduced to 0 + while (num > 0) { + // If the current value is less than or equal to the number, subtract the value and append the corresponding Roman numeral + if (values[i] <= num) { + num -= values[i]; + sb.append(romanNumerals[i]); + } else { + i++; + } + } + + return sb.toString(); +} diff --git a/Hash Table/integer_to_roman.js b/Hash Table/integer_to_roman.js new file mode 100644 index 00000000..c005d426 --- /dev/null +++ b/Hash Table/integer_to_roman.js @@ -0,0 +1,39 @@ +// Integer to Roman +/* + This solution uses a greedy approach to build the roman numeral string by subtracting the maximum + possible value at each step until the input number is zero. The time complexity of this implementation + is O(1) since there is a fixed number of roman numerals and the loop will always run 13 times. + The space complexity is also O(1) since we only store a single string. +*/ +function intToRoman(num) { + // Create arrays for the symbols and their values + const symbols = [ + "M", + "CM", + "D", + "CD", + "C", + "XC", + "L", + "XL", + "X", + "IX", + "V", + "IV", + "I", + ]; + const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; + + let result = ""; // Initialize an empty string to store the roman numeral + + // Loop through the values array + for (let i = 0; i < values.length; i++) { + // While the input num is greater than or equal to the current value + while (num >= values[i]) { + result += symbols[i]; // Add the corresponding symbol to the result string + num -= values[i]; // Subtract the value from the input num + } + } + + return result; // Return the roman numeral string +} diff --git a/Hash Table/integer_to_roman.py b/Hash Table/integer_to_roman.py new file mode 100644 index 00000000..5b9a339e --- /dev/null +++ b/Hash Table/integer_to_roman.py @@ -0,0 +1,34 @@ +# Integer to Roman +''' + The function takes an integer num as input and returns the corresponding Roman numeral as a string. + The mapping of values to symbols is defined in a dictionary symbols. The keys in the dictionary are + the values that can be used to represent Roman numerals, and the values are the symbols themselves. + + The keys are sorted in descending order in a list called keys. This is done so that we can iterate + through the keys in descending order and subtract the largest value from num until num is 0. + + We initialize an empty string called roman to store the Roman numeral representation. We then iterate + through the keys in keys and subtract the largest value from num until num is less than the current key. + We append the corresponding symbol to roman for each value subtracted. + + Finally, we return the string roman, which is the Roman numeral representation of the input integer num. +''' +def intToRoman(num: int) -> str: + # define the mapping of values to symbols + symbols = {1: 'I', 4: 'IV', 5: 'V', 9: 'IX', 10: 'X', 40: 'XL', 50: 'L', + 90: 'XC', 100: 'C', 400: 'CD', 500: 'D', 900: 'CM', 1000: 'M'} + + # create a list of keys in descending order + keys = sorted(symbols.keys(), reverse=True) + + # initialize an empty string to store the roman numeral representation + roman = '' + + # iterate through the keys and subtract the largest value from the number + # until the number is 0 + for k in keys: + while num >= k: + roman += symbols[k] + num -= k + + return roman diff --git a/HashTable/remove_duplicates.go b/Hash Table/remove_duplicates.go similarity index 100% rename from HashTable/remove_duplicates.go rename to Hash Table/remove_duplicates.go diff --git a/Hash Table/roman_to_integer.cpp b/Hash Table/roman_to_integer.cpp new file mode 100644 index 00000000..53775306 --- /dev/null +++ b/Hash Table/roman_to_integer.cpp @@ -0,0 +1,66 @@ +/* +Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. + +Symbol Value +I 1 +V 5 +X 10 +L 50 +C 100 +D 500 +M 1000 +For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II. + +Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: + +I can be placed before V (5) and X (10) to make 4 and 9. +X can be placed before L (50) and C (100) to make 40 and 90. +C can be placed before D (500) and M (1000) to make 400 and 900. +Given a roman numeral, convert it to an integer. + + + +Example 1: + +Input: s = "III" +Output: 3 +Explanation: III = 3. +Example 2: + +Input: s = "LVIII" +Output: 58 +Explanation: L = 50, V= 5, III = 3. +Example 3: + +Input: s = "MCMXCIV" +Output: 1994 +Explanation: M = 1000, CM = 900, XC = 90 and IV = 4. + + +Constraints: + +1 <= s.length <= 15 +s contains only the characters ('I', 'V', 'X', 'L', 'C', 'D', 'M'). +It is guaranteed that s is a valid roman numeral in the range [1, 3999]. + +*/ + +class Solution { +public: + int romanToInt(string s) { + // Map Roman Characters to integer values + unordered_map mp = {{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}, }; + int len = s.size(); + int sum = mp[s[len-1]]; + // start from end and build up the value to start + for(int i = len - 2; i >= 0; i--){ + if(mp[s[i]] < mp[s[i + 1]]){ + sum -= mp[s[i]]; + } + else{ + sum += mp[s[i]]; + } + } + return sum; + } +}; \ No newline at end of file diff --git a/Hash Table/roman_to_integer.go b/Hash Table/roman_to_integer.go new file mode 100644 index 00000000..4759ab0b --- /dev/null +++ b/Hash Table/roman_to_integer.go @@ -0,0 +1,65 @@ +// Roman To Integer +/* + This implementation converts a given Roman numeral string to its corresponding integer value. + The implementation uses a map to store the integer value of each Roman numeral, and then + iterates over the input string, checking if the next Roman numeral is greater than the + current one to determine whether to add or subtract the current value from the result. + The time complexity of this implementation is O(n), where n is the length of the input string, + since it iterates over the string only once. The space complexity is O(1), since the + implementation only uses a constant amount of extra space to store the map and result variable. + + Time complexity: O(n), where n is the length of the input string. The program iterates through + each character in the string once and performs a constant number of operations for each character. + Therefore, the time complexity is linear in the length of the input string. + + Space complexity: O(1). The program uses a constant amount of extra space to store the result + and the current and previous values. Therefore, the space complexity is constant, or O(1). +*/ +package main + +import ( + "fmt" +) + +func romanToInt(s string) int { + // Create a map to store the integer value of each Roman numeral + romanToIntMap := map[byte]int{ + 'I': 1, + 'V': 5, + 'X': 10, + 'L': 50, + 'C': 100, + 'D': 500, + 'M': 1000, + } + + // Initialize the result variable to 0 + result := 0 + + // Iterate over the input string + for i := 0; i < len(s); i++ { + // Get the integer value of the current Roman numeral + val := romanToIntMap[s[i]] + + // Check if the next Roman numeral is greater than the current one + if i+1 < len(s) && romanToIntMap[s[i+1]] > val { + // If the next Roman numeral is greater, subtract the current value from the result + result -= val + } else { + // If the next Roman numeral is not greater, add the current value to the result + result += val + } + } + + // Return the final result + return result +} + + + +func main() { + input := "IV" + output := romanToInt(input) + fmt.Printf("Roman numeral %s converted to integer is: %d\n", input, output) + +} \ No newline at end of file diff --git a/Hash Table/roman_to_integer.java b/Hash Table/roman_to_integer.java new file mode 100644 index 00000000..06986785 --- /dev/null +++ b/Hash Table/roman_to_integer.java @@ -0,0 +1,42 @@ +// AUTHOR : Sagar Wadhwa +class Solution { + public int romanToInt(String s) { + // Hashmap in java which stores key value pairs. Each key is unique and no key is repeated + HashMap map = new HashMap<>(); + + // Defining the basic rules of mapping from roman numeral to integer numbers. + map.put('I',1); + map.put('V',5); + map.put('X',10); + map.put('L',50); + map.put('C',100); + map.put('D',500); + map.put('M',1000); + + //extract the last character of the string, and find it in the map, store this keys value in the + // variable named "res" + int res = map.get(s.charAt(s.length()-1)); + + //iterate over the string from the second last character till the first character. Idea to solve + // this problem is that in the loop, for every character we check whether the value of the roman + // numeral at the i+1 th index is greater than or less than the value of the roman numeral at i th + // index. If the value of the roman numeral at the i th index is less than the value of the + // roman numeral at i+1 th index, then obviously that means we have to subtract the value of the + // roman numeral at the i th index from the result calculated so far. Else we have to add the value + // of the roman numeral at the i th index to the result calculated so far. Example: V means five, + // but if it is IV, then here the value of the roman numeral I is less than the value of the roman + // numeral V, so that means subtract I from V (4). If the string is VI, means 6, then here the value of + // V is greater than the value of I, so add I in V, that is 6. + // Doing so in an iterative fashion and storing the result and then adding or subtracting values + // from it accordingly will give us our final result. + for(int i=s.length()-2;i>=0;i--){ + + if(map.get(s.charAt(i)) < map.get(s.charAt(i+1))){ + res -= map.get(s.charAt(i)); + }else{ + res += map.get(s.charAt(i)); + } + } + return res; + } +} \ No newline at end of file diff --git a/Hash Table/roman_to_integer.js b/Hash Table/roman_to_integer.js new file mode 100644 index 00000000..f895aeba --- /dev/null +++ b/Hash Table/roman_to_integer.js @@ -0,0 +1,35 @@ +// Approach: In case of symbols like IV, CM, XC, etc., the first value is less than the second value. +// So we have to subtract the first value from the second value to get the exact value. +// Example: CM --> C=100, M=1000, so CM=1000-100=900. +// Therefore, we can conclude that in such cases, we have to subtract the first value from the whole result. +// Time complexity O(1) and Space complexity O(1). +var romanToInt = function(s) { + let map = new Map(); // Create a hashmap to store (symbol, value) pairs. + map['I'] = 1; + map['V'] = 5; + map['X'] = 10; + map['L'] = 50; + map['C'] = 100; + map['D'] = 500; + map['M'] = 1000; + + let arr = []; + for (let i = 0; i < s.length; i++) { // Store the value of each symbol in arr from left to right. + arr[i] = map[s.charAt(i)]; + } + + let ans = 0; + for (let i = 0; i < arr.length - 1; i++) { // Traverse from 0 to last-1. + // If the i-th element is less than the i+1-th element, then subtract the i-th element. + if (arr[i] < arr[i + 1]) { + ans = ans - arr[i]; + } + // Otherwise, add the i-th element. + else { + ans = ans + arr[i]; + } + } + // Add the last element because there is no further element to compare. + ans = ans + arr[arr.length - 1]; + return ans; +}; diff --git a/Hash Table/roman_to_integer.py b/Hash Table/roman_to_integer.py new file mode 100644 index 00000000..58e4e88e --- /dev/null +++ b/Hash Table/roman_to_integer.py @@ -0,0 +1,42 @@ +# Roman to Integer +''' + The function takes a string s as an input and returns the corresponding integer value + by converting the string from Roman numerals to its integer value. + + It does this by iterating through the string. If the current character has a lower + value than the character after it, the current character value is subtracted from + the total, so that it is added correctly. + + Otherwise, the current character value is added to the total. + + Finally, the total integer value is returned. +''' + +def roman_to_int(s: str) -> int: + # define mapping of values to symbols + symbols = { + 'I': 1, + 'V': 5, + 'X': 10, + 'L': 50, + 'C': 100, + 'D': 500, + 'M': 1000 + } + + # initialise integer to return total value + total = 0 + + # iterate through characters + for i in range(len(s) - 1): + # if the symbol after current symbol is less than it, + # subtract it from the total value + if symbols[s[i]] < symbols[s[i+1]]: + total -= symbols[s[i]] + # else, just add the corresponding value + else: + total += symbols[s[i]] + # add the last value + total += symbols[s[-1]] + + return total \ No newline at end of file diff --git a/HashTable/SumOfUniqueElements.java b/Hash Table/sum_of_unique_elements.java similarity index 100% rename from HashTable/SumOfUniqueElements.java rename to Hash Table/sum_of_unique_elements.java diff --git a/HashTable/three_number_sum.java b/Hash Table/three_number_sum.java similarity index 100% rename from HashTable/three_number_sum.java rename to Hash Table/three_number_sum.java diff --git a/Hash Table/two_sum.cpp b/Hash Table/two_sum.cpp new file mode 100644 index 00000000..21cfceb9 --- /dev/null +++ b/Hash Table/two_sum.cpp @@ -0,0 +1,54 @@ +/* + Write a function that takes in a non-empty array of distinct integers and an + integer representing a target sum. If any two numbers in the input array sum + up to the target sum, the function should return them in an array, in any + order. If no two numbers sum up to the target sum, the function should return + an empty array. + Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 + Output: [-1 11] +*/ +#include +#include +#include + +std::vector twoNumberSum(std::vector& nums, int target) { + // Create an unordered_map to store the indices of the elements in the vector + std::unordered_map map; + + // Loop through the vector + for (int i = 0; i < nums.size(); i++) { + // Calculate the complement of the current element with respect to the target sum + int complement = target - nums[i]; + + // Check if the complement is already in the map + if (map.count(complement) > 0) { + // If the complement is in the map, return the indices of the two elements that sum up to the target + return { map[complement], i }; + } + + // If the complement is not in the map, add the current element and its index to the map + map[nums[i]] = i; + } + + // If no two elements sum up to the target, return an empty vector + return {}; +} + +int main() { + // Example usage + std::vector nums = { 2, 7, 11, 15 }; + int target = 9; + std::vector result = twoNumberSum(nums, target); + + if (result.size() > 0) { + std::cout << "Indices of the two numbers that sum up to " << target << ": "; + for (int i : result) { + std::cout << i << " "; + } + std::cout << std::endl; + } else { + std::cout << "No two numbers found that sum up to " << target << "." << std::endl; + } + + return 0; +} diff --git a/HashTable/twosum.go b/Hash Table/two_sum.go similarity index 99% rename from HashTable/twosum.go rename to Hash Table/two_sum.go index 7a2a6da8..ba8f04b2 100644 --- a/HashTable/twosum.go +++ b/Hash Table/two_sum.go @@ -50,7 +50,7 @@ func TwoNumberSumTwoPointerMethod(array []int, target int) []int { // add result in array result = append(result, array[i]) result = append(result, array[j]) - // result the result + // return the result return result } else if array[i] + array[j] > target { // elements is greater that means look to left side of j diff --git a/HashTable/two_number_sum.java b/Hash Table/two_sum.java similarity index 54% rename from HashTable/two_number_sum.java rename to Hash Table/two_sum.java index 61620494..bcbccb18 100644 --- a/HashTable/two_number_sum.java +++ b/Hash Table/two_sum.java @@ -1,31 +1,40 @@ +/* + Write a function that takes in a non-empty array of distinct integers and an + integer representing a target sum. If any two numbers in the input array sum + up to the target sum, the function should return them in an array, in any + order. If no two numbers sum up to the target sum, the function should return + an empty array. + Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 + Output: [-1 11] +*/ import java.util.*; -/** - * - - Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. If any two numbers in the input array sum up to the target sum, the function should return them in an array, in any order. If no two numbers sum up to the target sum, the function should return an empty array. - - Note that the target sum has to be obtained by summing two different integers in the array; you can't add a single integer to itself in order to obtain the target sum. - - You can assume that there will be at most one pair of numbers summing up to the target sum. - Sample Input - - array = [3, 5, -4, 8, 11, 1, -1, 6] - targetSum = 10 - - Sample Output - - [-1, 11] // the numbers could be in reverse order - - - */ public class TwoNumberSum { + public static void main(String[] args) { int[] array = {3, 5, -4, 8, 11, 1, -1, 6}; int targetSum = 10; } - public static int[] twoNumberSumOptimal(int[] array, int targetSum) { -// O(nlog(n)) time | O(1) space + + // Brute Force Solution + // Time complexity: O(N^2); + // Space Complexity: O(1); + public static int[] TwoSumBruteForce(int[] array, int targetSum) { + for(int i = 0; i < array.length - 1; i++) { + int firstNum = array[i]; + for(int j = i + 1; j < array.length; j++) { + int secondNum = array[j]; + if(firstNum + secondNum == targetSum) + return new int[] {firstNum, secondNum}; + } + } return new int[0]; + } + + // Sorting Solution + // Time complexity: O(N log N); + // Space Complexity: O(1); + public static int[] TwoSumBySorting(int[] array, int targetSum) { + // O(nlog(n)) time | O(1) space Arrays.sort(array); int left = 0; int right = array.length - 1; @@ -38,8 +47,11 @@ public static int[] twoNumberSumOptimal(int[] array, int targetSum) { else right -= 1; } return new int[0]; } - public static int[] twoNumberSumUsingSet(int[] array, int targetSum) { -// O(n) time | O(n) space + + // Optimal Solution + // Time complexity: O(N); + // Space Complexity: O(N); + public static int[] TwoSumOptimal(int[] array, int targetSum) { Set nums = new HashSet<>(); for(int num: array) { int potentialMatch = targetSum - num; @@ -47,15 +59,4 @@ public static int[] twoNumberSumUsingSet(int[] array, int targetSum) { nums.add(num); } return new int[0]; } - public static int[] twoNumberSumBruteForce(int[] array, int targetSum) { -// O(n^2) time | O(1) space - for(int i = 0; i < array.length - 1; i++) { - int firstNum = array[i]; - for(int j = i + 1; j < array.length; j++) { - int secondNum = array[j]; - if(firstNum + secondNum == targetSum) - return new int[] {firstNum, secondNum}; - } - } return new int[0]; - } } diff --git a/Hash Table/two_sum.js b/Hash Table/two_sum.js new file mode 100644 index 00000000..50d8a33f --- /dev/null +++ b/Hash Table/two_sum.js @@ -0,0 +1,42 @@ +/* + Write a function that takes in a non-empty array of distinct integers and an + integer representing a target sum. If any two numbers in the input array sum + up to the target sum, the function should return them in an array, in any + order. If no two numbers sum up to the target sum, the function should return + an empty array. + Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 + Output: [-1 11] +*/ +function twoNumberSum(nums, target) { + // Create a new Map object to store the indices of the elements in the array + const map = new Map(); + + // Loop through the array + for (let i = 0; i < nums.length; i++) { + // Calculate the complement of the current element with respect to the target sum + const complement = target - nums[i]; + + // Check if the complement is already in the map + if (map.has(complement)) { + // If the complement is in the map, return the indices of the two elements that sum up to the target + return [map.get(complement), i]; + } + + // If the complement is not in the map, add the current element and its index to the map + map.set(nums[i], i); + } + + // If no two elements sum up to the target, return an empty array + return []; +} + +// Example usage +const nums = [2, 7, 11, 15]; +const target = 9; +const result = twoNumberSum(nums, target); + +if (result.length > 0) { + console.log(`Indices of the two numbers that sum up to ${target}: ${result}`); +} else { + console.log(`No two numbers found that sum up to ${target}.`); +} diff --git a/Hash Table/two_sum.py b/Hash Table/two_sum.py new file mode 100644 index 00000000..2d97f679 --- /dev/null +++ b/Hash Table/two_sum.py @@ -0,0 +1,36 @@ +''' + Write a function that takes in a non-empty array of distinct integers and an + integer representing a target sum. If any two numbers in the input array sum + up to the target sum, the function should return them in an array, in any + order. If no two numbers sum up to the target sum, the function should return + an empty array. + Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 + Output: [-1 11] +''' +# Brute Force Approach +# Time complexity: O(N^2); +# Space Complexity: O(1); +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + if nums[i] + nums[j] == target: + return [i, j] + return [] + +# Two Pointer approach +# Time complexity: O(N); +# Space Complexity: O(N); +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + # Create map to keep track of what we ahve seen so far + numToIndex = {} + for i in range(len(nums)): + # lets say first element in our array is 3, and target sum is 10 + # then we will look for 7 in our map, if its present then we simply return 7 and 3 + # if the required value is found then store result + if target - nums[i] in numToIndex: + return [numToIndex[target - nums[i]], i] + # keep track of what value in array we have seen so far + numToIndex[nums[i]] = i + return [] \ No newline at end of file diff --git a/Hash Table/zero_sum_subarray.cpp b/Hash Table/zero_sum_subarray.cpp new file mode 100644 index 00000000..3e833125 --- /dev/null +++ b/Hash Table/zero_sum_subarray.cpp @@ -0,0 +1,60 @@ +/* + You're given a list of integers nums. Write a function that returns a boolean representing + whether there exists a zero-sum subarray of nums + + Sample Input : = [-5, -5, 2, 3, -2] + Output : True + The subarray [-5, 2, 3] has a sum of 0 + + Approach: + Time and Space complexity : O(n) time | O(n) space - where n is the length of nums + This implementation uses an unordered map to keep track of the prefix sum of the input array nums. + We initialize the map with a key-value pair of 0 and -1, since a prefix sum of 0 indicates that the + subarray from index 0 to -1 (i.e., an empty subarray) has a sum of 0. We then iterate through the + input array nums, adding each element to the running sum sum and checking if the current sum is + already in the map. If it is, then we've found a subarray whose sum is 0, so we add the starting and + ending indices of the subarray to the result vector and break out of the loop. + If we reach the end of the loop without finding a zero sum subarray, then we return an empty vector. + Note that this implementation assumes that there is only one zero sum subarray in the input array. + If there could be multiple zero sum subarrays, then we would need to modify the implementation to + return all of them. +*/ +#include +#include +#include + +using namespace std; + +vector zeroSumSubarray(vector& nums) { + vector result; + unordered_map mp; + int sum = 0; + mp[0] = -1; + + for (int i = 0; i < nums.size(); i++) { + sum += nums[i]; + if (mp.find(sum) != mp.end()) { + result.push_back(mp[sum] + 1); + result.push_back(i); + break; + } + mp[sum] = i; + } + + return result; +} + +int main() { + vector nums = {4, 2, -3, 1, 6}; + vector result = zeroSumSubarray(nums); + if (result.empty()) { + cout << "No zero sum subarray found." << endl; + } else { + cout << "Zero sum subarray found: "; + for (int i = result[0]; i <= result[1]; i++) { + cout << nums[i] << " "; + } + cout << endl; + } + return 0; +} diff --git a/Hash Table/zero_sum_subarray.go b/Hash Table/zero_sum_subarray.go new file mode 100644 index 00000000..9551c557 --- /dev/null +++ b/Hash Table/zero_sum_subarray.go @@ -0,0 +1,44 @@ +/* + You're given a list of integers nums. Write a function that returns a boolean representing + whether there exists a zero-sum subarray of nums + + Sample Input : = [-5, -5, 2, 3, -2] + Output : True + The subarray [-5, 2, 3] has a sum of 0 + + Approach: + Time and Space complexity : O(n) time | O(n) space - where n is the length of nums + This implementation uses an unordered map to keep track of the prefix sum of the input array nums. + We initialize the map with a key-value pair of 0 and -1, since a prefix sum of 0 indicates that the + subarray from index 0 to -1 (i.e., an empty subarray) has a sum of 0. We then iterate through the + input array nums, adding each element to the running sum sum and checking if the current sum is + already in the map. If it is, then we've found a subarray whose sum is 0, so we add the starting and + ending indices of the subarray to the result vector and break out of the loop. + If we reach the end of the loop without finding a zero sum subarray, then we return an empty vector. + Note that this implementation assumes that there is only one zero sum subarray in the input array. + If there could be multiple zero sum subarrays, then we would need to modify the implementation to + return all of them. +*/ +package main + +import "fmt" + +func ZeroSumSubarray(nums []int) bool { + sums := map[int]bool{0: true} + currentSum := 0 + for _, num := range nums { + currentSum += num + if _, sumIsInSet := sums[currentSum]; sumIsInSet { + return true + } + sums[currentSum] = true + } + return false +} + +func main() { + nums := []int{-5, -5, 2, 3, -2} + fmt.Println(ZeroSumSubarray(nums)) + nums = []int{1, 2, 3, 4, 5} + fmt.Println(ZeroSumSubarray(nums)) +} \ No newline at end of file diff --git a/Hash Table/zero_sum_subarray.java b/Hash Table/zero_sum_subarray.java new file mode 100644 index 00000000..0fd2a8cd --- /dev/null +++ b/Hash Table/zero_sum_subarray.java @@ -0,0 +1,40 @@ +/* +Q -> Hash Table: You're given a list of integers nums. +Write a function that returns a boolean representing whether there exists a zero-sum subarray of nums in Java +*/ + +//Here's a Java code that implements a solution to find if there is a zero-sum subarray in the given list of +// integers using a hash table. + +import java.util.*; + +public class ZeroSumSubarray { + + public static boolean hasZeroSumSubarray(int[] nums) { + Set set = new HashSet<>(); + int sum = 0; + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + if (sum == 0 || set.contains(sum)) { + return true; + } + set.add(sum); + } + return false; + } + + public static void main(String[] args) { + int[] nums = {4, -3, 2, 1, 8}; + boolean hasZeroSumSubarray = hasZeroSumSubarray(nums); + System.out.println(hasZeroSumSubarray); + } +} + +/* +Explanation:- + +The above code uses a HashSet to keep track of the prefix sum of the elements of the input array. At each index, it checks if the sum up to that index has already been seen before in the HashSet. +If it has been seen, it means that there exists a subarray with a zero sum. If the sum equals zero, then the subarray starts from the beginning of the array. +The time complexity of this algorithm is O(n), where n is the size of the input array, since we traverse the input array only once. +The space complexity is also O(n), since the HashSet can contain up to n elements in the worst case. + */ diff --git a/Hash Table/zero_sum_subarray.js b/Hash Table/zero_sum_subarray.js new file mode 100644 index 00000000..6840a0cb --- /dev/null +++ b/Hash Table/zero_sum_subarray.js @@ -0,0 +1,35 @@ +/* + You're given a list of integers nums. Write a function that returns a boolean representing + whether there exists a zero-sum subarray of nums + + Sample Input : = [-5, -5, 2, 3, -2] + Output : True + The subarray [-5, 2, 3] has a sum of 0 + + Time and Space complexity : O(n) time | O(n) space - where n is the length of nums + + Approach: + The function takes in an array of integers nums. It initializes a set sums to keep track of the subarray sums we've seen so far, + and a variable currentSum to keep track of the current sum as we loop through the array. + + We then loop through each number in the array, adding the current number to the current sum. + We check if the current sum is zero or if we've seen it before (i.e. if it's already in the sums set). + If so, we've found a zero-sum subarray, so we return true. + + If we loop through the entire array without finding a zero-sum subarray, we return false. +*/ +function hasZeroSumSubarray(nums) { + const sums = new Set(); // Initialize a set to keep track of the subarray sums + let currentSum = 0; // Initialize a variable to keep track of the current sum + + for (const num of nums) { + // Loop through each number in the array + currentSum += num; // Add the current number to the current sum + if (currentSum === 0 || sums.has(currentSum)) { + // Check if the current sum is zero or if we've seen it before + return true; // If so, we've found a zero-sum subarray, so return true + } + sums.add(currentSum); // Add the current sum to the set of subarray sums + } + return false; // If we loop through the entire array without finding a zero-sum subarray, return false +} diff --git a/Hash Table/zero_sum_subarray.py b/Hash Table/zero_sum_subarray.py new file mode 100644 index 00000000..aec5aedd --- /dev/null +++ b/Hash Table/zero_sum_subarray.py @@ -0,0 +1,41 @@ +''' + You're given a list of integers nums. Write a function that returns a boolean representing + whether there exists a zero-sum subarray of nums + + Sample Input : = [-5, -5, 2, 3, -2] + Output : True + The subarray [-5, 2, 3] has a sum of 0 + + Approach: + Time and Space complexity : O(n) time | O(n) space - where n is the length of nums + This implementation uses an unordered map to keep track of the prefix sum of the input array nums. + We initialize the map with a key-value pair of 0 and -1, since a prefix sum of 0 indicates that the + subarray from index 0 to -1 (i.e., an empty subarray) has a sum of 0. We then iterate through the + input array nums, adding each element to the running sum sum and checking if the current sum is + already in the map. If it is, then we've found a subarray whose sum is 0, so we add the starting and + ending indices of the subarray to the result vector and break out of the loop. + If we reach the end of the loop without finding a zero sum subarray, then we return an empty vector. + Note that this implementation assumes that there is only one zero sum subarray in the input array. + If there could be multiple zero sum subarrays, then we would need to modify the implementation to + return all of them. +''' +def zero_sum_subarray(nums): + # Initialize a set to keep track of previously encountered prefix sums. + prefix_sums = set() + prefix_sum = 0 + + # Loop through each number in the array. + for num in nums: + # Add the current number to the running prefix sum. + prefix_sum += num + + # If the current prefix sum is in the set of previous prefix sums, + # then we have found a subarray whose sum is zero. + if prefix_sum in prefix_sums: + return True + + # Add the current prefix sum to the set of previous prefix sums. + prefix_sums.add(prefix_sum) + + # If no subarray whose sum is zero was found, return False. + return False diff --git a/HashTable/LongestSubstring.java b/HashTable/LongestSubstring.java deleted file mode 100644 index e4f76110..00000000 --- a/HashTable/LongestSubstring.java +++ /dev/null @@ -1,60 +0,0 @@ -import java.util.Set; -import java.util.HashSet; - -/* -Given a string s, find the length of the longest -substring without repeating characters. - - - -Example 1: - -Input: s = "abcabcbb" -Output: 3 -Explanation: The answer is "abc", with the length of 3. -Example 2: - -Input: s = "bbbbb" -Output: 1 -Explanation: The answer is "b", with the length of 1. -Example 3: - -Input: s = "pwwkew" -Output: 3 -Explanation: The answer is "wke", with the length of 3. -Notice that the answer must be a substring, "pwke" is a subsequence and not a substring. - - -Constraints: - -0 <= s.length <= 5 * 104 -s consists of English letters, digits, symbols and spaces. -*/ - -class Solution { - public int lengthOfLongestSubstring(String s) { - if (s == null || s.equals("")) - return 0; - - int start=0,end=0,maxLength=0; - Set uniqueCharacters = new HashSet<>(); - while (end < s.length()) { - if (uniqueCharacters.add(s.charAt(end))) { - end++; - maxLength = Math.max(maxLength, uniqueCharacters.size()); - } else { - uniqueCharacters.remove(s.charAt(start)); - start++; - } - } - return maxLength; - - } -} -class LongestSubstring { - public static void main(String[] args) { - Solution s=new Solution(); - String str="abcabcbb"; - System.out.println(s.lengthOfLongestSubstring(str)); - } -} \ No newline at end of file diff --git a/HashTable/SmallerNumberThanCurrent.java b/HashTable/SmallerNumberThanCurrent.java deleted file mode 100644 index 44371430..00000000 --- a/HashTable/SmallerNumberThanCurrent.java +++ /dev/null @@ -1,67 +0,0 @@ -import java.util.HashMap; -/* -Given the array nums, for each nums[i] find out how many numbers in the array are smaller than it. That is, for each nums[i] you have to count the number of valid j's such that j != i and nums[j] < nums[i]. - -Return the answer in an array. - - - -Example 1: - -Input: nums = [8,1,2,2,3] -Output: [4,0,1,1,3] -Explanation: -For nums[0]=8 there exist four smaller numbers than it (1, 2, 2 and 3). -For nums[1]=1 does not exist any smaller number than it. -For nums[2]=2 there exist one smaller number than it (1). -For nums[3]=2 there exist one smaller number than it (1). -For nums[4]=3 there exist three smaller numbers than it (1, 2 and 2). -Example 2: - -Input: nums = [6,5,4,8] -Output: [2,1,0,3] -Example 3: - -Input: nums = [7,7,7,7] -Output: [0,0,0,0] - - -Constraints: - -2 <= nums.length <= 500 -0 <= nums[i] <= 100 -*/ -class Solution { - public int[] smallerNumbersThanCurrent(int[] nums) { - int[] res= new int[nums.length]; - int count=0; - HashMap hm = new HashMap(); - for(int i = 0; i < nums.length ; i++) - { - for(int j = 0 ; j < nums.length ; j++) - { - if(nums[j] < nums[i]) - count++; - - } - hm.put(nums[i],count); - count = 0; - } - - for(int i = 0 ;i < nums.length ; i++) - res[i] = hm.get(nums[i]); - - return res; - } -} -class Main { - public static void main(String[] args) { - int arr[] = {1,2,2,7}; - Solution s = new Solution(); - int ans[] = s.smallerNumbersThanCurrent(arr); - for (int i = 0 ; i < ans.length ; i++){ - System.out.println(ans[i]); - } - - } -} \ No newline at end of file diff --git a/HashTable/col_sum.java b/HashTable/col_sum.java deleted file mode 100644 index 8f114a2c..00000000 --- a/HashTable/col_sum.java +++ /dev/null @@ -1,75 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * You are given a 2D integer matrix A, return a 1D integer array containing column-wise sums of original matrix. - * - * - * - * Problem Constraints - * 1 <= A.size() <= 103 - * - * 1 <= A[i].size() <= 103 - * - * 1 <= A[i][j] <= 103 - * - * - * - * Input Format - * First argument is a 2D array of integers.(2D matrix). - * - * - * - * Output Format - * Return an array conatining column-wise sums of original matrix. - * - * - * - * Example Input - * Input 1: - * - * [1,2,3,4] - * [5,6,7,8] - * [9,2,3,4] - * - * - * Example Output - * Output 1: - * - * {15,10,13,16} - * - * - * Example Explanation - * Explanation 1 - * - * Column 1 = 1+5+9 = 15 - * Column 2 = 2+6+2 = 10 - * Column 3 = 3+7+3 = 13 - * Column 4 = 4+8+4 = 16 - */ -public class ColSum { - public static void main(String[] args) { - List> array = new ArrayList<>(); - array.add(Arrays.asList(1, 2, 3, 4)); - array.add(Arrays.asList(5, 6, 7, 8)); - array.add(Arrays.asList(9, 2, 3, 4)); - - System.out.println((solve(3, 4, array))); - } - public static ArrayList solve (int row, int col, List> array) { - // O(row*col) time | O(col) space - ArrayList result = new ArrayList<>(); - - for (int colIdx = 0; colIdx < col; colIdx++) { - int currentSum = 0; - for (int rowIdx = 0; rowIdx < row; rowIdx++) { - int currentNum = array.get(rowIdx).get(colIdx); - currentSum += currentNum; - } - result.add(currentSum); - } - return result; - } - -} diff --git a/HashTable/equilbrium_array.java b/HashTable/equilbrium_array.java deleted file mode 100644 index ab634e40..00000000 --- a/HashTable/equilbrium_array.java +++ /dev/null @@ -1,56 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; - -/** - * You are given an array A of integers of size N. - * - * Your task is to find the equilibrium index of the given array - * - * The equilibrium index of an array is an index such that the sum of elements at lower indexes is equal to the sum of elements at higher indexes. - * - * NOTE: - * - * Array indexing starts from 0. - * If there is no equilibrium index then return -1. - * If there are more than one equilibrium indexes then return the minimum index. - * - * Input 1: - * A=[-7, 1, 5, 2, -4, 3, 0] - * Input 2: - * - * A=[1,2,3] - */ -public class EquilibriumOfAnArray { - public static void main(String[] args) { - ArrayList array = new ArrayList<>(Arrays.asList( - -7653, -3893, 2371, 4846, 5531, 7995, -9637, 2740, -5807, -5974, -8040, -5191, 2756, 7044, 1702, 2357, 6428, -3363, -7233, 356, 1161, -6762, 3844, -2591, 1683, -1529, -1485, 5264, 5837, 6942, -2790, 362, -3670, 8013, -882, 1014, 869, -4855, -5179, 2357, -8530, 3458, -3298, 9639, 9387, -3568, -4375, -2076, 6962, 1023, 6093, 7771, -4167, 5472, 710, -1886, -7533, 5588, 1830, -7054, -8271, 7956, 9231, -8723, 133, 5288, -7930, 6596, 9084, 3889, -1322, -9644, -1845, -6600, -3502, -1679, -524, -2646, -7516, 7477, 3345, -9345, 6552, -9659, -8228, 8736, -3801, 2717, -5218, 33, -9392, -737, -343, -5206, -5151, -192, 9857, -7362, 6713, 7524, 1892, 2156, -4224, 8030, -5094, 959, 9250, -4588, -4368, 3531, 5868, -9777, 7064, -5718, 6412, -189, -4323, -5987, 8161, 2709, 7433, 9648, -185, 270, -1299, -1976, -4157, -4372, -7090, -633, -9468, -8274, 9549, -6744, 2385, 8156, 5688, -792, -3338, 2283, 6503, -9786, 3878, 9541, -6152, 3785, 9396, -9695, -6004, 3621, -645, -3609, 2176, 6398, 1248, 2320, -4962, 5011, -8832, 6127, -7635, -6142, -4646, 3047, -2509, -4769, 4140, 5508, 9420, 8120, -2694, 6560, 8398, -100, 5759, 2696, 5696, 7748, -9611, 1007, -5228, 8574, 4507, -1011, 2723, -9726, 179, -2428, 9181, 4898, -8915, 7768, -5208, 8306, -2659, 3844, -661, 8452, 6041, 1380, 7817, 8973, 6751, -4815, 5347, -2711, 188, -5371, -679, -8278, 1903, -5038, -5791, -7893, 6515, -4994, 4527, -2608, -1213, 6028, 8742, -4275, -4817, -6160, 6422, -1766, -8639, 6205, -3150, 4615, 7417, 8710, -6074, -344, 4148, 1425, -632, -9160, 3297, -7114, 5159, 1386, 9770, 2347, -3587, -3875, -2635, 5048, -5901, -7484, 8975, -4308, 161, 299, -4049, -8815, -7762, 7018, -7943, 237, -6695, 7629, -7953, 9459, 4735, -3829, 9727, -6403, 5466, 6218, -5877, 2033, -4857, 1585, 514, -6989, 5236, -9830, -5191, 5947, 2560, -4052, -8077, -1288, 492, -4326, -492, 2294, -4923, -5192, 5162, 3137, 5975, 7399, -5645, 4187, -8523, 3651, -2419, 7813, 6036, -7307, 8254, 7936, -9467, 5581, -3412, 7572, 5229, 101, 1171, 8309, -6208, -8279, 444, -2281, -2046, -8015, 9570, -7134, 4339, 5946, 3592, -3576, -886, -4246, -610, 8529, 114, 6778, -7997, 7117, 7970, -9467, 1722, -1286, 3767, -930, 7682, -3814, -4258, -3810, -8109, -9843, 8266, -732, 6784, -8437, 2357, -4750, -7906, -9440, -4353, -7544, 8803, 5253, 5256, -5497, 8886, 4304, 8080, 908, 6009, 4940, -9357, 3402, -1661, -1435, 5537, -7720, 5460, -872, -1353, -5385, 9094, -4783, 9087, -8572, -1667, 1788, -7608, 2228, 6087, 2984, 7494, -7699, -2480, -3224, -7232, 4543, -6029, -2972, 9430, 8164, 1959, -2684, -2414, -8991, -3467, -4217, -1649, 837, 4336, -2265, -3976, 9518, -734, 4976, 3196, 8596, -6076, -1447, -4851, 4907, -478, -7859, -5003, 8428, -9053, 4681, -795, 3330, 5359, -5970, -220, -7393, 1088, -1559, -9193, 7574, 6186, -9753, -9647, 7090, 443, -4809, 3298, 6116, 2494, 7231, 3493, -6231, -6764, -6311, 5140, 5977, 4169, 5221, 4568, -1875, 7542, 9705, 2771, -718, 3135, 5548, -1085, 3003, -3784, -2730, -6471, 9204, 9575, -8391, -3986, 1410, -5961, 4005, -6029, -6209, 8290, 2692, 3424, 5242, -8314, 4330, -2775, 4755, 8850, 2378, 8147, 5597, 8121, -5413, 7104, 9328, 5535, 0, 9065, -4788, -1505, 2202, -8444, 5989, 7361, 9707, -7802, 3466, -8042, 2077, -8845, -66, -7741, 5097, -268, -4540, -3439, -9265, -2806, 6926, 4592, 3148, -7634, 2777, -9401, -6748, -9755, 7814, -7294, -2509, -820, 338, 7721, 4314, 5798, 5146, -9934, -1057, 8088, 4854, 9482, 719, 7099, -5376, -2543, 587, 2026, -5367, -4480, 2011, -3743, 5779, -9267, -7509, 8485, -666, 1450, -4380, -1108, -2459, -7715, -3057, -1689, 436, 2696, -6039, -1375, -9400, 4052, 5780, 2796, -4295, 3960, 855, 548, -5908, -5673, -8366, 7366, -3138, -3512, 9567, -7559, 9387, -7031, 9293, -4569, -2683, -1176, 7786, -3516, -2594, 4886, 4669, 2079, -6810, -1645, -1342, 3529, 2070, -6946, 6439, 952, -1495, -4243, 8994, -6882 - )); - System.out.println(solve(array)); - } - public static int solve(ArrayList array) { - // O(n) time | O(n) space - /** - * 1. find prefix sum. - * 2. check for equilibrium - */ - System.out.println(array); - ArrayList prefixSum = new ArrayList<>(); - - prefixSum.add(array.get(0)); - for (int i = 1; i < array.size(); i++) { - int currentPrefixSum = prefixSum.get(i - 1) + array.get(i); - prefixSum.add(currentPrefixSum); - } - - for (int i = 0; i < array.size(); i++) { - if (i == 0 ) { - if (prefixSum.get(prefixSum.size() - 1) - prefixSum.get(i) == 0) return i; - continue; - } - if (prefixSum.get(i - 1) == prefixSum.get(prefixSum.size() - 1) - prefixSum.get(i)) - return i; - } - return -1; - } - -} diff --git a/HashTable/first_repeating_character.go b/HashTable/first_repeating_character.go deleted file mode 100644 index 97143f3e..00000000 --- a/HashTable/first_repeating_character.go +++ /dev/null @@ -1,30 +0,0 @@ -// Give an algorithm for printing the first repeated character if there are duplicated elements in it - -package main - -import "fmt" - -// FirstRepeatedCharacter: returns the first repeating character -// Approach: we know extended ascii has 256 values, so create an array with length 256 -// initialize with all zeroes. -// Each of input character in word go to the corresponding position and increment its count -// while scanning the array if we see some value is already 1 then return the value -func FirstRepeatedCharacter(word string) byte { - lengthOfString := len(word) - // The maximum number of characters that can be represented in extended ASCII is 256 - asciiArr := [256]int{} - for i := 0; i < 256; i++ { - asciiArr[i] = 0 - } - for i := 0; i < lengthOfString; i++ { - if asciiArr[word[i]] == 1 { - return word[i] - } - asciiArr[word[i]]++ - } - return byte(0) -} - -func main() { - fmt.Printf("%c", FirstRepeatedCharacter("abcdefghe")); -} \ No newline at end of file diff --git a/HashTable/row_sum.java b/HashTable/row_sum.java deleted file mode 100644 index 3844877f..00000000 --- a/HashTable/row_sum.java +++ /dev/null @@ -1,71 +0,0 @@ -import java.util.*; -/** - * You are given a 2D integer matrix A, return a 1D integer array containing row-wise sums of original matrix. - * - * - * - * Problem Constraints - * - * 1 <= A.size() <= 103 - * - * 1 <= A[i].size() <= 103 - * - * 1 <= A[i][j] <= 103 - * - * - * - * Input Format - * First argument A is a 2D array of integers.(2D matrix). - * - * - * - * Output Format - * Return an array conatining row-wise sums of original matrix. - * - * Example Input - * Input 1: - * - * [1,2,3,4] - * [5,6,7,8] - * [9,2,3,4] - * - * - * Example Output - * Output 1: - * - * [10,26,18] - * - * - * Example Explanation - * Explanation 1 - * - * Row 1 = 1+2+3+4 = 10 - * Row 2 = 5+6+7+8 = 26 - * Row 3 = 9+2+3+4 = 18 - */ - -public class RowSum { - public static void main(String[] args) { - List> array = new ArrayList<>(); - array.add(Arrays.asList(1, 2, 3, 4)); - array.add(Arrays.asList(5, 6, 7, 8)); - array.add(Arrays.asList(9, 2, 3, 4)); - - System.out.println((solve(3, 4, array))); - } - public static ArrayList solve (int row, int col, List> array) { - // O(row*col) time | O(row) space - ArrayList result = new ArrayList<>(); - - for (int rowIdx = 0; rowIdx < row; rowIdx++) { - int currentSum = 0; - for (int colIdx = 0; colIdx < col; colIdx++) { - int currentNum = array.get(rowIdx).get(colIdx); - currentSum += currentNum; - } - result.add(currentSum); - } - return result; - } - -} diff --git a/Heaps/heap.cpp b/Heaps/heap.cpp new file mode 100644 index 00000000..3dbf3431 --- /dev/null +++ b/Heaps/heap.cpp @@ -0,0 +1,131 @@ +/* + Implement a Min-Heap class that supports + + Building a Min Heap from an input array of integers. + Inserting integers in the heap. + Removing the heap's minimum / root value. + Peeking at the heap's minimum / root value. + Sifting integers up and down the heap, which is to be used when inserting and removing values. + + Note that the heap should be represented in the form of an array. + + Explanation: + + The code snippet implements a MinHeap data structure in Go. + + - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. + It calls the `BuildHeap` method to construct the heap structure. + - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the + last non-leaf node. + - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. + - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. + It compares the element with its parent and swaps it if necessary. + - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. + - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, + removes the last element from the heap, and then calls `siftDown` to maintain the heap property. + - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then + calls `siftUp` to maintain the heap property. + - `swap`: This method swaps two elements in the heap given their indices. + - `length`: This method returns the number of elements in the heap. + + Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, + and retrieval of the minimum element. + + BuildHeap: O(n) time | O(1) space - where n is the length of the input array + SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap + SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap + Peek: O(1) time | O(1) space + Remove: O(log(n)) time | O(1) space - where n is the length of the heap + Insert: O(log(n)) time | O(1) space - where n is the length of the heap + +*/ +#include + +class MinHeap { +public: + std::vector heap; // The heap represented as a vector + + void buildHeap(std::vector& array) { + int first = (array.size() - 2) / 2; // Start from the last parent node + for (int currentIdx = first; currentIdx >= 0; currentIdx--) { + siftDown(currentIdx, array.size() - 1); + } + } + + void siftDown(int currentIndex, int endIndex) { + int childOneIdx = currentIndex * 2 + 1; // Calculate the index of the first child + while (childOneIdx <= endIndex) { + int childTwoIdx = -1; // Initialize the index of the second child + if (currentIndex * 2 + 2 <= endIndex) { + childTwoIdx = currentIndex * 2 + 2; // Calculate the index of the second child if it exists + } + int indexToSwap = childOneIdx; // Assume the first child is the one to swap with + if (childTwoIdx > -1 && heap[childOneIdx] > heap[childTwoIdx]) { + // If the second child exists and is smaller, update the index to swap with + indexToSwap = childTwoIdx; + } + if (heap[currentIndex] > heap[indexToSwap]) { + // If the current element is greater than the one to swap with, perform the swap + swap(currentIndex, indexToSwap); + currentIndex = indexToSwap; + childOneIdx = currentIndex * 2 + 1; // Update the index of the first child + } else { + return; + } + } + } + + void siftUp() { + int currentIdx = heap.size() - 1; // Start from the last element + int parentIdx = (currentIdx - 1) / 2; // Calculate the index of the parent + while (currentIdx > 0) { + int current = heap[currentIdx]; + int parent = heap[parentIdx]; + if (current < parent) { + // If the current element is smaller than the parent, perform the swap + swap(currentIdx, parentIdx); + currentIdx = parentIdx; + parentIdx = (currentIdx - 1) / 2; // Update the index of the parent + } else { + return; + } + } + } + + int peek() { + if (heap.empty()) { + return -1; + } + return heap[0]; // Return the minimum element at the top of the heap + } + + int remove() { + int l = heap.size(); + swap(0, l - 1); // Swap the root with the last element + int peeked = heap[l - 1]; // Remove the last element (minimum) and store it + heap.pop_back(); + siftDown(0, l - 2); // Sift down the new root element + return peeked; + } + + void insert(int value) { + heap.push_back(value); // Append the new element to the end of the heap + siftUp(); // Sift up the new element to its correct position + } + + void swap(int i, int j) { + int temp = heap[i]; + heap[i] = heap[j]; + heap[j] = temp; // Swap elements at indices i and j + } + + int length() { + return heap.size(); // Return the number of elements in the heap + } +}; + +MinHeap* newMinHeap(std::vector& array) { + MinHeap* heap = new MinHeap(); // Create a new MinHeap object + heap->buildHeap(array); // Build the heap using the given array + return heap; +} diff --git a/Heaps/heap.go b/Heaps/heap.go index e91bbe04..30198d5a 100644 --- a/Heaps/heap.go +++ b/Heaps/heap.go @@ -1,147 +1,137 @@ -package main +/* + Implement a Min-Heap class that supports -import ( - "fmt" - "math" -) + Building a Min Heap from an input array of integers. + Inserting integers in the heap. + Removing the heap's minimum / root value. + Peeking at the heap's minimum / root value. + Sifting integers up and down the heap, which is to be used when inserting and removing values. -// Item: Defines the interface for an element to be held by a Heap instance -type Item interface { - Less(item Item) bool -} -// Heap: binary heap with support for min heap operations -type Heap struct { - size int - data []Item -} + Note that the heap should be represented in the form of an array. -// New: returns a pointer to an empty min-heap -func New() *Heap { - return &Heap{} -} + Explanation: -// Parent: For a node at ith location its parent is ar (i - 1) / 2 location -func Parent(i int) int { - return int(math.Floor(float64(i - 1) / 2.0)) -} + The code snippet implements a MinHeap data structure in Go. -// LeftChild: For a node at ith location its left children is at 2 * i + 1 location -func LeftChild(parent int) int { - return (2 * parent) + 1 -} + - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. + It calls the `BuildHeap` method to construct the heap structure. + - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the + last non-leaf node. + - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. + - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. + It compares the element with its parent and swaps it if necessary. + - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. + - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, + removes the last element from the heap, and then calls `siftDown` to maintain the heap property. + - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then + calls `siftUp` to maintain the heap property. + - `swap`: This method swaps two elements in the heap given their indices. + - `length`: This method returns the number of elements in the heap. -// RightChild: For a node at ith location its right children is at 2 * i + 2 th locations -func RightChild(parent int) int { - return (2 * parent) + 2 -} + Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, + and retrieval of the minimum element. -// GetMinimum: Minimum element is always at root -func GetMinimum(h *Heap) (Item, error) { - if h.size == 0 { - return nil, fmt.Errorf("Unable to get element from empty heap") - } - return h.data[0], nil + BuildHeap: O(n) time | O(1) space - where n is the length of the input array + SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap + SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap + Peek: O(1) time | O(1) space + Remove: O(log(n)) time | O(1) space - where n is the length of the heap + Insert: O(log(n)) time | O(1) space - where n is the length of the heap + +*/ +package main + +// MinHeap represents a min heap data structure. +type MinHeap []int + +// NewMinHeap creates a new MinHeap from an input array and returns a pointer to it. +func NewMinHeap(array []int) *MinHeap { + // Create a heap from the input array + heap := MinHeap(array) + ptr := &heap + // Build the heap structure + ptr.BuildHeap(array) + return ptr } -// Note: Deleting an element uses percolateUp, and inserting an element uses percolateDown. -// PercolateUp: move from bottom to top -// Heap is a complete binary tree and in the worst case we start at the root and come -// down to the leaf. This is equal to the height of the complete binary tree. -// Time Complexity: O(log n) Space Complexity: O(1). -func (h *Heap) percolateUp() { - idx := h.size - if idx <= 0 { - return - } - for { - p := Parent(idx) - if p < 0 || h.data[p].Less(h.data[idx]) { - break - } - swap(h, p, idx) - idx = p +// BuildHeap constructs the heap by calling siftDown on each parent node. +func (h *MinHeap) BuildHeap(array []int) { + // Calculate the index of the first parent node + first := (len(array) - 2) / 2 + // Iterate over each parent node and sift it down + for currentIdx := first + 1; currentIdx >= 0; currentIdx-- { + h.siftDown(currentIdx, len(array)-1) } } -// To delete an element from heap, we just need to delete the element from the root. This is the only operation -// (maximum element) supported by standard heap. After deleting the root element, copy the last element of the heap -// (tree) and delete that last element. -// After replacing the last element, the tree may not satisfy the heap property. To make it heap again, call the -// percolateDown function. -// 1 Copy the first element into some variable -// 2 Copy the last element into first element location -// 3 percolateDown the first element -// Time Complexity: O(log n) Space Complexity: O(1). -func (h *Heap) percolateDown(i int) { - p := i - for { - l := LeftChild(p) - r := RightChild(p) - s := p - if l < h.size && h.data[l].Less(h.data[s]) { - s = l +// siftDown moves an element down the heap until it reaches its correct position. +func (h *MinHeap) siftDown(currentIndex, endIndex int) { + childOneIdx := currentIndex*2 + 1 + for childOneIdx <= endIndex { + childTwoIdx := -1 + if currentIndex*2+2 <= endIndex { + childTwoIdx = currentIndex*2 + 2 } - if r < h.size && h.data[r].Less(h.data[s]) { - s = r + indexToSwap := childOneIdx + if childTwoIdx > -1 && (*h)[childOneIdx] > (*h)[childTwoIdx] { + indexToSwap = childTwoIdx } - if s == p { - break + if (*h)[currentIndex] > (*h)[indexToSwap] { + h.swap(currentIndex, indexToSwap) + currentIndex = indexToSwap + childOneIdx = currentIndex*2 + 1 + } else { + return } - swap(h, p, s) - p = s } } -func swap(h *Heap, i int, j int) { - temp := h.data[i] - h.data[i] = h.data[j] - h.data[j] = temp +// siftUp moves an element up the heap until it reaches its correct position. +func (h *MinHeap) siftUp() { + currentIdx := h.length() - 1 + parentIdx := (currentIdx - 1) / 2 + for currentIdx > 0 { + current, parent := (*h)[currentIdx], (*h)[parentIdx] + if current < parent { + h.swap(currentIdx, parentIdx) + currentIdx = parentIdx + parentIdx = (currentIdx - 1) / 2 + } else { + return + } + } } -// Extract - removes and returns the 'item' at the top of the heap, maintaining the min-heap invariant -func (h *Heap) Extract() (Item, error) { - n := h.size - if n == 0 { - return nil, fmt.Errorf("Unable to extract from empty Heap") - } - m := h.data[0] - h.data[0] = h.data[n-1] - h.data = h.data[:n-1] - h.size-- - if h.size > 0 { - h.percolateDown(0) - } else { - h.data = nil +// Peek returns the minimum element in the heap without removing it. +func (h MinHeap) Peek() int { + if len(h) == 0 { + return -1 } - return m, nil + return h[0] } -// Insert - inserts 'item' into the Heap, maintaining the min-heap -func (h *Heap) Insert(item Item) { - if h.size == 0 { - h.data = make([]Item, 1) - h.data[0] = item - } else { - h.data = append(h.data, item) - } - h.size++ - h.percolateUp() +// Remove removes and returns the minimum element in the heap. +func (h *MinHeap) Remove() int { + l := h.length() + h.swap(0, l-1) + peeked := (*h)[l-1] + *h = (*h)[:l-1] + h.siftDown(0, l-1) + return peeked } -// Heapify - returns a pointer to a min-heap composed of the elements of 'items' -// One simple approach for building the heap is, take n input items and place them into an empty heap. This can be -// done with n successive inserts and takes O(nlogn) in the worst case. This is due to the fact that each insert -// operation takes O(logn). -func Heapify(items []Item) *Heap { - h := New() - n := len(items) - h.data = make([]Item, n) - copy(h.data, items) - h.size = len(items) - i := int(n/2) - for i >= 0 { - h.percolateDown(i) - i-- - } - return h -} \ No newline at end of file +// Insert inserts a new element into the heap. +func (h *MinHeap) Insert(value int) { + *h = append(*h, value) + h.siftUp() +} + +// swap swaps two elements in the heap given their indices. +func (h MinHeap) swap(i, j int) { + h[i], h[j] = h[j], h[i] +} + +// length returns the number of elements in the heap. +func (h MinHeap) length() int { + return len(h) +} diff --git a/Heaps/heap.java b/Heaps/heap.java new file mode 100644 index 00000000..d854bf90 --- /dev/null +++ b/Heaps/heap.java @@ -0,0 +1,151 @@ +/* + Implement a Min-Heap class that supports + + Building a Min Heap from an input array of integers. + Inserting integers in the heap. + Removing the heap's minimum / root value. + Peeking at the heap's minimum / root value. + Sifting integers up and down the heap, which is to be used when inserting and removing values. + + Note that the heap should be represented in the form of an array. + + Explanation: + + The code snippet implements a MinHeap data structure in Go. + + - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. + It calls the `BuildHeap` method to construct the heap structure. + - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the + last non-leaf node. + - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. + - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. + It compares the element with its parent and swaps it if necessary. + - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. + - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, + removes the last element from the heap, and then calls `siftDown` to maintain the heap property. + - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then + calls `siftUp` to maintain the heap property. + - `swap`: This method swaps two elements in the heap given their indices. + - `length`: This method returns the number of elements in the heap. + + Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, + and retrieval of the minimum element. + + BuildHeap: O(n) time | O(1) space - where n is the length of the input array + SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap + SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap + Peek: O(1) time | O(1) space + Remove: O(log(n)) time | O(1) space - where n is the length of the heap + Insert: O(log(n)) time | O(1) space - where n is the length of the heap + +*/ +import java.util.Arrays; + +public class MinHeap { + private int[] heap; // The heap represented as an array + private int size; // The current size of the heap + + public MinHeap(int[] array) { + heap = Arrays.copyOf(array, array.length); // Create a copy of the input array + size = array.length; + buildHeap(); // Build the heap + } + + private void buildHeap() { + int first = (size - 2) / 2; // Start from the last parent node + for (int currentIdx = first; currentIdx >= 0; currentIdx--) { + siftDown(currentIdx); + } + } + + private void siftDown(int currentIndex) { + int childOneIdx = currentIndex * 2 + 1; // Calculate the index of the first child + while (childOneIdx < size) { + int childTwoIdx = -1; // Initialize the index of the second child + if (currentIndex * 2 + 2 < size) { + childTwoIdx = currentIndex * 2 + 2; // Calculate the index of the second child if it exists + } + int indexToSwap = childOneIdx; // Assume the first child is the one to swap with + if (childTwoIdx > -1 && heap[childOneIdx] > heap[childTwoIdx]) { + // If the second child exists and is smaller, update the index to swap with + indexToSwap = childTwoIdx; + } + if (heap[currentIndex] > heap[indexToSwap]) { + // If the current element is greater than the one to swap with, perform the swap + swap(currentIndex, indexToSwap); + currentIndex = indexToSwap; + childOneIdx = currentIndex * 2 + 1; // Update the index of the first child + } else { + return; + } + } + } + + private void siftUp() { + int currentIdx = size - 1; // Start from the last element + int parentIdx = (currentIdx - 1) / 2; // Calculate the index of the parent + while (currentIdx > 0) { + int current = heap[currentIdx]; + int parent = heap[parentIdx]; + if (current < parent) { + // If the current element is smaller than the parent, perform the swap + swap(currentIdx, parentIdx); + currentIdx = parentIdx; + parentIdx = (currentIdx - 1) / 2; // Update the index of the parent + } else { + return; + } + } + } + + public int peek() { + if (size == 0) { + return -1; + } + return heap[0]; // Return the minimum element at the top of the heap + } + + public int remove() { + swap(0, size - 1); // Swap the root with the last element + int peeked = heap[size - 1]; // Remove the last element (minimum) and store it + size--; + siftDown(0); // Sift down the new root element + return peeked; + } + + public void insert(int value) { + if (size >= heap.length) { + heap = Arrays.copyOf(heap, size * 2); // Resize the array if necessary + } + heap[size] = value; // Append the new element to the end of the heap + size++; + siftUp(); // Sift up the new element to its correct position + } + + private void swap(int i, int j) { + int temp = heap[i]; + heap[i] = heap[j]; + heap[j] = temp; // Swap elements at indices i and j + } + + public int length() { + return size; // Return the number of elements in the heap + } +} + +public class Main { + public static void main(String[] args) { + int[] array = { 9, 4, 7, 1, -2, 6, 5 }; + MinHeap minHeap = new MinHeap(array); + + System.out.println("Peek: " + minHeap.peek()); + System.out.println("Remove: " + minHeap.remove()); + System.out.println("Length: " + minHeap.length()); + + minHeap.insert(2); + minHeap.insert(-5); + + System.out.println("Peek: " + minHeap.peek()); + System.out.println("Length: " + minHeap.length()); + } +} diff --git a/Heaps/heap.js b/Heaps/heap.js new file mode 100644 index 00000000..4fbcd714 --- /dev/null +++ b/Heaps/heap.js @@ -0,0 +1,139 @@ +/* + Implement a Min-Heap class that supports + + Building a Min Heap from an input array of integers. + Inserting integers in the heap. + Removing the heap's minimum / root value. + Peeking at the heap's minimum / root value. + Sifting integers up and down the heap, which is to be used when inserting and removing values. + + Note that the heap should be represented in the form of an array. + + Explanation: + + The code snippet implements a MinHeap data structure in Go. + + - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. + It calls the `BuildHeap` method to construct the heap structure. + - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the + last non-leaf node. + - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. + - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. + It compares the element with its parent and swaps it if necessary. + - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. + - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, + removes the last element from the heap, and then calls `siftDown` to maintain the heap property. + - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then + calls `siftUp` to maintain the heap property. + - `swap`: This method swaps two elements in the heap given their indices. + - `length`: This method returns the number of elements in the heap. + + Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, + and retrieval of the minimum element. + + BuildHeap: O(n) time | O(1) space - where n is the length of the input array + SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap + SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap + Peek: O(1) time | O(1) space + Remove: O(log(n)) time | O(1) space - where n is the length of the heap + Insert: O(log(n)) time | O(1) space - where n is the length of the heap + +*/ +class MinHeap { + constructor(array) { + this.heap = array.slice(); // Create a copy of the input array + this.size = array.length; + this.buildHeap(); // Build the heap + } + + buildHeap() { + const first = Math.floor((this.size - 2) / 2); // Start from the last parent node + for (let currentIdx = first; currentIdx >= 0; currentIdx--) { + this.siftDown(currentIdx); + } + } + + siftDown(currentIndex) { + let childOneIdx = currentIndex * 2 + 1; // Calculate the index of the first child + while (childOneIdx < this.size) { + let childTwoIdx = -1; // Initialize the index of the second child + if (currentIndex * 2 + 2 < this.size) { + childTwoIdx = currentIndex * 2 + 2; // Calculate the index of the second child if it exists + } + let indexToSwap = childOneIdx; // Assume the first child is the one to swap with + if (childTwoIdx > -1 && this.heap[childOneIdx] > this.heap[childTwoIdx]) { + // If the second child exists and is smaller, update the index to swap with + indexToSwap = childTwoIdx; + } + if (this.heap[currentIndex] > this.heap[indexToSwap]) { + // If the current element is greater than the one to swap with, perform the swap + this.swap(currentIndex, indexToSwap); + currentIndex = indexToSwap; + childOneIdx = currentIndex * 2 + 1; // Update the index of the first child + } else { + return; + } + } + } + + siftUp() { + let currentIdx = this.size - 1; // Start from the last element + let parentIdx = Math.floor((currentIdx - 1) / 2); // Calculate the index of the parent + while (currentIdx > 0) { + const current = this.heap[currentIdx]; + const parent = this.heap[parentIdx]; + if (current < parent) { + // If the current element is smaller than the parent, perform the swap + this.swap(currentIdx, parentIdx); + currentIdx = parentIdx; + parentIdx = Math.floor((currentIdx - 1) / 2); // Update the index of the parent + } else { + return; + } + } + } + + peek() { + if (this.size === 0) { + return -1; + } + return this.heap[0]; // Return the minimum element at the top of the heap + } + + remove() { + this.swap(0, this.size - 1); // Swap the root with the last element + const peeked = this.heap[this.size - 1]; // Remove the last element (minimum) and store it + this.size--; + this.heap.length = this.size; // Resize the heap array + this.siftDown(0); // Sift down the new root element + return peeked; + } + + insert(value) { + this.heap.push(value); // Append the new element to the end of the heap + this.size++; + this.siftUp(); // Sift up the new element to its correct position + } + + swap(i, j) { + [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]]; // Swap elements at indices i and j + } + + length() { + return this.size; // Return the number of elements in the heap + } +} + +// Example usage: +const array = [9, 4, 7, 1, -2, 6, 5]; +const minHeap = new MinHeap(array); + +console.log("Peek:", minHeap.peek()); +console.log("Remove:", minHeap.remove()); +console.log("Length:", minHeap.length()); + +minHeap.insert(2); +minHeap.insert(-5); + +console.log("Peek:", minHeap.peek()); +console.log("Length:", minHeap.length()); diff --git a/Heaps/heap.py b/Heaps/heap.py new file mode 100644 index 00000000..76ba70ad --- /dev/null +++ b/Heaps/heap.py @@ -0,0 +1,109 @@ +''' + Implement a Min-Heap class that supports + + Building a Min Heap from an input array of integers. + Inserting integers in the heap. + Removing the heap's minimum / root value. + Peeking at the heap's minimum / root value. + Sifting integers up and down the heap, which is to be used when inserting and removing values. + + Note that the heap should be represented in the form of an array. + + Explanation: + + The code snippet implements a MinHeap data structure in Go. + + - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. + It calls the `BuildHeap` method to construct the heap structure. + - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the + last non-leaf node. + - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. + - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. + It compares the element with its parent and swaps it if necessary. + - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. + - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, + removes the last element from the heap, and then calls `siftDown` to maintain the heap property. + - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then + calls `siftUp` to maintain the heap property. + - `swap`: This method swaps two elements in the heap given their indices. + - `length`: This method returns the number of elements in the heap. + + Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, + and retrieval of the minimum element. + + BuildHeap: O(n) time | O(1) space - where n is the length of the input array + SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap + SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap + Peek: O(1) time | O(1) space + Remove: O(log(n)) time | O(1) space - where n is the length of the heap + Insert: O(log(n)) time | O(1) space - where n is the length of the heap + +''' +class MinHeap: + def __init__(self): + self.heap = [] # The heap represented as a list + + def build_heap(self, array): + # Build the heap by calling sift_down on each parent node + first = (len(array) - 2) // 2 # Start from the last parent node + for current_idx in range(first, -1, -1): + self.sift_down(current_idx, len(array) - 1) + + def sift_down(self, current_idx, end_idx): + child_one_idx = current_idx * 2 + 1 # Calculate the index of the first child + while child_one_idx <= end_idx: + child_two_idx = -1 # Initialize the index of the second child + if current_idx * 2 + 2 <= end_idx: + child_two_idx = current_idx * 2 + 2 # Calculate the index of the second child if it exists + index_to_swap = child_one_idx # Assume the first child is the one to swap with + if child_two_idx > -1 and self.heap[child_one_idx] > self.heap[child_two_idx]: + # If the second child exists and is smaller, update the index to swap with + index_to_swap = child_two_idx + if self.heap[current_idx] > self.heap[index_to_swap]: + # If the current element is greater than the one to swap with, perform the swap + self.swap(current_idx, index_to_swap) + current_idx = index_to_swap + child_one_idx = current_idx * 2 + 1 # Update the index of the first child + else: + return + + def sift_up(self): + current_idx = len(self.heap) - 1 # Start from the last element + parent_idx = (current_idx - 1) // 2 # Calculate the index of the parent + while current_idx > 0: + current, parent = self.heap[current_idx], self.heap[parent_idx] + if current < parent: + # If the current element is smaller than the parent, perform the swap + self.swap(current_idx, parent_idx) + current_idx = parent_idx + parent_idx = (current_idx - 1) // 2 # Update the index of the parent + else: + return + + def peek(self): + if not self.heap: + return -1 + return self.heap[0] # Return the minimum element at the top of the heap + + def remove(self): + l = len(self.heap) + self.swap(0, l - 1) # Swap the root with the last element + peeked = self.heap.pop() # Remove the last element (minimum) and store it + self.sift_down(0, l - 2) # Sift down the new root element + return peeked + + def insert(self, value): + self.heap.append(value) # Append the new element to the end of the heap + self.sift_up() # Sift up the new element to its correct position + + def swap(self, i, j): + self.heap[i], self.heap[j] = self.heap[j], self.heap[i] # Swap elements at indices i and j + + def length(self): + return len(self.heap) # Return the number of elements in the heap + + +def new_min_heap(array): + heap = MinHeap() # Create a new MinHeap object + heap.build_heap(array) # Build the heap using the given array + return heap diff --git a/Heaps/k_closest.py b/Heaps/k_closest.py new file mode 100644 index 00000000..dda20a07 --- /dev/null +++ b/Heaps/k_closest.py @@ -0,0 +1,17 @@ +class Solution: + def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: + min_heap = [] + + # For each point, find distance from origin + for x,y in points: + dist = sqrt(x**2 + y**2) + min_heap.append([dist,x,y]) + + # Pop 'k' smallest distances + heapify(min_heap) + res = [] + for _ in range(k): + _,x,y = heappop(min_heap) + res.append([x,y]) + + return res \ No newline at end of file diff --git a/Leetcode/Graphs_flood_fill.cpp b/Leetcode/Graphs_flood_fill.cpp deleted file mode 100644 index ccdab3ff..00000000 --- a/Leetcode/Graphs_flood_fill.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// Flood fill, also called seed fill, is a flooding algorithm that determines and alters the area connected to a given node -// in a multi-dimensional array with some matching attribute. It is used in the "bucket" fill tool of paint programs to fill -// connected, similarly-colored areas with a different color, and in games such as Go and Minesweeper for determining -// which pieces are cleared. Source(https://en.wikipedia.org/wiki/Flood_fill) -#include -using namespace std; -int R, C; -void print_matrix(char input[][50]){ - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cout << input[i][j]; - } - cout << "\n"; - } -} -int dx[] = {-1, 0, 1, 0}; -int dy[] = {0, -1, 0, 1}; -void flood_fill(char input[][50], int i, int j, char ch, char color){ - // base case : make sure we do not cross boundries of input matrix - if(i < 0 || j < 0 || i >= R || j >= C) - return; - // if we are coming to a cell that is not equal to ch then we dont do anything - if(input[i][j] != ch) - return; - // fill the color - input[i][j] = color; - - // dfs flood_fill on all directions - for(int k = 0; k < 4; k++){ - flood_fill(input, i + dx[k], j + dy[k], ch, color); - } -} -int main(){ - cin >> R >> C; - char input[15][50]; - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cin >> input[i][j]; - } - } - print_matrix(input); - flood_fill(input, 8, 13, '.', 'r'); - print_matrix(input); - flood_fill(input, 2, 15, '.', 'b'); - print_matrix(input); -return 0; -} - -/* -Input -15 30 -.............................. -.............#####............ -.............#...#............ -.....#########...#######...... -....###.....######.....###.... -...##....................##... -..##......................#... -..##.....................##... -..###...................##.... -....###................###.... -......###............###...... -........###........###........ -..........##########.......... -.............................. -...........A.P.P.L.E.......... -Output : -.............................. -.............#####............ -.............#...#............ -.....#########...#######...... -....###.....######.....###.... -...##....................##... -..##......................#... -..##.....................##... -..###...................##.... -....###................###.... -......###............###...... -........###........###........ -..........##########.......... -.............................. -...........A.P.P.L.E.......... -.............................. -.............#####............ -.............#...#............ -.....#########...#######...... -....###rrrrr######rrrrr###.... -...##rrrrrrrrrrrrrrrrrrrr##... -..##rrrrrrrrrrrrrrrrrrrrrr#... -..##rrrrrrrrrrrrrrrrrrrrr##... -..###rrrrrrrrrrrrrrrrrrr##.... -....###rrrrrrrrrrrrrrrr###.... -......###rrrrrrrrrrrr###...... -........###rrrrrrrr###........ -..........##########.......... -.............................. -...........A.P.P.L.E.......... -.............................. -.............#####............ -.............#bbb#............ -.....#########bbb#######...... -....###rrrrr######rrrrr###.... -...##rrrrrrrrrrrrrrrrrrrr##... -..##rrrrrrrrrrrrrrrrrrrrrr#... -..##rrrrrrrrrrrrrrrrrrrrr##... -..###rrrrrrrrrrrrrrrrrrr##.... -....###rrrrrrrrrrrrrrrr###.... -......###rrrrrrrrrrrr###...... -........###rrrrrrrr###........ -..........##########.......... -.............................. -...........A.P.P.L.E.......... -*/ \ No newline at end of file diff --git a/Leetcode/Graphs_jump.cpp b/Leetcode/Graphs_jump.cpp deleted file mode 100644 index 42b3139e..00000000 --- a/Leetcode/Graphs_jump.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - Given an array of non-negative integers arr, - you are initially positioned at start index of the array. - When you are at index i, you can jump to i + arr[i] or i - arr[i], - check if you can reach to any index with value 0. - Notice that you can not jump outside of the array at any time. - Input: arr = [4,2,3,0,3,1,2], start = 5 - Output: true -*/ -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -template -class Graph{ - map > L; - public: - Graph(){ - - } - void add_edge(T u, T v, bool bidir = false){ - L[u].push_back(v); - if(bidir) - L[v].push_back(u); - } - void print_adj_list(){ - for(auto x : L){ - cout << x.first << "->"; - for(auto element : x.second){ - cout << element << ","; - } - cout << endl; - } - } - bool can_jump(T source, T destination){ - queue q; - map visited; - q.push(source); - visited[source] = true; - while(!q.empty()){ - T node = q.front(); - q.pop(); - for(T neighbours : L[node]){ - if(!visited[neighbours]){ - visited[neighbours] = true; - q.push(neighbours); - } - - } - } - if(!visited[destination]){ - return false; - } - return true; - } -}; -int main(){ - Graph g; - int n, starting_node; - - cin >> n >> starting_node; - vector vertices(n); - int pos_of_zero = 3; - - for(int i = 0; i < n; i++){ - cin >> vertices[i]; - } - for(int i = 0; i < n; i++){ - cout << vertices[i] << ","; - } - cout << endl; - g.add_edge(0, 0 + vertices[0]); - for(int i = 1; i < n - 1; i++){ - if(vertices[i] == 0) - continue; - if(i + vertices[i] > 0 && i + vertices[i] < n){ - g.add_edge(i, i + vertices[i]); - } - if(i - vertices[i] >= 0 && i - vertices[i] < n-1){ - g.add_edge(i, i - vertices[i]); - } - } - g.add_edge(n-1, (n-1) - vertices[n - 1]); - g.print_adj_list(); - cout << endl; - if(g.can_jump(starting_node, pos_of_zero)) - cout << "YES"; - else - cout << "No"; - - return 0; -} \ No newline at end of file diff --git a/Leetcode/Graphs_kill_process.cpp b/Leetcode/Graphs_kill_process.cpp deleted file mode 100644 index ff64d407..00000000 --- a/Leetcode/Graphs_kill_process.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - Given a list of process where each process has a unique id and parent id. - Parent id is the id of the process that initiated that process. - You have to kill a particular process given by an integer kill. - Print id of all the processes that will be killed to kill that process - In order to kill a process, all its child processes should be killed as well - also only one process have parent id as 0 ie the process that started itself - Input : process id : [3, 1, 5, 7, 10, 11, 12] - parent id : [0, 3, 3, 5, 5, 10, 10] - kill_id : 5 - Output: [5, 7, 10, 11, 12] -*/ -#include -using namespace std; - -template -class Graph{ - map > L; - public: - Graph(){ - - } - void add_list(int u, int v, bool bidir = false){ - L[u].push_back(v); - if(bidir){ - L[v].push_back(u); - } - } - void print_graph(){ - for(T node : L){ - cout << node.first << "->"; - for(T neighbour: node.second){ - cout << neighbour << ","; - } - cout << endl; - } - } - void kill_process(int process){ - map visited; - queue q; - q.push(process); - visited[process] = true; - while(!q.empty()){ - int node = q.front(); - cout << node << "->"; - q.pop(); - for(T neighbour : L[node]){ - if(!visited[neighbour]){ - q.push(neighbour); - visited[neighbour] = true; - } - } - } - } -}; -int main(){ - Graph g; - int n; - cout << "Enter number of process : "; - cin >> n; - vector process_id(n); - vector parent_id(n); - for(int i = 0; i < n; i++){ - cin >> process_id[i]; - } - for(int i = 0; i < n; i++){ - cin >> parent_id[i]; - } - for(int i = 0; i < n; i++){ - g.add_list(parent_id[i], process_id[i]); - } - int kill; - cout << "Enter process to be killed : "; - cin >> kill; - g.kill_process(kill); - return 0; -} \ No newline at end of file diff --git a/Leetcode/ValidateIp.java b/Leetcode/ValidateIp.java deleted file mode 100644 index 48eefc5d..00000000 --- a/Leetcode/ValidateIp.java +++ /dev/null @@ -1,86 +0,0 @@ -/* -Given a string queryIP, return "IPv4" if IP is a valid IPv4 address, - "IPv6" if IP is a valid IPv6 address or "Neither" if IP is not a correct IP of any type. - -A valid IPv4 address is an IP in the form "x1.x2.x3.x4" where 0 <= xi <= 255 and -xi cannot contain leading zeros. For example, "192.168.1.1" and "192.168.1.0" are -valid IPv4 addresses while "192.168.01.1", "192.168.1.00", and "192.168@1.1" are invalid IPv4 addresses. - -A valid IPv6 address is an IP in the form "x1:x2:x3:x4:x5:x6:x7:x8" where: - -1 <= xi.length <= 4 -xi is a hexadecimal -string which may contain digits, lowercase English letter - ('a' to 'f') and upper-case English letters ('A' to 'F'). -Leading zeros are allowed in xi. -For example, "2001:0db8:85a3:0000:0000:8a2e:0370:7334" and - "2001:db8:85a3:0:0:8A2E:0370:7334" are valid IPv6 addresses, while - "2001:0db8:85a3::8A2E:037j:7334" and "02001:0db8:85a3:0000:0000:8a2e:0370:7334" are invalid IPv6 addresses. */ - - -/* - * Input: queryIP = "172.16.254.1" -Output: "IPv4" -Explanation: This is a valid IPv4 address, return "IPv4". - -Input: queryIP = "2001:0db8:85a3:0:0:8A2E:0370:7334" -Output: "IPv6" -Explanation: This is a valid IPv6 address, return "IPv6". - -Input: queryIP = "256.256.256.256" -Output: "Neither" -Explanation: This is neither a IPv4 address nor a IPv6 address. - */ - - - - -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.Scanner; - class Solution{ - public String validIPAddress(String queryIP) { - if(isValidIPv4(queryIP)) - return "IPv4"; - else if(isValidIPv6(queryIP)) - return "IPv6"; - - return "Neither"; - - } - - public static boolean isValidIPv4(String ip) - { - String parts - = "^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; - String regex - = "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){4}$"; - Pattern checkIp = Pattern.compile(regex); - - if (ip == null) - return false; - Matcher m = checkIp.matcher(ip); - return m.matches(); - } - public static boolean isValidIPv6(String ip) - { - String regex - = "^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$"; - Pattern checkIp= Pattern.compile(regex,Pattern.CASE_INSENSITIVE); - - if (ip == null) - return false; - Matcher m = checkIp.matcher(ip); - return m.matches(); - } - -} - -class ValiDateIp{ -public static void main(String args[]){ - Scanner inp=new Scanner(System.in); - Solution sol=new Solution(); - String queryIp=inp.next(); - System.out.print(sol.validIPAddress(queryIp)); - } -} \ No newline at end of file diff --git a/Leetcode/arrays_strings_is_unique.cpp b/Leetcode/arrays_strings_is_unique.cpp deleted file mode 100644 index 222aa54a..00000000 --- a/Leetcode/arrays_strings_is_unique.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Implement an algorithm to determine if a string has all unique characters. -// what if you cannot use additional data structures? -// Program Author : Abhisek Kumar Gupta -// Approach 1 : compare every character of string with other character TC O(n^2) -// Approach 2 : Sort the string and compare neighbouring character for dups -// TC of approach 2 : O(n log(n)) -#include -using namespace std; -bool is_unique_normal(string s){ - // using additional data structure - // TC O(min(c, n)) c being size of character set n = len of string - // SC is O(1) here - if(s.length() > 128) - return true; - bool visited[128]; - for(int i = 0; i < s.length(); i++){ - int val = s[i] - 'a'; - if(visited[val]){ - return false; - } - visited[val] = true; - } - return true; -} -bool is_unique(string s){ - // without using additional data structures - // here we reduce our space usage - if(s.length() > 128) - return false; - int checker = 0; - for(int i = 0; i < s.length(); i++){ - int val = s[i] - 'a'; - if(checker & (1 << val)) - return false; - checker |= (1 << val); - } - return true; -} -void print_ans(bool ans, string s){ - if(ans){ - cout << s << " is unique\n"; - } - else{ - cout << s << " is not unique\n"; - } -} -int main(){ - string s = "ABCDD"; - string t = "ABCD"; - string u = "AAAAAABCD"; - is_unique_normal(s) == true ? print_ans(true, s) : print_ans(false, s); - is_unique_normal(t) == true ? print_ans(true, t) : print_ans(false, t); - is_unique_normal(u) == true ? print_ans(true, u) : print_ans(false, u); - cout << "\n***********************\n"; - is_unique(s) == true ? print_ans(true, s) : print_ans(false, s); - is_unique(t) == true ? print_ans(true, t) : print_ans(false, t); - is_unique(u) == true ? print_ans(true, u) : print_ans(false, u); - return 0; -} \ No newline at end of file diff --git a/Leetcode/binary_tree_count_nodes.cpp b/Leetcode/binary_tree_count_nodes.cpp deleted file mode 100644 index 236998bb..00000000 --- a/Leetcode/binary_tree_count_nodes.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Binary Tree : Count Number of Nodes -// Program Author : Abhisek Kumar Gupta -/* - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - 1 -1 15 20 - / \ /\ /\ - 1 -1 -1 -1 -1 -1 - /\ - -1 -1 - Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 - Output : 9 -*/ - -#include -using namespace std; -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int d){ - data = d; - } -}; -Node* build_binary_tree(){ - int data; - cin >> data; - if(data == -1){ - return NULL; - } - Node* root = new Node(data); - root->left = build_binary_tree(); - root->right = build_binary_tree(); - return root; -} -int count_number_of_nodes(Node* root){ - if(root == NULL) - return 0; - return 1 + count_number_of_nodes(root->left) + count_number_of_nodes(root->right); -} -int main(){ - Node* root = build_binary_tree(); - int number_of_nodes = count_number_of_nodes(root); - cout << number_of_nodes; - return 0; -} \ No newline at end of file diff --git a/Leetcode/binary_tree_diameter_of_a_tree_optimal.cpp b/Leetcode/binary_tree_diameter_of_a_tree_optimal.cpp deleted file mode 100644 index f21b774b..00000000 --- a/Leetcode/binary_tree_diameter_of_a_tree_optimal.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Binary Tree : Diameter of tree O(n) -// Program Author : Abhisek Kumar Gupta -/* - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - 1 -1 15 20 - / \ /\ /\ - 1 -1 -1 -1 -1 -1 - /\ - -1 -1 - Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 - Output : Height : 5 - Diameter : 7 -*/ -#include -using namespace std; - -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -class Pair{ - public: - int height; - int diameter; -}; -Node* build_binary_tree(){ - int data; - cin >> data; - if(data == -1) - return NULL; - Node* root = new Node(data); - root->left = build_binary_tree(); - root->right = build_binary_tree(); - return root; -} -Pair compute_diameter(Node* root){ - Pair p; - if(root == NULL){ - p.height = 0; - p.diameter = 0; - return p; - } - Pair left = compute_diameter(root->left); - Pair right = compute_diameter(root->right); - p.height = max(left.height, right.height) + 1; - p.diameter = max(left.height + right.height, max(left.diameter, right.diameter)); - return p; -} -int main(){ - Node* root = build_binary_tree(); - Pair p = compute_diameter(root); - cout << p.height << endl; - cout << p.diameter << endl; - return 0; -} \ No newline at end of file diff --git a/Leetcode/binary_tree_preorder_traversal.cpp b/Leetcode/binary_tree_preorder_traversal.cpp deleted file mode 100644 index b9456a0a..00000000 --- a/Leetcode/binary_tree_preorder_traversal.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Pre-Order Traversal of a Binary-Tree -// Program Author : Abhisek Kumar Gupta -/* - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - 1 -1 15 20 - / \ /\ /\ - -1 -1 -1 -1 -1 -1 - Input : 40 10 5 1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 - Output : 40->10->5->1->30->28->15->20 -*/ -#include -using namespace std; -class Node{ - public: - int data; - Node *left; - Node *right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } - -}; - -Node* build_binary_tree(){ - int data; - cin >> data; - if(data == -1) - return NULL; - Node* root = new Node(data); - root->left = build_binary_tree(); - root->right = build_binary_tree(); - return root; -} - -void print_binary_tree(Node* root){ - if(root == NULL) - return; - cout << root->data << "->"; - print_binary_tree(root->left); - print_binary_tree(root->right); -} - -int main(){ - Node* root = build_binary_tree(); - print_binary_tree(root); -} \ No newline at end of file diff --git a/Leetcode/edit_distance_dp.cpp b/Leetcode/edit_distance_dp.cpp deleted file mode 100644 index 5b110203..00000000 --- a/Leetcode/edit_distance_dp.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - Given two strings str1 and str2 and following three operations that can performed on str1. - 1) Insert - 2) Remove - 3) Replace - Find minimum number of operations required to convert ‘str1’ into ‘str2’. - For example if input strings are CAT AND CAR the edit distance is 1. - - Input : s1 : saturday s2 : sunday - Output : 3 -*/ -// Dynamic Programming Solution : TC O(n^2) -// Porgram Author : Abhisek Kumar Gupta -#include -using namespace std; -int find_edit_distance(string s1, string s2, int l1, int l2){ - int dp[100][100] = {}; - for(int i = 0; i <= l1; i++){ - dp[i][0] = i; - } - for(int i = 0; i <= l2; i++){ - dp[0][i] = i; - } - for(int i = 1; i <= l1; i++){ - for(int j = 1; j <= l2; j++){ - if(s1[i] == s2[j]){ - dp[i][j] = dp[i - 1][j - 1]; - } - else{ - int del = dp[i][j - 1]; - int replace = dp[i - 1][j - 1]; - int insert = dp[i - 1][j]; - dp[i][j] = min(del, min(replace, insert)) + 1; - } - } - } - for(int i = 0; i <= l1; i++){ - for(int j = 0; j <= l2; j++){ - cout << setw(5) < -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -int length(node* head){ - int len = 0; - while(head != NULL){ - head = head->next; - len++; - } - return len; -} -void insert_at_head(node *&head, int data){ - node *n = new node(data); - n->next = head; - head = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_head(head, 1); - insert_at_head(head, 2); - insert_at_head(head, 3); - print_linked_list(head); - int len = length(head); - cout << endl < -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; - -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -istream& operator>>(istream &is, node *&head){ - makeLinkedList(head); - return is; -} -ostream& operator<<(ostream &os, node *&head){ - print_linked_list(head); - return os; -} -node* merge_two_lists(node *a, node *b){ - if(a == NULL){ - return b; - } - else if(b == NULL){ - return a; - } - node *c = NULL; - if(a->data < b->data){ - c = a; - c->next = merge_two_lists(a->next, b); - } - else{ - c = b; - c->next = merge_two_lists(a, b->next); - } - return c; -} -node* merge_k_lists(node *arr[], int last){ - while(last != 0){ - int i = 0, j = last; - while(i < j){ - arr[i] = merge_two_lists(arr[i], arr[j]); - i++; - j--; - if(i >= j) - last = j; - } - } - return arr[0]; -} -int main(){ - - int n; - cin >> n; - node *arr[n]; - for(int i = 0; i < n; i++){ - arr[i] = NULL; - } - for(int i = 0; i < n; i++){ - cin >> arr[i]; - } - cout << endl; - for(int i = 0; i < n; i++){ - cout << arr[i] << endl;; - } - node *head = merge_k_lists(arr, n-1); - cout << head; - return 0; -} \ No newline at end of file diff --git a/Leetcode/linked_list_reverse_recursive.cpp b/Leetcode/linked_list_reverse_recursive.cpp deleted file mode 100644 index ec360e09..00000000 --- a/Leetcode/linked_list_reverse_recursive.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Given the head of a singly linked list, reverse the list, and return the reversed list recursively -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} - -node* recursive_reverse(node *head){ - if(head->next == NULL || head == NULL){ - return head; - } - node *mini_head = recursive_reverse(head->next); - node *current = head; - current->next->next = current; - current->next = NULL; - return mini_head; -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - cout << endl; - head = recursive_reverse(head); - print_linked_list(head); - return 0; -} \ No newline at end of file diff --git a/Leetcode/linked_list_swap_nodes_in_pair_iterative.cpp b/Leetcode/linked_list_swap_nodes_in_pair_iterative.cpp deleted file mode 100644 index 03a85460..00000000 --- a/Leetcode/linked_list_swap_nodes_in_pair_iterative.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Given a linked list, swap every two adjacent nodes and return its head. -// You must solve the problem without modifying the values in the list's nodes -// (i.e., only nodes themselves may be changed.) Iterative -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - cout << endl; - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} - -node* swapNodesInPairIterative(node *head){ - node *dummy = new node(0); - node *prev = dummy; - node *curr = head; - node *second = NULL; - node *nextPair = NULL; - while(curr && curr->next){ - nextPair = curr->next->next; - second = curr->next; - - second->next = curr; - curr->next = nextPair; - prev->next = second; - - prev = curr; - curr = nextPair; - } - return dummy->next; - -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - head = swapNodesInPairIterative(head); - print_linked_list(head); - return 0; -} \ No newline at end of file diff --git a/Leetcode/min_steps_to_reduce_a_number_to_one_dp.cpp b/Leetcode/min_steps_to_reduce_a_number_to_one_dp.cpp deleted file mode 100644 index 32d48a77..00000000 --- a/Leetcode/min_steps_to_reduce_a_number_to_one_dp.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Minimum steps to reduce a number to one conditions are as follows -// a) subtract 1 [one operation] -// b) divide by 2 [one operation] -// c) divide by 3 [one operation] - -// Dynamic Programming solution -// Program Author : Abhisek Kumar Gupta -/* - Input : 10 - Output : 3 - Explanation : 10 reduced to 9 reduced to 3 reduced to 1 [total 3 operations] - Input : 15 - Output : 4 -*/ -#include -using namespace std; -int dp[10000]; -int find_min_steps(int number){ - int r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; - dp[0] = 0; - dp[1] = 0; - dp[2] = 1; - dp[3] = 1; - for(int i = 4; i <= number; i++){ - r1 = 1 + dp[i - 1]; - if(i % 2 == 0) - r2 = 1 + dp[i / 2]; - if(i % 3 == 0) - r3 = 1 + dp[i / 3]; - dp[i] = min(r1, min(r2, r3)); - r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; - } - - return dp[number]; -} -int main(){ - int number; - cin >> number; - memset(dp, 0, sizeof(dp)); - int result = find_min_steps(number); - cout << result; - return 0; -} \ No newline at end of file diff --git a/Leetcode/next_greater_element.cpp b/Leetcode/next_greater_element.cpp deleted file mode 100644 index c8dbd7d1..00000000 --- a/Leetcode/next_greater_element.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* -You are given two integer arrays nums1 and nums2 both of unique elements, where nums1 is a subset of nums2. -Find all the next greater numbers for nums1's elements in the corresponding places of nums2. -The Next Greater Number of a number x in nums1 is the first greater number to its right in nums2. -If it does not exist, return -1 for this number. -Input: nums1 = [4,1,2], nums2 = [1,3,4,2] -Output: [-1,3,-1] -Explanation: -For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1. -For number 1 in the first array, the next greater number for it in the second array is 3. -For number 2 in the first array, there is no next greater number for it in the second array, so output -1. -*/ - vector nextGreaterElement(vector& nums1, vector& nums2) { - vector result(nums1.size(), -1); - stack st; - unordered_map umap; - // we will keep pushing the element into stack unless element in num2 is less than TOS - // if element is greater then we map TOS with element and pop out the TOS - for(auto element : nums2){ - while(!st.empty() && element > st.top()){ - umap[st.top()] = element; - st.pop(); - } - st.push(element); - } - // mapping is complete now we just traverse first array i.e num1 and check if its present in map - // if its present in map then we store it in result or we escape - // note : result already is populated with -1's for cases such as if NGE is not present - for(int i = 0; i < nums1.size(); i++){ - if(umap.find(nums1[i]) != umap.end()){ - result[i] = umap[nums1[i]]; - } - } - return result; - } \ No newline at end of file diff --git a/Leetcode/rod_cutting_problem_dp.cpp b/Leetcode/rod_cutting_problem_dp.cpp deleted file mode 100644 index 0aa8e363..00000000 --- a/Leetcode/rod_cutting_problem_dp.cpp +++ /dev/null @@ -1,36 +0,0 @@ - /* - Given a rod of length n inches and an array of prices that contains prices of all - pieces of size smaller than n. Determine the maximum value obtainable by cutting - up the rod and selling the pieces. For example, if length of the rod is 8 and the - values of different pieces are given as following, then the maximum obtainable - value is 22 (by cutting in two pieces of lengths 2 and 6) - Input : 8 - : 1 5 8 9 10 17 17 20 - Output : 22 -*/ -// Dynamic Programming solution TC : O(n^2) -// Program Author: Abhisek Kumar Gupta -#include -using namespace std; -int max_profit(vector profit, int total_length){ - int dp[100] = {}; - for(int length = 1; length <= total_length; length++){ - int best = 0; - for(int cut = 1; cut <= length; cut++){ - best = max(best, profit[cut] + dp[length - cut]); - } - dp[length] = best; - } - return dp[total_length]; -} -int main(){ - int total_length; - cin >> total_length; - vector profit(total_length + 1); - for(int length = 1; length <= total_length; length++) - cin >> profit[length]; - int result = max_profit(profit, total_length); - cout << result; - return 0; -} - diff --git a/Linked List/Add_two_numbers.py b/Linked List/Add_two_numbers.py new file mode 100644 index 00000000..60f7dd47 --- /dev/null +++ b/Linked List/Add_two_numbers.py @@ -0,0 +1,46 @@ +'''Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem :Linked List: Add Two Numbers in Python +Issue Number : #621 +Problem statement : + +Explanation of the below python code : + +This code implements a solution for the "Add Two Numbers" problem on LeetCode, where the inputs are two non-empty linked lists representing two non-negative integers. The goal is to return the sum of the two integers as a linked list. + +The function takes in two linked lists, l1 and l2, and creates a new linked list represented by the variable 'dummy' which will store the sum. The current node of the new linked list is represented by the variable 'curr'. The variable 'carry' stores the carry-over value from the previous sum. + +The function then loops through both input linked lists, l1 and l2, as well as any carry-over value from the previous sum. It adds the values of the corresponding nodes in l1 and l2 (if they exist) and any carry-over value from the previous sum. It then calculates the carry-over value and the new value for the current node by dividing the sum by 10 and taking the remainder. The carry-over value is then updated for the next iteration of the loop. + +The new node with the calculated value is added to the end of the new linked list using the 'curr' variable. 'curr' is then updated to point to the new node. Once the loop is completed, the new linked list is returned, but without the initial 'dummy' node, as the first node in the list is actually the second node in the sequence. + +Overall, the function creates a new linked list representing the sum of the input linked lists, using a carry-over value to handle any values greater than 10. It runs in linear time in the length of the input linked lists, making it an efficient solution. + + +''' + +-------------------------------------------------------------------------//python code begins here---------------------------------------------------------------------------------------------------------------------------------------------------------- + +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: + dummy = ListNode() + curr = dummy + carry = 0 + while l1 or l2 or carry: + val = carry + if l1: + val += l1.val + l1 = l1.next + if l2: + val += l2.val + l2 = l2.next + carry, val = divmod(val, 10) + curr.next = ListNode(val) + curr = curr.next + return dummy.next diff --git a/Linked List/Intersection_LL.cpp b/Linked List/Intersection_LL.cpp new file mode 100644 index 00000000..708f0541 --- /dev/null +++ b/Linked List/Intersection_LL.cpp @@ -0,0 +1,32 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { + ListNode *pA = headA; + ListNode *pB = headB; + + // Traverse both linked lists until they meet or reach the end (NULL) + while (pA != pB) { + // Move pointers to the next node + pA = (pA != nullptr) ? pA->next : headB; + pB = (pB != nullptr) ? pB->next : headA; + } + + // Return the intersection node (or NULL if no intersection) + return pA; + } +}; + +/* +Explanation: +The provided code implements the solution to find the intersection point of two singly-linked lists headA and headB. The getIntersectionNode function takes two ListNode pointers as parameters and returns the intersection node if one exists, or NULL if there is no intersection. + +The approach used here is based on the concept of "runner pointers." The pointers pA and pB start from the heads of the two linked lists and traverse through the lists. When a pointer reaches the end of its list, it is redirected to the head of the other list. This ensures that the pointers will meet at the intersection point if it exists, or they will both reach the end of the lists (NULL) simultaneously if there is no intersection. + */ diff --git a/Linked List/LFU_Cache.cpp b/Linked List/LFU_Cache.cpp new file mode 100644 index 00000000..120bfa0c --- /dev/null +++ b/Linked List/LFU_Cache.cpp @@ -0,0 +1,86 @@ +// Question--> LFU Cache +// Design and implement a data structure for a Least Frequently Used (LFU) cache. + +// Implement the LFUCache class: + +// LFUCache(int capacity) Initializes the object with the capacity of the data structure. +// int get(int key) Gets the value of the key if the key exists in the cache. Otherwise, returns -1. +// void put(int key, int value) Update the value of the key if present, or inserts the key if not already present. When the cache reaches its capacity, it should invalidate and remove the least frequently used key before inserting a new item. For this problem, when there is a tie (i.e., two or more keys with the same frequency), the least recently used key would be invalidated. +// To determine the least frequently used key, a use counter is maintained for each key in the cache. The key with the smallest use counter is the least frequently used key. + +// When a key is first inserted into the cache, its use counter is set to 1 (due to the put operation). The use counter for a key in the cache is incremented either a get or put operation is called on it. + +// The functions get and put must each run in O(1) average time complexity + + +// solution --> + +//minFreq is the smallest frequency so far +//The main idea is to put all keys with the same frequency to a linked list so the most recent one can be evicted; +//mIter stored the key's position in the linked list; + + +#include +using namespace std; + + +class LFUCache { + int cap; + int size; + int minFreq; + unordered_map> m; //key to {value,freq}; + unordered_map::iterator> mIter; //key to list iterator; + unordered_map> fm; //freq to key list; +public: + LFUCache(int capacity) { + // initialize your data structure here + cap=capacity; + size=0; + } + + int get(int key) { + if(m.count(key)==0) return -1; // if key not found return -1; + + fm[m[key].second].erase(mIter[key]); //erase from old freq list + m[key].second++; // increase freq; + fm[m[key].second].push_back(key); // add to new freq list; + mIter[key]=--fm[m[key].second].end();// point to new freq list; + + // if old min freq list empty, increase min freq; + if(fm[minFreq].size()==0 ) + minFreq++; + + return m[key].first; // return value + } + + void put(int key, int value) { + if(cap<=0) return; // corner case + + int storedValue=get(key); // get value if already present + + // if already present update value, increase freq; + if(storedValue!=-1) + { + m[key].first=value; + return; + } + + // if capacity full, erase least freq used element from this list + if(size>=cap ) + { + m.erase( fm[minFreq].front() ); + mIter.erase( fm[minFreq].front() ); + fm[minFreq].pop_front(); + size--; + } + + m[key]={value, 1}; // insert new key value pair with freq 1; + fm[1].push_back(key); // add to freq list 1; + mIter[key]=--fm[1].end(); // point to new freq list; + minFreq=1; // since new element added min freq will be 1; + size++; // increase size + } +}; + + +// https://leetcode.com/problems/lfu-cache/ \ No newline at end of file diff --git a/Linked List/Linked_List_Component.java b/Linked List/Linked_List_Component.java new file mode 100644 index 00000000..6bb8c763 --- /dev/null +++ b/Linked List/Linked_List_Component.java @@ -0,0 +1,72 @@ + + + +/**PROBLEM + * You are given the head of a linked list containing unique integer values and an integer array nums + *that is a subset of the linked list values. + +*Return the number of connected components in nums where two values are connected if + *they appear consecutively in the linked list. +*/ + +// SAMPLE I/O +// Input: head = [0,1,2,3], nums = [0,1,3] +// Output: 2 +// Explanation: 0 and 1 are connected, so [0, 1] and [3] are the two connected components. + +// Approach +/** + * Creating a HashMap to store all the values of nums[] + * Iterating list and if current node(head) we check if the hashmap contains the value] + * if yes the we increment the ans by one and setting the flag to false + * + * + * + * Time Complexity: O(N) + * Space Complexity : O(N) + */ + + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public int numComponents(ListNode head, int[] nums) { + // Create a HashMap to store the values from nums as keys and their indices as values + HashMap hm = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + hm.put(nums[i], i); + } + + boolean flag = true; // Flag to track if a connected component is found + int ans = 0; // Variable to store the number of connected components + + // Traverse the linked list + while (head != null) { + // Check if the current node's value is present in the HashMap + while (head != null && hm.containsKey(head.val)) { + head = head.next; // Move to the next node + + // If this is the start of a new connected component, increment the answer + if (flag) { + ans += 1; + flag = false; + } + } + flag = true; // Reset the flag + + if (head != null) { + head = head.next; // Move to the next node + } + } + + return ans; + } +} diff --git a/Linked List/MiddleOfLinkedList.java b/Linked List/MiddleOfLinkedList.java new file mode 100644 index 00000000..613b597e --- /dev/null +++ b/Linked List/MiddleOfLinkedList.java @@ -0,0 +1,46 @@ +/* QUESTION and SAMPLE I/O + + Given the head of a singly linked list, return the middle node of the linked list. + If there are two middle nodes, return the second middle node. + + Sample Input = [1,2,3,4,5] + Sample Output = 3 + + APPROACH and EXPLANATION + + This program is done using two pointers: slow pointer and fast pointer. + * Both pointer starts from the head node. + * Slow pointer moves one step at a time. + * Fast pointer moves two steps at a time. + * As the fast pointer reaches the end, slow pointer will be at the middle of the linked list. + * Accessing the slow pointer's value for 'val' will give you the value for the middle element of linked list. + + + Time Complexity: O(n) + Space Complexity: O(1) + + */ + + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + public ListNode middleNode(ListNode head) { + ListNode slow_pointer = head; //initialize slow pointer + ListNode fast_pointer = head; //initialize fast pointer + while(fast_pointer!=null && fast_pointer.next!=null){ + slow_pointer = slow_pointer.next; //moves to next node + fast_pointer = fast_pointer.next.next; //moves two nodes forward + } + return slow_pointer; + } +} \ No newline at end of file diff --git a/Linked List/MiddleofLL.py b/Linked List/MiddleofLL.py new file mode 100644 index 00000000..bf232d90 --- /dev/null +++ b/Linked List/MiddleofLL.py @@ -0,0 +1,32 @@ +class ListNode: + def __init__(self, value=0, next=None): + self.value = value + self.next = next + +def find_middle(head): + # Initialize slow and fast pointers to the head of the linked list + slow_ptr = head + fast_ptr = head + + # Traverse the linked list with slow and fast pointers + while fast_ptr is not None and fast_ptr.next is not None: + # Move slow pointer one step at a time + slow_ptr = slow_ptr.next + # Move fast pointer two steps at a time + fast_ptr = fast_ptr.next.next + + # When the fast pointer reaches the end, the slow pointer will be at the middle + return slow_ptr + +# Create a sample linked list: 1 -> 2 -> 3 -> 4 -> 5 +head = ListNode(1) +head.next = ListNode(2) +head.next.next = ListNode(3) +head.next.next.next = ListNode(4) +head.next.next.next.next = ListNode(5) + +# Find the middle node of the linked list +middle_node = find_middle(head) + +# Print the value of the middle node +print("Middle Node:", middle_node.value) # Output: Middle Node: 3 diff --git a/Linked List/RemoveKthNodeFromEnd.java b/Linked List/RemoveKthNodeFromEnd.java new file mode 100644 index 00000000..de390c23 --- /dev/null +++ b/Linked List/RemoveKthNodeFromEnd.java @@ -0,0 +1,102 @@ +//Problem Number : 19 - Leetcode + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +/* + * Problem Description : + * Given the head of a linked list, remove the nth node + * from the end of the list and return its head. + * + * linkedlist = 1 -> 2 -> 3 -> 4 -> 5-> null + * n = 2 + */ + +/* + * Approach we follow to solve this Problem. + * Given : head of linkedlist and n = number of node to remove from end + * Solution : + * => Since we have to remove node from end , we need to calculate length of + * linkedlist. + * To do so lets create a function calcLength() , it take head node as parameter + * and return length of linked list . + * => Store the length of linkedlist in variable namely (len) by calling + * function calcLength(). + * => To make it simple , lets calculate position of node from start of + * linkedlist by subtracting length of node with nth node to remove. + * and store the result in another variable called target. [ target = len - n] + * => Now, we check for target == 0 and if its true than we have to remove our + * head and point the head to next element . + * for ex : head.next = head + * After just return our head because we have no need to check other part. + * => create a pointer = 1 and store head of LikedList to temp variable . + * => After that , we have to iterate over linkedlist till our temp is not equal + * to null to find our target element. + * (Note : target element is always prev of node of node to remove for ex : 1-> 2-> 3->4-> 5 + * In this case 3 is our target because we have to remove 4.) + * => for each iteration , we check if our target == pointer , In case its true + * the we have to handle two case : + * Case 1 : if our target node is last node of linked list ,then point temp to + * null + * explanation : 1-> 2-> 3-> 4-> 5 -> null + * if our target is 5 the our new linkedlist will we like this : 1-> 2-> 3-> 4-> + * null + * Case 2 : Target is in middle of linkedlist then , update temp pointer to + * temp.next.next + * explanation : 1-> 2-> 3-> 4-> 5 -> null + * let say we have to remove 3 : then temp = 2 + * temp.next = 3 : node to remove + * temp.next.next = 4 + * output : 1-> 2-> 4-> 5 -> null + * => increment pointer and update temp to next node. + * => In last just return node + */ + +class Solution { + + public int calcLength(ListNode head) { + int cnt = 0; + while (head != null) { + cnt++; + head = head.next; + } + return cnt; + } + + public ListNode removeNthFromEnd(ListNode head, int n) { + + int len = calcLength(head); + int target = len - n; + if (target == 0) { + head = head.next; + return head; + } + int pointer = 1; + + ListNode temp = head; + while (temp != null) { + if (pointer == target) { + ListNode key = temp.next; + if (key == null) { + temp = null; + } else { + temp.next = key.next; + } + + } + pointer++; + temp = temp.next; + } + + return head; + + } +} \ No newline at end of file diff --git a/Linked List/Remove_nth_node_from_end.java b/Linked List/Remove_nth_node_from_end.java new file mode 100644 index 00000000..32c108de --- /dev/null +++ b/Linked List/Remove_nth_node_from_end.java @@ -0,0 +1,73 @@ +/** + Problem :-Remove the nth node from end of the list + https://leetcode.com/problems/remove-nth-node-from-end-of-list/description/ + + Approach:- + 1. First find the length of the list and store it in a variable named 'len'. + 2. Then traverse the list (len-n) + 3. Now you are one node behind the node that we have to remove. + + 4. Now set the current node's next to the next.next i.e temp.next=temp.next.next. + + Time Complexity : O(N) + we traverse the list for calculating its length and removing the node.Hence O(N). + + Space Complexity : O(1) + No extra space is required + + Note :- The code is well documented. So take a look. + + + + */ + + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + public ListNode removeNthFromEnd(ListNode head, int n) { + if (head == null) return head; // If the head is null, return the head itself + if (head.next == null) { // If there is only one node in the list + if (n == 1) return null; // If n is 1, remove the node and return null + return head; // Otherwise, return the head itself + } + + ListNode temp = head; // Create a temporary node pointing to the head + + int len = size(head); // Get the length of the list + if (len - n == 0) { // If the node to be removed is the head itself + head = head.next; // Move the head to the next node + return head; + } + + len -= n; // Calculate the index of the previous node of the node to be removed + for (int i = 1; i < len; i++) { // Traverse to the previous node so we can remove the next node + temp = temp.next; + } + + if (temp != null && temp.next != null) { // If the previous node and the node to be removed exist + temp.next = temp.next.next; // Point the previous node to the node after the one to be removed + } + + return head; // Return the updated head + } + + int size(ListNode temp) { + ListNode s = temp; + int n = 0; + while (s != null) { // Traverse the list to count the number of nodes + s = s.next; + n += 1; + } + return n; // Return the size of the list + } +} diff --git a/Linked List/add_two_numbers.cpp b/Linked List/add_two_numbers.cpp new file mode 100644 index 00000000..a378b5b8 --- /dev/null +++ b/Linked List/add_two_numbers.cpp @@ -0,0 +1,153 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution +{ +public: + ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) + { + ListNode *ansHead = new ListNode((l1->val + l2->val) % 10); + ListNode *ans = ansHead; + int carry = (l1->val + l2->val) / 10; + l1 = l1->next; + l2 = l2->next; + while (l1 != NULL && l2 != NULL) + { + ListNode *temp = new ListNode((l1->val + l2->val + carry) % 10); + carry = (l1->val + l2->val + carry) / 10; + l1 = l1->next; + l2 = l2->next; + ans->next = temp; + ans = temp; + } + while (l1 != NULL) + { + ListNode *temp = new ListNode((l1->val + carry) % 10); + carry = (l1->val + carry) / 10; + l1 = l1->next; + ans->next = temp; + ans = temp; + } + while (l2 != NULL) + { + ListNode *temp = new ListNode((l2->val + carry) % 10); + carry = (l2->val + carry) / 10; + l2 = l2->next; + ans->next = temp; + ans = temp; + } + if (carry > 0) + { + ListNode *temp = new ListNode(carry); + ans->next = temp; + ans = temp; + } + return ansHead; + } + ListNode *solve(ListNode *&head1, ListNode *&head2) + { + if (head1 == NULL) + { + return head2; + } + if (head2 == NULL) + { + return head1; + } + + // linked list which contains the final answer + ListNode *ansHead = NULL; + ListNode *ansTail = NULL; + + int carry = 0; + while (head1 != NULL && head2 != NULL) + { + int sum = head1->val + head2->val + carry; + int digit = sum % 10; + carry = sum / 10; + + ListNode *newNode = new ListNode(digit); + if (ansHead == NULL) + { + ansHead = newNode; + ansTail = newNode; + } + else + { + ansTail->next = newNode; + ansTail = newNode; + } + head1 = head1->next; + head2 = head2->next; + } + + // head1 linked list pending to be solved + while (head1 != NULL) + { + int sum = carry + head1->val; + int digit = sum % 10; + carry = sum / 10; + + ListNode *newNode = new ListNode(digit); + if (ansHead == NULL) + { + ansHead = newNode; + ansTail = newNode; + } + else + { + ansTail->next = newNode; + ansTail = newNode; + } + head1 = head1->next; + } + + // head2 linked list pending to be solved + while (head2 != NULL) + { + int sum = carry + head2->val; + int digit = sum % 10; + carry = sum / 10; + + ListNode *newNode = new ListNode(digit); + if (ansHead == NULL) + { + ansHead = newNode; + ansTail = newNode; + } + else + { + ansTail->next = newNode; + ansTail = newNode; + } + head2 = head2->next; + } + + while (carry != 0) + { + int sum = carry; + int digit = sum % 10; + carry = sum / 10; + + ListNode *newNode = new ListNode(digit); + if (ansHead == NULL) + { + ansHead = newNode; + ansTail = newNode; + } + else + { + ansTail->next = newNode; + ansTail = newNode; + } + } + return ansHead; + } +}; \ No newline at end of file diff --git a/Linked List/add_two_numbers.js b/Linked List/add_two_numbers.js new file mode 100644 index 00000000..3cd0104c --- /dev/null +++ b/Linked List/add_two_numbers.js @@ -0,0 +1,80 @@ +// Linked List: Add Two Numbers in Javascript #622 + +/* + + You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. + + You may assume the two numbers do not contain any leading zero, except the number 0 itself. + + Example 1: + - Input: l1 = [2,4,3], l2 = [5,6,4] + - Output: [7,0,8] + - Explanation: 342 + 465 = 807. + + Example 2: + - Input: l1 = [0], l2 = [0] + - Output: [0] + + Example 3: + - Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] + - Output: [8,9,9,9,0,0,0,1] + + Constraints: + - The number of nodes in each linked list is in the range [1, 100]. + - 0 <= Node.val <= 9 + - It is guaranteed that the list represents a number that does not have leading zeros. + +*/ + +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ + +var addTwoNumbers = function (l1, l2) { + const head = new ListNode(); // creating a new LL. + let temp = head; // assign head into temp to traverse the LL. + let carry = 0; // to store carry that we will get during the add operation. + + while (l1 || l2) { + // loop will get executed until one of the LL becomes null. + let sum = carry; + + //perform summation with both LL one by one and assign it to the sum variable. + if (l1) { + sum += l1.val; + l1 = l1.next; + } + + if (l2) { + sum += l2.val; + l2 = l2.next; + } + + // to store the carry part we will do sum/10 and store it's floor value to carry variable. For Example: + /* + if sum = 12 + so, sum/10 = 1.2 + and Math.floor(1.2) = 1 and we get our carry part. + > carry = 1. + */ + carry = Math.floor(sum / 10); + temp.next = new ListNode(sum % 10); // at this point we are removing carry part from sum and adding remaining part into the ll. + temp = temp.next; + } + + if (carry > 0) { + // after the completion of summation, if there is any cary left it will be added to list. + temp.next = new ListNode(carry); + } + + return head.next; // return the head of the node so ll can be traverse. +}; diff --git a/Linked List/delete_kth_node.js b/Linked List/delete_kth_node.js new file mode 100644 index 00000000..1192e751 --- /dev/null +++ b/Linked List/delete_kth_node.js @@ -0,0 +1,78 @@ +/*Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Remove Kth node from end in Javascript +Issue Number : #687 +Problem statement : + +Explanation of the below Javascript code : + +Define a Node class to represent each node in the linked list. +Create a function called removeKthFromEnd that accepts the head of the linked list and the value of k as parameters. +Now you can create a linked list and call the removeKthFromEnd function to remove the kth node from the end. + + +*/ + +class Node { + constructor(value) { + this.value = value; + this.next = null; + } +} +function removeKthFromEnd(head, k) { + // Step 1: Create two pointers, fast and slow, and set them both to the head of the linked list. + let fast = head; + let slow = head; + + // Step 2: Move the fast pointer k nodes ahead. + for (let i = 0; i < k; i++) { + if (fast === null) { + // The linked list does not have k nodes. + return head; + } + fast = fast.next; + } + + // Step 3: If the fast pointer reaches the end, the kth node from the end is the head itself. + if (fast === null) { + head = head.next; + return head; + } + + // Step 4: Move the fast and slow pointers together until the fast pointer reaches the end. + while (fast.next !== null) { + fast = fast.next; + slow = slow.next; + } + + // Step 5: Remove the kth node by updating the next pointer of the previous node. + slow.next = slow.next.next; + + // Step 6: Return the modified linked list. + return head; +} +// Example usage: +const head = new Node(1); +head.next = new Node(2); +head.next.next = new Node(3); +head.next.next.next = new Node(4); +head.next.next.next.next = new Node(5); + +const k = 2; +const modifiedHead = removeKthFromEnd(head, k); + +// Display the modified linked list. +let current = modifiedHead; +while (current !== null) { + console.log(current.value); + current = current.next; +} + +/* +OUTPUT: +1 +2 +3 +5 +*/ diff --git a/Linked List/double_linked_list.go b/Linked List/double_linked_list.go new file mode 100644 index 00000000..a4092121 --- /dev/null +++ b/Linked List/double_linked_list.go @@ -0,0 +1,76 @@ +// Implementation of a doubly linked list in Go +/* + In this implementation, we define a Node struct that contains the data as well as pointers to the previous and next nodes. + We also define a LinkedList struct that contains a pointer to the head and tail nodes. + + The AddNode method adds a new node to the end of the list. If the list is empty, the new node is set as both the head and tail. + Otherwise, the new node is added after the current tail, and the current tail is set as the previous node of the new node. + Finally, the new node is set as the new tail. + + The PrintList method traverses the list from the head node and prints the data of each node. + + In the main function, we create a new doubly linked list, add some nodes to it, and print the list. +*/ +package main + +import "fmt" + +// Node represents a node in the doubly linked list +type Node struct { + data int + prev *Node + next *Node +} + +// LinkedList represents the doubly linked list +type LinkedList struct { + head *Node + tail *Node +} + +// AddNode adds a new node to the end of the doubly linked list +func (list *LinkedList) AddNode(data int) { + // Create a new node + newNode := &Node{data: data} + + // If the list is empty, set the new node as head and tail + if list.head == nil { + list.head = newNode + list.tail = newNode + return + } + + // Set the new node as the next node of the current tail + list.tail.next = newNode + + // Set the current tail as the previous node of the new node + newNode.prev = list.tail + + // Set the new node as the new tail + list.tail = newNode +} + +// PrintList prints the doubly linked list +func (list *LinkedList) PrintList() { + // Start from the head node + currNode := list.head + + // Traverse the list and print the data of each node + for currNode != nil { + fmt.Printf("%d ", currNode.data) + currNode = currNode.next + } +} + +func main() { + // Create a new doubly linked list + list := &LinkedList{} + + // Add nodes to the list + list.AddNode(1) + list.AddNode(2) + list.AddNode(3) + + // Print the list + list.PrintList() +} diff --git a/Linked List/doubly_linked_list.cpp b/Linked List/doubly_linked_list.cpp new file mode 100644 index 00000000..c8cd3a15 --- /dev/null +++ b/Linked List/doubly_linked_list.cpp @@ -0,0 +1,131 @@ +/* Name : Aneesh +Github username : 007aneesh +Repository name : data-structures-and-algorithms + +Problem : Doubly linked list in c++ +Issue Number : #975 +Problem statement : Given an integer n representing the length of linked list and you have to print linked list from start and end. + +Sample testcases: + +Testcase 1 --> + +Input: +5 +1 2 3 4 5 + +Output: +Printing forwardly................................ +1 +2 +3 +4 +5 +Printing backwardly................................ +5 +4 +3 +2 +1 + + +Testcase 2 --> +Input: +3 +10 20 30 + +Output: +Printing forwardly................................ +10 +20 +30 +Printing backwardly................................ +30 +20 +10 + + + +Time Complexity = O(n) +Space Complexity = O(n) + + +Explanation: +In the main function, the program first takes input n, +the number of nodes to be inserted in the linked list. +Then, in a loop, it takes n values as input and creates new nodes with those values. +The nodes are then linked together to form a doubly linked list. +Finally, it calls the PrintForward and PrintBackward functions +to print the elements of the list in forward and backward order, respectively. +*/ + +// ----------------------------------------------------------------------------- code begins now! + +#include +using namespace std; + +// Define a Node class +class Node { +public: + int data; + Node* next; + Node* prev; + Node(int d) { + this->data = d; + next = NULL; + prev = NULL; + } +}; + +// Function to print the linked list forward +void PrintForward(Node* head) { + Node* traverse = head; + while (traverse != NULL) { + cout << traverse->data << endl; + traverse = traverse->next; + } +} + +// Function to print the linked list backward +void PrintBackward(Node* tail) { + Node* traverse = tail; + while (tail != NULL) { + cout << traverse->data << endl; + traverse = traverse->prev; + } +} + +int main() { + int n, value; + cin>>n; // Read the number of nodes to be created + Node *head = nullptr; // Initialize head pointer + Node *tail = nullptr; // Initialize tail pointer + + // Read 'n' values and create a doubly linked list + for (int i = 0; i < n; i++) { + cin >> value; // Read the value for the current node + Node* newNode = new Node(value); // Create a new node with the value + + // If the list is empty, set the head and tail to the new node + if (head == NULL) { + head = newNode; + tail = newNode; + } else { + // Add the new node to the end of the list + newNode->next = nullptr; + newNode->prev = tail; + tail->next = newNode; + tail = newNode; + } + } + + // Print the linked list forward + cout << "Printing forwardly................................" << endl; + PrintForward(head); + + // Print the linked list backward + cout << "Printing backwardly................................" << endl; + PrintBackward(tail); + + return 0; +} diff --git a/Linked List/doubly_linked_list.java b/Linked List/doubly_linked_list.java new file mode 100644 index 00000000..c709b4b8 --- /dev/null +++ b/Linked List/doubly_linked_list.java @@ -0,0 +1,107 @@ +/*A Doubly Linked List is a data structure in which each node contains a reference to the previous node and the next node in the sequence. It extends the functionality of a Singly Linked List by allowing traversal in both forward and backward directions.The first node of the list is referred to as the head, and the last node is referred to as the tail. In this implementation, the Node class represents a node in the doubly linked list. Each node has a data field to store the value, and prev and next fields to maintain references to the previous and next nodes in the list.*/ + +class Node { + int data; + Node prev; + Node next; + + public Node(int data) { + this.data = data; + this.prev = null; + this.next = null; + } +} + +class DoublyLinkedList { + Node head; + + public DoublyLinkedList() { + this.head = null; + } + + // Insert a new node at the end of the list + public void insert(int data) { + Node newNode = new Node(data); + + if (head == null) { + // If the list is empty, make the new node the head + head = newNode; + } else { + Node current = head; + + // Traverse to the end of the list + while (current.next != null) { + current = current.next; + } + + // Link the new node to the last node + current.next = newNode; + newNode.prev = current; + } + } + + // Delete a node with the given data value + public void delete(int data) { + Node current = head; + + while (current != null) { + if (current.data == data) { + if (current.prev != null) { + // If the node to be deleted is not the first node + + // Update the previous node's next reference + current.prev.next = current.next; + } else { + // If the node to be deleted is the first node + + // Update the head reference to the next node + head = current.next; + } + + if (current.next != null) { + // If the node to be deleted is not the last node + + // Update the next node's previous reference + current.next.prev = current.prev; + } + + break; + } + + current = current.next; + } + } + + // Display the elements of the list + public void display() { + Node current = head; + + while (current != null) { + System.out.print(current.data + " "); + current = current.next; + } + + System.out.println(); + } +} + +public class Main { + public static void main(String[] args) { + DoublyLinkedList list = new DoublyLinkedList(); + + list.insert(10); + list.insert(20); + list.insert(30); + list.insert(40); + + list.display(); // Output: 10 20 30 40 + + list.delete(20); + list.delete(40); + + list.display(); // Output: 10 30 + } +} + +/* Time complexity: O(n) for insertions and deletions, and O(1) for display. +Space complexity: O(1) for all operations */ diff --git a/Linked List/doubly_linked_list.js b/Linked List/doubly_linked_list.js new file mode 100644 index 00000000..f3dea4c7 --- /dev/null +++ b/Linked List/doubly_linked_list.js @@ -0,0 +1,190 @@ +class Node { + constructor(val) { + this.val = val + this.prev = null + this.next = null + } +} + +class DoublyLinkedList { + constructor() { + this.head = null + this.tail = null + this.length = 0 + } + + insertAtTail(val) { + const node = new Node(val) + + if (!this.head) { + this.head = node + this.tail = node + this.length++ + } else { + node.prev = this.tail + this.tail.next = node + this.tail = node + this.length++ + } + } + + insertAtHead(val) { + const node = new Node(val) + + if (!this.head) { + this.head = node + this.tail = node + this.length++ + } else { + node.next = this.head + this.head.prev = node + this.head = node + this.length++ + } + } + + deleteAtTail() { + if (!this.head) throw new Error("Empty Linked List!") + + if (this.length === 1) { + this.head = null + this.tail = null + this.length-- + } else { + this.tail = this.tail.prev + this.tail.next = null + this.length-- + } + } + + deleteAtHead() { + if (!this.head) throw new Error("Empty Linked List!") + + if (this.length === 1) { + this.head = null + this.tail = null + this.length-- + } else { + this.head = this.head.next + this.head.prev = null + this.length-- + } + } + + print() { + if (!this.head) console.log("Empty Linked List!") + + else { + let curr = this.head + let str = "null <- " + while (curr) { + str += curr.val + if (curr.next) { + str += " <-> " + } else { + str += " -> null" + } + curr = curr.next + } + console.log(str) + } + } + + getLength() { + return this.length + } + + getAtIdx(idx) { + if (idx >= this.length || idx < 0) throw new Error("Index out of bounds!") + + else { + let currIdx = 0 + let curr = this.head + + while (currIdx < idx) { + curr = curr.next + currIdx++ + } + + return curr.val + } + } + + putAtIdx(val, idx) { + if (idx > this.length || idx < 0) throw new Error("Index out of bounds!") + + const node = new Node(val) + + if (idx === 0) { + this.head.prev = node + node.next = this.head + this.head = node + } else { + let currIdx = 0 + let curr = this.head + + while (currIdx < idx) { + curr = curr.next + currIdx++ + } + + node.next = curr + node.prev = curr.prev + curr.prev.next = node + } + + this.length++ + } + + deleteAtIdx(idx) { + if (idx >= this.length || idx < 0) throw new Error("Index out of bounds!") + + let currIdx = 0 + let curr = this.head + + while (currIdx < idx) { + curr = curr.next + currIdx++ + } + + curr.prev.next = curr.next + curr.next.prev = curr.prev + this.length-- + } + + search(val) { + let curr = this.head + let currIdx = 0 + + while (curr) { + if (curr.val === val) return currIdx + + curr = curr.next + currIdx++ + } + + return "Value not found" + } + + toArray() { + let arr = [] + + let curr = this.head + + while (curr) { + arr.push(curr.val) + curr = curr.next + } + + return arr + } +} + +// SAMPLE USECASE +const list = new DoublyLinkedList () +list.insertAtHead (10) +list.insertAtTail (20) +list.insertAtHead (30) +list.insertAtTail (40) +list.putAtIdx (100, 2) +list.print () \ No newline at end of file diff --git a/Linked List/floyds_cycle_detection.cpp b/Linked List/floyds_cycle_detection.cpp new file mode 100644 index 00000000..94868fa5 --- /dev/null +++ b/Linked List/floyds_cycle_detection.cpp @@ -0,0 +1,94 @@ +/* + +Initialize two-pointers and start traversing the linked list. +Move the slow pointer by one position. +Move the fast pointer by two positions. +If both pointers meet at some point then a loop exists and if the fast pointer meets the end position then no loop exists. + + +Time complexity: O(n), as the loop is traversed once. +Auxiliary Space: O(1), only two pointers are used therefore constant space complexity. + +*/ + + +#include +using namespace std; + +class Node { +public: + int data; + Node* next; + + Node(int data) + { + this->data = data; + next = NULL; + } +}; + +// initialize a new head for the linked list +Node* head = NULL; +class Linkedlist { +public: + // insert new value at the start + void insert(int value) + { + Node* newNode = new Node(value); + if (head == NULL) + head = newNode; + else { + newNode->next = head; + head = newNode; + } + } + + // detect if there is a loop + // in the linked list + bool detectLoop() + { + Node *slowPointer = head, + *fastPointer = head; + + while (slowPointer != NULL + && fastPointer != NULL + && fastPointer->next != NULL) { + slowPointer = slowPointer->next; + fastPointer = fastPointer->next->next; + if (slowPointer == fastPointer) + return 1; + } + + return 0; + } +}; + +int main() +{ + Linkedlist l1; + // inserting new values + l1.insert(10); + l1.insert(20); + l1.insert(30); + l1.insert(40); + l1.insert(50); + + // adding a loop for the sake + // of this example + Node* temp = head; + while (temp->next != NULL) + temp = temp->next; + + temp->next = head; + + // loop added; + + if (l1.detectLoop()) + cout << "Loop exists in the" + << " Linked List" << endl; + else + cout << "Loop does not exists " + << "in the Linked List" << endl; + + return 0; +} \ No newline at end of file diff --git a/Linked List/floyds_cycle_detection.go b/Linked List/floyds_cycle_detection.go new file mode 100644 index 00000000..6f20b922 --- /dev/null +++ b/Linked List/floyds_cycle_detection.go @@ -0,0 +1,73 @@ +// Floyds Cycle Detection +/* + Explanation: + The Floyd's cycle detection algorithm is a two-pointer algorithm used to detect if a linked list has a + cycle. It works by initializing two pointers, slow and fast, both pointing to the head of the linked list. + Then, it moves the slow pointer by one step and the fast pointer by two steps. If there is a cycle in the linked list, + the fast pointer will eventually catch up to the slow pointer. If there is no cycle, the fast pointer will reach the + end of the linked list. This algorithm has a time complexity of O(n) and a space complexity of O(1). + + In the implementation above, we define a Node struct to represent a node in the linked list. + The hasCycle function takes the head of the linked list as input and returns a boolean indicating + whether the linked list has a cycle or not. We initialize two pointers, slow and fast, both pointing + to the head of the linked list. We then loop through the linked list while the fast pointer is not + null and the next of the fast pointer is not null. In each iteration, we move the slow pointer + by one step and the fast pointer by two steps. If the slow and fast pointers meet, we know there + is a cycle and we return true. Otherwise, we continue the loop until the end of the linked list and return false. + + The time complexity of Floyd's cycle detection algorithm is O(n), where n is the number of nodes in the linked list. + This is because in the worst case, we would need to traverse the entire linked list twice: once to reach the point + where the cycle begins, and once more to detect the cycle. + + The space complexity of the algorithm is O(1), as we are only using a few constant extra variables to perform + the detection, regardless of the size of the linked list. +*/ + +package main + +import "fmt" + +// Definition for singly-linked list. +type Node struct { + value int + next *Node +} + + +// DetectCycle detects if there's a cycle in a linked list using Floyd's cycle detection algorithm +func hasCycle(head *Node) bool { + // Initialize slow and fast pointers + slow, fast := head, head + + // Move slow and fast pointers until they meet or fast pointer reaches end of the list + for fast != nil && fast.next != nil { + slow = slow.next + fast = fast.next.next + + // If slow and fast pointers meet, there's a cycle + if slow == fast { + return true + } + } + + // If fast pointer reaches end of the list, there's no cycle + return false +} + +func main() { + // Create a linked list with a cycle + head := &Node{value: 1} + node2 := &Node{value: 2} + node3 := &Node{value: 3} + node4 := &Node{value: 4} + node5 := &Node{value: 5} + head.next = node2 + node2.next = node3 + node3.next = node4 + node4.next = node5 + node5.next = node2 // Create a cycle + + // Detect cycle in the linked list + hasCycle := hasCycle(head) + fmt.Println(hasCycle) // Output: true +} diff --git a/Linked List/floyds_cycle_detection.java b/Linked List/floyds_cycle_detection.java new file mode 100644 index 00000000..5868baf1 --- /dev/null +++ b/Linked List/floyds_cycle_detection.java @@ -0,0 +1,93 @@ +/* + +Initialize two-pointers and start traversing the linked list. +Move the slow pointer by one position. +Move the fast pointer by two positions. +If both pointers meet at some point then a loop exists and if the fast pointer meets the end position then no loop exists. + + +Time complexity: O(n), as the loop is traversed once. +Auxiliary Space: O(1), only two pointers are used therefore constant space complexity. + +*/ + + +import java.util.*; + +class GFG{ + +static class Node { + int data; + Node next; + + Node(int data) + { + this.data = data; + next = null; + } +}; + +// initialize a new head for the linked list +static Node head = null; +static class Linkedlist { + // insert new value at the start + void insert(int value) + { + Node newNode = new Node(value); + if (head == null) + head = newNode; + else { + newNode.next = head; + head = newNode; + } + } + + // detect if there is a loop + // in the linked list + boolean detectLoop() + { + Node slowPointer = head, + fastPointer = head; + + while (slowPointer != null + && fastPointer != null + && fastPointer.next != null) { + slowPointer = slowPointer.next; + fastPointer = fastPointer.next.next; + if (slowPointer == fastPointer) + return true; + } + + return false; + } +} + +public static void main(String[] args) +{ + Linkedlist l1 = new Linkedlist(); + // inserting new values + l1.insert(10); + l1.insert(20); + l1.insert(30); + l1.insert(40); + l1.insert(50); + + // adding a loop for the sake + // of this example + Node temp = head; + while (temp.next != null) + temp = temp.next; + + temp.next = head; + + // loop added; + + if (l1.detectLoop()) + System.out.print("Loop exists in the" + + " Linked List" +"\n"); + else + System.out.print("Loop does not exists " + + "in the Linked List" +"\n"); + +} +} \ No newline at end of file diff --git a/Linked List/intersection_of_two_linked_lists.cpp b/Linked List/intersection_of_two_linked_lists.cpp new file mode 100644 index 00000000..8439bd65 --- /dev/null +++ b/Linked List/intersection_of_two_linked_lists.cpp @@ -0,0 +1,79 @@ +/* + + Given the heads of two singly linked-lists headA and headB, return the node at which the two lists intersect. If the two linked lists have no intersection at all, return null. + + + Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3 + Output: Intersected at '8' + + Input: intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 + Output: Intersected at '2' + + Constraints: + + > The number of nodes of listA is in the m. + > The number of nodes of listB is in the n. + > 1 <= m, n <= 3 * 104 + > 1 <= Node.val <= 105 + > 0 <= skipA < m + > 0 <= skipB < n + > intersectVal is 0 if listA and listB do not intersect. + > intersectVal == listA[skipA] == listB[skipB] if listA and listB intersect. + +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { + // Define basic variables needed + int len1=0; + int len2=0; + ListNode* tempA=headA; + ListNode* tempB=headB; + + // Calculate the length of both Linked Lists and store in len1 and len2 + while(tempA!=NULL) + { + len1++; + tempA=tempA->next; + } + while(tempB!=NULL) + { + len2++; + tempB=tempB->next; + } + + // Here, we assume that length of Linked-List A is less than or equal + // to that of Linked List B + if(len1>len2){ + swap(headA,headB); + } + + // Re-initialize variables + tempA=headA; + tempB=headB; + int n=abs(len2-len1); + while(n--) + { + tempB=tempB->next; + } + + // Finally, Find the Intersection Node + while(tempA!=tempB) + { + tempA=tempA->next; + tempB=tempB->next; + } + + // Return the final answer + return tempA; + } +}; \ No newline at end of file diff --git a/Linked List/liniked_list_sort_list.cpp b/Linked List/liniked_list_sort_list.cpp new file mode 100644 index 00000000..03298627 --- /dev/null +++ b/Linked List/liniked_list_sort_list.cpp @@ -0,0 +1,75 @@ +/* + Given the head of a linked list, return the list after sorting it in ascending order. + + Input: head = [4,2,1,3] + Output: [1,2,3,4] + + Input: head = [-1,5,3,4,0] + Output: [-1,0,3,4,5] + + Input: head = [] + Output: [] + + + Constraints: + + The number of nodes in the list is in the range [0, 5 * 104]. + -105 <= Node.val <= 105 + + Follow up: Can you sort the linked list in O(n logn) time and O(1) memory (i.e. constant space)? +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* compute_midpoint(ListNode* head){ + if(head == NULL || head->next == NULL){ + return head; + } + ListNode* slow = head; + ListNode* fast = head->next; + while(fast != NULL && fast->next != NULL){ + slow = slow->next; + fast = fast->next->next; + } + return slow; + } + ListNode* merge_two_lists(ListNode* A, ListNode* B){ + if(A == NULL) + return B; + else if(B == NULL) + return A; + ListNode* C = NULL; + if(A->val < B->val){ + C = A; + C->next = merge_two_lists(A->next, B); + } + else{ + C = B; + C->next = merge_two_lists(A, B->next); + } + return C; + } + ListNode* sortList(ListNode* head) { + if(head == NULL || head->next == NULL) + return head; + ListNode* mid = compute_midpoint(head); + ListNode* A = head; + ListNode* B = mid->next; + mid->next = NULL; + A = sortList(A); + B = sortList(B); + + ListNode* C = merge_two_lists(A, B); + return C; + } +}; \ No newline at end of file diff --git a/Linked_List/linked_list.go b/Linked List/linked_list.go similarity index 100% rename from Linked_List/linked_list.go rename to Linked List/linked_list.go diff --git a/Linked List/linked_list.js b/Linked List/linked_list.js new file mode 100644 index 00000000..4c771140 --- /dev/null +++ b/Linked List/linked_list.js @@ -0,0 +1,226 @@ +/* + insertAtTail (val) + insertAtHead (val) + deleteAtTail () + deleteAtHead () + getLength () + print () + getAtIdx (idx) + putAtIdx (val, idx) + deleteAtIdx (idx) + search (val) + toArray () + reverse () +*/ + +class Node { + constructor(val) { + this.val = val + this.next = null + } +} + +class LinkedList { + constructor() { + this.head = null + this.tail = null + this.length = 0 + } + + insertAtTail(val) { + const node = new Node(val) + + if (!this.head) { + this.head = node + this.tail = node + this.length++ + } else { + this.tail.next = node + this.tail = node + this.length++ + } + } + + insertAtHead(val) { + const node = new Node(val) + + if (!this.head) { + this.head = node + this.tail = node + this.length++ + } else { + node.next = this.head + this.head = node + this.length++ + } + } + + print() { + if (this.length === 0) console.log ("Empty Linked List!") + else { + let curr = this.head + let str = '' + + while (curr) { + str += curr.val + ' -> ' + curr = curr.next + } + + console.log(str + 'null') + } + } + + deleteAtTail() { + if (!this.head) console.log("Empty Linked List!") + + else { + let curr = this.head + let prev = this.head + + while (curr.next) { + prev = curr + curr = curr.next + } + + prev.next = null + this.tail = prev + this.length-- + } + } + + deleteAtHead() { + if (!this.head) console.log("Empty Linked List!") + + else { + let curr = this.head + this.head = curr.next + this.length-- + } + } + + + getLength() { + return this.length + } + + getAtIdx (idx) { + if (idx >= this.length || idx < 0) throw new Error ("Index out of bounds") + + else { + let currIdx = 0 + let curr = this.head + + while (currIdx < idx) { + currIdx++ + curr = curr.next + } + + return curr.val + } + } + + putAtIdx (val, idx) { + if (idx > this.length || idx < 0) throw new Error ("Index out of bounds") + + const node = new Node (val) + + if (!this.head) { + this.head = node + this.length++ + return + } + + let currIdx = 0 + let curr = this.head + let prev = this.head + + while (currIdx < idx) { + currIdx++ + prev = curr + curr = curr.next + } + + prev.next = node + node.next = curr + this.length++ + } + + deleteAtIdx (idx) { + if (idx >= this.length || idx < 0) throw new Error ("Index out of bounds") + + else { + let currIdx = 0 + let curr = this.head + let prev = this.head + + while (currIdx < idx) { + currIdx++ + prev = curr + curr = curr.next + } + + prev.next = curr.next + this.length-- + } + } + + search (val) { + if (!this.head) return "Empty Linked List!" + if (!this.head.next && this.head.val !== val) return null + if (!this.head.next && this.head.val === val) return 0 + + let currIdx = 0 + let curr = this.head + + while (curr) { + if (curr.val === val) return currIdx + currIdx++ + curr = curr.next + } + + return null + } + + toArray () { + const arr = [] + if (!this.head) return null + + let curr = this.head + + while (curr) { + arr.push (curr.val) + curr = curr.next + } + + return arr + } + + reverse () { + if (!this.head) throw new Error ("Empty Linked List") + if (!this.head.next) return + + let prev = null + let curr = this.head + let next = curr.next + + while (curr) { + next = curr.next + curr.next = prev + prev = curr + curr = next + } + + this.head = prev + } +} + +// SAMPLE USECASE + +const list = new LinkedList() +list.insertAtTail (10) +list.insertAtTail (20) +list.insertAtTail (30) +list.insertAtTail (40) +list.insertAtTail (50) +list.reverse () +list.print () \ No newline at end of file diff --git a/Linked List/linked_list.py b/Linked List/linked_list.py new file mode 100644 index 00000000..6af19fc8 --- /dev/null +++ b/Linked List/linked_list.py @@ -0,0 +1,82 @@ +# Implementation of Singly Linked List in Python +''' + This implementation defines a Node class to represent a node in the linked list, and a LinkedList + class to represent the linked list itself. The LinkedList class has methods to insert nodes at the + beginning or end of the list, delete nodes from the list, and print the list. Each node has two + attributes: its data and a reference to the next node in the list (next). + The LinkedList class has one attribute: the head node of the list (head). When a new node is + inserted at the beginning of the list, its next attribute is set to the current head node, and the + head attribute is set to the new node. When a new node is inserted at the end of the list, it is + appended to the last node's next attribute. When a node is deleted, the list is traversed to find + the node with the given data, and its next attribute is set to the node after it. Finally, the + print_list method traverses the list and prints each node's data. +''' +# Define a class to represent a node in the linked list +class Node: + def __init__(self, data): + # Each node has two attributes: data and a reference to the next node + self.data = data + self.next = None + +# Define a class to represent the linked list itself +class LinkedList: + def __init__(self): + # Each linked list has one attribute: the head node (which initially is None) + self.head = None + + # Method to insert a new node at the beginning of the linked list + def insert_at_beginning(self, data): + # Create a new node + new_node = Node(data) + + # Set the next node of the new node to be the current head node + new_node.next = self.head + + # Set the head node to be the new node + self.head = new_node + + # Method to insert a new node at the end of the linked list + def insert_at_end(self, data): + # Create a new node + new_node = Node(data) + + # If the list is empty, set the head node to be the new node + if self.head is None: + self.head = new_node + return + + # Traverse to the last node in the list + current_node = self.head + while current_node.next: + current_node = current_node.next + + # Set the next node of the last node to be the new node + current_node.next = new_node + + # Method to delete the first occurrence of a node with the given data + def delete_node(self, data): + # If the list is empty, do nothing + if self.head is None: + return + + # If the node to be deleted is the head node, set the head node to be the next node + if self.head.data == data: + self.head = self.head.next + return + + # Traverse the list to find the node to be deleted + current_node = self.head + while current_node.next: + if current_node.next.data == data: + current_node.next = current_node.next.next + return + current_node = current_node.next + + # Method to print the linked list + def print_list(self): + # Traverse the list and print each node's data + current_node = self.head + while current_node: + print(current_node.data, end=" -> ") + current_node = current_node.next + print("None") diff --git a/Linked List/linked_list_add_two_numbers.py b/Linked List/linked_list_add_two_numbers.py new file mode 100644 index 00000000..2021deca --- /dev/null +++ b/Linked List/linked_list_add_two_numbers.py @@ -0,0 +1,60 @@ +''' +You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. + +You may assume the two numbers do not contain any leading zero, except the number 0 itself. + +Example 1: +Input: l1 = [2,4,3], l2 = [5,6,4] +Output: [7,0,8] +Explanation: 342 + 465 = 807. + +Example 2: +Input: l1 = [0], l2 = [0] +Output: [0] + +Example 3: +Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] +Output: [8,9,9,9,0,0,0,1] + + +Constraints: + +The number of nodes in each linked list is in the range [1, 100]. +0 <= Node.val <= 9 +It is guaranteed that the list represents a number that does not have leading zeros. + +Complexity +Time complexity : O(max(l1,l2)). +Space complexity : O(n). + +''' + +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + carry = 0 + head = ListNode('*'); + header_pointer = head; + while(l1 != None or l2 != None): + temp = carry + if(l1 == None): + temp += l2.val + l2 = l2.next + elif(l2 == None): + temp += l1.val + l1 = l1.next + else: + temp += l1.val+l2.val + l1 = l1.next + l2 = l2.next + head.next = ListNode(temp % 10) + carry = temp // 10 + head = head.next + if(carry != 0): + head.next = ListNode(carry) + head = head.next + return header_pointer.next diff --git a/Linked List/linked_list_compute_middle.cpp b/Linked List/linked_list_compute_middle.cpp new file mode 100644 index 00000000..e1c3c71d --- /dev/null +++ b/Linked List/linked_list_compute_middle.cpp @@ -0,0 +1,75 @@ +// Given the head of a singly linked list, return the middle node of the linked list. +// If there are two middle nodes, return the second middle node. + +// Input: head = [1,2,3,4,5] +// Output: [3,4,5] +// Explanation: The middle node of the list is node 3. + +// TIME ANS SPACE COMPLEXITY OF THE SOLUTION IS :: +// Time complexity: O(n) +// Space complexity: O(1) + +#include +using namespace std; + +// creating Node manualy +class Node +{ +public: + int data; + Node *next; + + Node() + { + this->data = 0; + this->next = NULL; + } + Node(int data) + { + this->data = data; + this->next = NULL; + } +}; + +Node *middleNode(Node *head) +{ + + Node *slow = head; + Node *fast = head; + + // Move slow pointer by one node at a time and fast pointer two nodes at a time. + // While fast pointer reaches the end, slow pointer must have reached the middle node. + + while (fast != NULL) + { + fast = fast->next; + if (fast != NULL) + { + fast = fast->next; + slow = slow->next; + } + } + return slow; // return slow as ans +} + +int main() +{ + // creating nodes. + Node *first = new Node(1); + Node *second = new Node(2); + Node *third = new Node(3); + Node *fourth = new Node(4); + Node *fifth = new Node(5); + + // head of linked list + Node *head = first; + + // Creating connection of nodes + first->next = second; + second->next = third; + third->next = fourth; + fourth->next = fifth; + + cout << "The middle node of the list is node " << middleNode(head)->data << endl; + return 0; +} \ No newline at end of file diff --git a/Linked List/linked_list_compute_middle.java b/Linked List/linked_list_compute_middle.java new file mode 100644 index 00000000..40363256 --- /dev/null +++ b/Linked List/linked_list_compute_middle.java @@ -0,0 +1,48 @@ +/* +In this code, the ListNode class represents a node in the linked list, with an integer value val and a reference to the next node next. The FindMiddleLinkedList class has a findMiddle method that takes a ListNode as its input and returns the middle node of the linked list using the fast and slow pointers algorithm. + +*/ +class ListNode { + int val; + ListNode next; + + ListNode(int val) { + this.val = val; + this.next = null; + } +} + +class FindMiddleLinkedList { + public ListNode findMiddle(ListNode head) { + // Create two pointers, slow and fast, both initially pointing to the head of the linked list. + ListNode slow = head; + ListNode fast = head; + + // Move the slow pointer one step at a time and the fast pointer two steps at a time, until the fast pointer reaches the end of the list. + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + // When the fast pointer reaches the end of the list, the slow pointer will be pointing to the middle of the list. + return slow; + } + + public static void main(String[] args) { + // Create a sample linked list + ListNode head = new ListNode(1); + head.next = new ListNode(2); + head.next.next = new ListNode(3); + head.next.next.next = new ListNode(4); + head.next.next.next.next = new ListNode(5); + + // Create an instance of the FindMiddleLinkedList class + FindMiddleLinkedList f = new FindMiddleLinkedList(); + + // Call the findMiddle method to get the middle node of the linked list + ListNode middleNode = f.findMiddle(head); + + // Print the value of the middle node + System.out.println("The middle node has value: " + middleNode.val); + } +} diff --git a/Linked_List/linked_list_delete_at_any_pos.cpp b/Linked List/linked_list_delete_at_any_pos.cpp similarity index 95% rename from Linked_List/linked_list_delete_at_any_pos.cpp rename to Linked List/linked_list_delete_at_any_pos.cpp index 798bc483..664d8f9f 100644 --- a/Linked_List/linked_list_delete_at_any_pos.cpp +++ b/Linked List/linked_list_delete_at_any_pos.cpp @@ -1,122 +1,122 @@ -// Insert at any position in a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -int length(node* head){ - int len = 0; - while(head != NULL){ - head = head->next; - len++; - } - return len; -} -void insert_at_head(node *&head, int data){ - node *n = new node(data); - n->next = head; - head = n; -} -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void insert_at_anypos(node *&head, int data, int pos){ - if(head == NULL || pos == 0){ - insert_at_head(head, data); - } - else if(pos > length(head)){ - insert_at_tail(head, data); - } - else{ - node* temp = head; - node *n = new node(data); - for(int i = 0; i < pos - 1; i++){ - temp = temp->next; - } - n->next = temp->next; - temp->next = n; - } -} -void delete_at_head(node *&head){ - if(head == NULL){ - return; - } - node *temp = head; - head = head->next; - delete temp; -} -void delete_at_tail(node *&head){ - node *prev = NULL; - node *temp = head; - while(temp->next != NULL){ - prev = temp; - temp = temp->next; - } - prev->next = NULL; - delete temp; -} -void delete_at_any_pos(node *&head, int pos){ - if(pos == 0 || pos == 1){ - delete_at_head(head); - return; - } - else if(pos > length(head)){ - delete_at_tail(head); - } - else{ - node *prev = NULL; - node *temp = head; - for(int i = 0; i < pos - 1; i++){ - prev = temp; - temp = temp->next; - } - prev->next = temp->next; - delete temp; - } -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_head(head, 1); - insert_at_head(head, 2); - insert_at_head(head, 3); - insert_at_anypos(head, 6, 10); - insert_at_tail(head, 33); - insert_at_head(head, 44); - insert_at_anypos(head, 66, 4); - insert_at_tail(head, 100); - insert_at_anypos(head, 11, 3); - print_linked_list(head); - delete_at_any_pos(head, 2); - delete_at_any_pos(head, 3); - delete_at_any_pos(head, 4); - insert_at_anypos(head, 22, 2); - cout << endl; - print_linked_list(head); - delete_at_any_pos(head, 2); - cout << endl; - print_linked_list(head); - return 0; +// Insert at any position in a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +int length(node* head){ + int len = 0; + while(head != NULL){ + head = head->next; + len++; + } + return len; +} +void insert_at_head(node *&head, int data){ + node *n = new node(data); + n->next = head; + head = n; +} +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void insert_at_anypos(node *&head, int data, int pos){ + if(head == NULL || pos == 0){ + insert_at_head(head, data); + } + else if(pos > length(head)){ + insert_at_tail(head, data); + } + else{ + node* temp = head; + node *n = new node(data); + for(int i = 0; i < pos - 1; i++){ + temp = temp->next; + } + n->next = temp->next; + temp->next = n; + } +} +void delete_at_head(node *&head){ + if(head == NULL){ + return; + } + node *temp = head; + head = head->next; + delete temp; +} +void delete_at_tail(node *&head){ + node *prev = NULL; + node *temp = head; + while(temp->next != NULL){ + prev = temp; + temp = temp->next; + } + prev->next = NULL; + delete temp; +} +void delete_at_any_pos(node *&head, int pos){ + if(pos == 0 || pos == 1){ + delete_at_head(head); + return; + } + else if(pos > length(head)){ + delete_at_tail(head); + } + else{ + node *prev = NULL; + node *temp = head; + for(int i = 0; i < pos - 1; i++){ + prev = temp; + temp = temp->next; + } + prev->next = temp->next; + delete temp; + } +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_head(head, 1); + insert_at_head(head, 2); + insert_at_head(head, 3); + insert_at_anypos(head, 6, 10); + insert_at_tail(head, 33); + insert_at_head(head, 44); + insert_at_anypos(head, 66, 4); + insert_at_tail(head, 100); + insert_at_anypos(head, 11, 3); + print_linked_list(head); + delete_at_any_pos(head, 2); + delete_at_any_pos(head, 3); + delete_at_any_pos(head, 4); + insert_at_anypos(head, 22, 2); + cout << endl; + print_linked_list(head); + delete_at_any_pos(head, 2); + cout << endl; + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_delete_at_head.cpp b/Linked List/linked_list_delete_at_head.cpp similarity index 95% rename from Linked_List/linked_list_delete_at_head.cpp rename to Linked List/linked_list_delete_at_head.cpp index 1a793e38..54717b17 100644 --- a/Linked_List/linked_list_delete_at_head.cpp +++ b/Linked List/linked_list_delete_at_head.cpp @@ -1,45 +1,45 @@ -// Delete from Head in a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_head(node *&head, int data){ - node *n = new node(data); - n->next = head; - head = n; -} -void delete_at_head(node *&head){ - if(head == NULL){ - return; - } - node *temp = head; - head = head->next; - delete temp; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_head(head, 1); - insert_at_head(head, 2); - insert_at_head(head, 3); - print_linked_list(head); - cout << endl; - delete_at_head(head); - delete_at_head(head); - print_linked_list(head); - return 0; +// Delete from Head in a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_head(node *&head, int data){ + node *n = new node(data); + n->next = head; + head = n; +} +void delete_at_head(node *&head){ + if(head == NULL){ + return; + } + node *temp = head; + head = head->next; + delete temp; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_head(head, 1); + insert_at_head(head, 2); + insert_at_head(head, 3); + print_linked_list(head); + cout << endl; + delete_at_head(head); + delete_at_head(head); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_dalete_at_tail.cpp b/Linked List/linked_list_delete_at_tail.cpp similarity index 95% rename from Linked_List/linked_list_dalete_at_tail.cpp rename to Linked List/linked_list_delete_at_tail.cpp index 39b9c10d..1ce28b3b 100644 --- a/Linked_List/linked_list_dalete_at_tail.cpp +++ b/Linked List/linked_list_delete_at_tail.cpp @@ -1,59 +1,59 @@ -// Delete from Tail in a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; - -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void delete_at_tail(node *&head){ - node *prev = NULL; - node *temp = head; - while(temp->next != NULL){ - prev = temp; - temp = temp->next; - } - prev->next = NULL; - delete temp; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_tail(head, 20); - insert_at_tail(head, 33); - insert_at_tail(head, 100); - insert_at_tail(head, 200); - insert_at_tail(head, 201); - insert_at_tail(head, 202); - print_linked_list(head); - cout << endl; - delete_at_tail(head); - delete_at_tail(head); - - print_linked_list(head); - return 0; -} +// Delete from Tail in a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; + +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void delete_at_tail(node *&head){ + node *prev = NULL; + node *temp = head; + while(temp->next != NULL){ + prev = temp; + temp = temp->next; + } + prev->next = NULL; + delete temp; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_tail(head, 20); + insert_at_tail(head, 33); + insert_at_tail(head, 100); + insert_at_tail(head, 200); + insert_at_tail(head, 201); + insert_at_tail(head, 202); + print_linked_list(head); + cout << endl; + delete_at_tail(head); + delete_at_tail(head); + + print_linked_list(head); + return 0; +} diff --git a/Linked List/linked_list_delete_node.go b/Linked List/linked_list_delete_node.go new file mode 100644 index 00000000..dbceb40d --- /dev/null +++ b/Linked List/linked_list_delete_node.go @@ -0,0 +1,18 @@ +/* + We are given a pointer to a node (not the tail node) in a singly linked list. Delete that node from the + linked list. +*/ +package main + +/* + Approach: + 1. We can move the data from the next node into the current node + 2. Delete the next node. + Time Complexity: O(1). Space Complexity: O(1). +*/ +func deleteNode(node *ListNode) { + temp = node.next + node.data = node.next.data + node.next = temp.next + temp = nil +} \ No newline at end of file diff --git a/Linked List/linked_list_even_or_odd.go b/Linked List/linked_list_even_or_odd.go new file mode 100644 index 00000000..575ab03e --- /dev/null +++ b/Linked List/linked_list_even_or_odd.go @@ -0,0 +1,32 @@ +/* + Check whether the given Linked List length is even or odd? + + Approach: + Use a 2x pointer. Take a pointer that moves at 2x [two nodes at a time]. At the end, if the length is even, + then the pointer will be nil; otherwise it will point to the last node. +*/ +// Time Complexity: O(⌊n/2⌋) ≈O(n). Space Complexity: O(1). +package main + +// has two fields [data] of type integer and [next] of type *node (holds the memory address of next node) +type node struct { + data int + next *node +} +//has three fields length, head and tail node +type LinkedList struct { + length int + head *node + tail *node +} + +func (ll *LinkedList) IsLengthEven() bool { + current := ll.head + for current != nil && current.next != nil { + current = current.next.next + } + if current != nil { + return false + } + return true +} \ No newline at end of file diff --git a/Linked_List/linked_list_find_length.cpp b/Linked List/linked_list_find_length.cpp similarity index 95% rename from Linked_List/linked_list_find_length.cpp rename to Linked List/linked_list_find_length.cpp index 1ebd3d6e..9097c7cd 100644 --- a/Linked_List/linked_list_find_length.cpp +++ b/Linked List/linked_list_find_length.cpp @@ -1,44 +1,44 @@ -// Find length of a LinkedList -// Program Author : Abhisek Kumar Gupta - -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -int length(node* head){ - int len = 0; - while(head != NULL){ - head = head->next; - len++; - } - return len; -} -void insert_at_head(node *&head, int data){ - node *n = new node(data); - n->next = head; - head = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_head(head, 1); - insert_at_head(head, 2); - insert_at_head(head, 3); - print_linked_list(head); - int len = length(head); - cout << endl < +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +int length(node* head){ + int len = 0; + while(head != NULL){ + head = head->next; + len++; + } + return len; +} +void insert_at_head(node *&head, int data){ + node *n = new node(data); + n->next = head; + head = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_head(head, 1); + insert_at_head(head, 2); + insert_at_head(head, 3); + print_linked_list(head); + int len = length(head); + cout << endl < -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -int length(node* head){ - int len = 0; - while(head != NULL){ - head = head->next; - len++; - } - return len; -} -void insert_at_head(node *&head, int data){ - node *n = new node(data); - n->next = head; - head = n; -} -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void insert_at_anypos(node *&head, int data, int pos){ - if(head == NULL || pos == 0){ - insert_at_head(head, data); - } - else if(pos > length(head)){ - insert_at_tail(head, data); - } - else{ - node* temp = head; - node *n = new node(data); - for(int i = 0; i < pos - 1; i++){ - temp = temp->next; - } - n->next = temp->next; - temp->next = n; - } -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_head(head, 1); - insert_at_head(head, 2); - insert_at_head(head, 3); - insert_at_anypos(head, 6, 10); - insert_at_tail(head, 33); - insert_at_head(head, 44); - insert_at_anypos(head, 66, 4); - insert_at_tail(head, 100); - insert_at_anypos(head, 11, 3); - print_linked_list(head); - return 0; +// Insert at any position in a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +int length(node* head){ + int len = 0; + while(head != NULL){ + head = head->next; + len++; + } + return len; +} +void insert_at_head(node *&head, int data){ + node *n = new node(data); + n->next = head; + head = n; +} +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void insert_at_anypos(node *&head, int data, int pos){ + if(head == NULL || pos == 0){ + insert_at_head(head, data); + } + else if(pos > length(head)){ + insert_at_tail(head, data); + } + else{ + node* temp = head; + node *n = new node(data); + for(int i = 0; i < pos - 1; i++){ + temp = temp->next; + } + n->next = temp->next; + temp->next = n; + } +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_head(head, 1); + insert_at_head(head, 2); + insert_at_head(head, 3); + insert_at_anypos(head, 6, 10); + insert_at_tail(head, 33); + insert_at_head(head, 44); + insert_at_anypos(head, 66, 4); + insert_at_tail(head, 100); + insert_at_anypos(head, 11, 3); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_insert_at_head.cpp b/Linked List/linked_list_insert_at_head.cpp similarity index 95% rename from Linked_List/linked_list_insert_at_head.cpp rename to Linked List/linked_list_insert_at_head.cpp index 4879f46b..f2b7a50c 100644 --- a/Linked_List/linked_list_insert_at_head.cpp +++ b/Linked List/linked_list_insert_at_head.cpp @@ -1,34 +1,34 @@ -// Search recursively in a LinkedList -// Program Author : Abhisek Kumar Gupta - -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_head(node *&head, int data){ - node *n = new node(data); - n->next = head; - head = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_head(head, 1); - insert_at_head(head, 2); - insert_at_head(head, 3); - print_linked_list(head); - return 0; +// Search recursively in a LinkedList +// Program Author : Abhisek Kumar Gupta + +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_head(node *&head, int data){ + node *n = new node(data); + n->next = head; + head = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_head(head, 1); + insert_at_head(head, 2); + insert_at_head(head, 3); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_insert_at_tail.cpp b/Linked List/linked_list_insert_at_tail.cpp similarity index 95% rename from Linked_List/linked_list_insert_at_tail.cpp rename to Linked List/linked_list_insert_at_tail.cpp index 653adf27..55b93bed 100644 --- a/Linked_List/linked_list_insert_at_tail.cpp +++ b/Linked List/linked_list_insert_at_tail.cpp @@ -1,43 +1,43 @@ -// Insert at Tail in a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; - -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_tail(head, 20); - insert_at_tail(head, 33); - insert_at_tail(head, 100); - insert_at_tail(head, 200); - print_linked_list(head); - - return 0; +// Insert at Tail in a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; + +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_tail(head, 20); + insert_at_tail(head, 33); + insert_at_tail(head, 100); + insert_at_tail(head, 200); + print_linked_list(head); + + return 0; } \ No newline at end of file diff --git a/Linked List/linked_list_kth_from_end.go b/Linked List/linked_list_kth_from_end.go new file mode 100644 index 00000000..410617c5 --- /dev/null +++ b/Linked List/linked_list_kth_from_end.go @@ -0,0 +1,31 @@ +// Finding Kth node from end of a LinkedList +package main + +/* +Approach +1. Make two pointers slow and fast and keep them both on the head of the linked list. +2. Now, move the fast pointer k steps away from the slow pointer. +3. Now, move the fast pointer and the slow pointer each one step at a time till + the fast pointer reaches the tail of the linked list. When the fast pointer reaches the tail, + the node at which the slow pointer resides will be our answer. + +Time and space complexity + + Time complexity of the solution is O(n) as we have traversed the linked list once. + We have traversed it in two parts. + First we traversed k elements and then we traversed the remaining (size minus k) elements. + + Space complexity is O(1) as we have not used any extra space for the solution. + + +*/ +func kthFromEnd(head *ListNode, n int) *ListNode { + first, second := head, head + for ; n > 0; n-- { + second = second.next + } + for ; second.next != nil; first, second = first.next, second.next { + } + first.next = first.next.next + return first +} \ No newline at end of file diff --git a/Linked_List/linked_list_kth_node_from_end.cpp b/Linked List/linked_list_kth_node_from_end.cpp similarity index 95% rename from Linked_List/linked_list_kth_node_from_end.cpp rename to Linked List/linked_list_kth_node_from_end.cpp index 726a3124..b64e5c47 100644 --- a/Linked_List/linked_list_kth_node_from_end.cpp +++ b/Linked List/linked_list_kth_node_from_end.cpp @@ -1,65 +1,65 @@ -// Finding Kth node from end of a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -node* kth_node_from_end(node *head, int k){ - if(head->next == NULL || head == NULL){ - return head; - } - node *slow = head; - node *fast = head; - while(k--){ - fast = fast->next; - } - while(fast){ - fast = fast->next; - slow = slow->next; - } - return slow; -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - int k; - cin >> k; - node *kth_node = kth_node_from_end(head, k); - cout << endl; - cout << kth_node->data << endl; - return 0; +// Finding Kth node from end of a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +node* kth_node_from_end(node *head, int k){ + if(head->next == NULL || head == NULL){ + return head; + } + node *slow = head; + node *fast = head; + while(k--){ + fast = fast->next; + } + while(fast){ + fast = fast->next; + slow = slow->next; + } + return slow; +} +int main(){ + node *head = NULL; + makeLinkedList(head); + print_linked_list(head); + int k; + cin >> k; + node *kth_node = kth_node_from_end(head, k); + cout << endl; + cout << kth_node->data << endl; + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_linear_search.cpp b/Linked List/linked_list_linear_search.cpp similarity index 95% rename from Linked_List/linked_list_linear_search.cpp rename to Linked List/linked_list_linear_search.cpp index 91997de8..e9f2eff8 100644 --- a/Linked_List/linked_list_linear_search.cpp +++ b/Linked List/linked_list_linear_search.cpp @@ -1,55 +1,55 @@ -// Search linearly in a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -bool linear_search(node *head, int key){ - if(head == NULL){ - return false; - } - else{ - node *temp = head; - while(temp != NULL){ - if(temp->data == key){ - return true; - } - temp = temp->next; - } - } - return false; -} -void insert_at_head(node *&head, int data){ - node *n = new node(data); - n->next = head; - head = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_head(head, 1); - insert_at_head(head, 2); - insert_at_head(head, 3); - print_linked_list(head); - cout << endl; - if(linear_search(head, 1)){ - cout << "Found"; - } - else{ - cout << "Not Found"; - } - return 0; +// Search linearly in a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +bool linear_search(node *head, int key){ + if(head == NULL){ + return false; + } + else{ + node *temp = head; + while(temp != NULL){ + if(temp->data == key){ + return true; + } + temp = temp->next; + } + } + return false; +} +void insert_at_head(node *&head, int data){ + node *n = new node(data); + n->next = head; + head = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_head(head, 1); + insert_at_head(head, 2); + insert_at_head(head, 3); + print_linked_list(head); + cout << endl; + if(linear_search(head, 1)){ + cout << "Found"; + } + else{ + cout << "Not Found"; + } + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_merge_k_sorted_lists.cpp b/Linked List/linked_list_merge_k_sorted_lists.cpp similarity index 95% rename from Linked_List/linked_list_merge_k_sorted_lists.cpp rename to Linked List/linked_list_merge_k_sorted_lists.cpp index d8a30f91..47519766 100644 --- a/Linked_List/linked_list_merge_k_sorted_lists.cpp +++ b/Linked List/linked_list_merge_k_sorted_lists.cpp @@ -1,99 +1,99 @@ -// Merge K sorted LinkedLists -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; - -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -istream& operator>>(istream &is, node *&head){ - makeLinkedList(head); - return is; -} -ostream& operator<<(ostream &os, node *&head){ - print_linked_list(head); - return os; -} -node* merge_two_lists(node *a, node *b){ - if(a == NULL){ - return b; - } - else if(b == NULL){ - return a; - } - node *c = NULL; - if(a->data < b->data){ - c = a; - c->next = merge_two_lists(a->next, b); - } - else{ - c = b; - c->next = merge_two_lists(a, b->next); - } - return c; -} -node* merge_k_lists(node *arr[], int last){ - while(last != 0){ - int i = 0, j = last; - while(i < j){ - arr[i] = merge_two_lists(arr[i], arr[j]); - i++; - j--; - if(i >= j) - last = j; - } - } - return arr[0]; -} -int main(){ - - int n; - cin >> n; - node *arr[n]; - for(int i = 0; i < n; i++){ - arr[i] = NULL; - } - for(int i = 0; i < n; i++){ - cin >> arr[i]; - } - cout << endl; - for(int i = 0; i < n; i++){ - cout << arr[i] << endl;; - } - node *head = merge_k_lists(arr, n-1); - cout << head; - return 0; +// Merge K sorted LinkedLists +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; + +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +istream& operator>>(istream &is, node *&head){ + makeLinkedList(head); + return is; +} +ostream& operator<<(ostream &os, node *&head){ + print_linked_list(head); + return os; +} +node* merge_two_lists(node *a, node *b){ + if(a == NULL){ + return b; + } + else if(b == NULL){ + return a; + } + node *c = NULL; + if(a->data < b->data){ + c = a; + c->next = merge_two_lists(a->next, b); + } + else{ + c = b; + c->next = merge_two_lists(a, b->next); + } + return c; +} +node* merge_k_lists(node *arr[], int last){ + while(last != 0){ + int i = 0, j = last; + while(i < j){ + arr[i] = merge_two_lists(arr[i], arr[j]); + i++; + j--; + if(i >= j) + last = j; + } + } + return arr[0]; +} +int main(){ + + int n; + cin >> n; + node *arr[n]; + for(int i = 0; i < n; i++){ + arr[i] = NULL; + } + for(int i = 0; i < n; i++){ + cin >> arr[i]; + } + cout << endl; + for(int i = 0; i < n; i++){ + cout << arr[i] << endl;; + } + node *head = merge_k_lists(arr, n-1); + cout << head; + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_merge_two_sorted_linked_list.cpp b/Linked List/linked_list_merge_two_sorted_linked_list.cpp similarity index 95% rename from Linked_List/linked_list_merge_two_sorted_linked_list.cpp rename to Linked List/linked_list_merge_two_sorted_linked_list.cpp index aa2219f0..275ba9d7 100644 --- a/Linked_List/linked_list_merge_two_sorted_linked_list.cpp +++ b/Linked List/linked_list_merge_two_sorted_linked_list.cpp @@ -1,77 +1,77 @@ -// Merge two sorted LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; - -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -istream& operator>>(istream &is, node *&head){ - makeLinkedList(head); - return is; -} -ostream& operator<<(ostream &os, node *&head){ - print_linked_list(head); - return os; -} -node* merge_two_lists(node *a, node *b){ - if(a == NULL){ - return b; - } - else if(b == NULL){ - return a; - } - node *c = NULL; - if(a->data < b->data){ - c = a; - c->next = merge_two_lists(a->next, b); - } - else{ - c = b; - c->next = merge_two_lists(a, b->next); - } - return c; -} -int main(){ - node *head = NULL; - node *head2 = NULL; - cin >> head >> head2; - cout << head << endl << head2; - cout << endl; - node *x = merge_two_lists(head, head2); - cout << x << endl; - return 0; +// Merge two sorted LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; + +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +istream& operator>>(istream &is, node *&head){ + makeLinkedList(head); + return is; +} +ostream& operator<<(ostream &os, node *&head){ + print_linked_list(head); + return os; +} +node* merge_two_lists(node *a, node *b){ + if(a == NULL){ + return b; + } + else if(b == NULL){ + return a; + } + node *c = NULL; + if(a->data < b->data){ + c = a; + c->next = merge_two_lists(a->next, b); + } + else{ + c = b; + c->next = merge_two_lists(a, b->next); + } + return c; +} +int main(){ + node *head = NULL; + node *head2 = NULL; + cin >> head >> head2; + cout << head << endl << head2; + cout << endl; + node *x = merge_two_lists(head, head2); + cout << x << endl; + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_mergesort_an_unsorted_list.cpp b/Linked List/linked_list_mergesort_an_unsorted_list.cpp similarity index 95% rename from Linked_List/linked_list_mergesort_an_unsorted_list.cpp rename to Linked List/linked_list_mergesort_an_unsorted_list.cpp index ff9217b5..d1ccd804 100644 --- a/Linked_List/linked_list_mergesort_an_unsorted_list.cpp +++ b/Linked List/linked_list_mergesort_an_unsorted_list.cpp @@ -1,100 +1,100 @@ -// MergeSort an unsorted LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -node* compute_midpoint(node *head){ - if(head->next == NULL || head == NULL){ - return head; - } - node *slow = head; - node *fast = head->next; - while(fast != NULL && fast->next != NULL){ - fast = fast->next->next; - slow = slow->next; - } - return slow; -} -istream& operator>>(istream &is, node *&head){ - makeLinkedList(head); - return is; -} -ostream& operator<<(ostream &os, node *&head){ - print_linked_list(head); - return os; -} -node* merge_two_lists(node *a, node *b){ - if(a == NULL){ - return b; - } - else if(b == NULL){ - return a; - } - node *c = NULL; - if(a->data < b->data){ - c = a; - c->next = merge_two_lists(a->next, b); - } - else{ - c = b; - c->next = merge_two_lists(a, b->next); - } - return c; -} -node* mergeSort(node* head){ - if(head == NULL || head->next == NULL){ - return head; - } - node *mid = compute_midpoint(head); - node *a = head; - node *b = mid->next; - mid->next = NULL; - a = mergeSort(a); - b = mergeSort(b); - node *c = merge_two_lists(a, b); - return c; -} -int main(){ - node *head = NULL; - node *head2 = NULL; - cin >> head; - cout << head << endl; - node *x = mergeSort(head); - cout << x << endl; - return 0; +// MergeSort an unsorted LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +node* compute_midpoint(node *head){ + if(head->next == NULL || head == NULL){ + return head; + } + node *slow = head; + node *fast = head->next; + while(fast != NULL && fast->next != NULL){ + fast = fast->next->next; + slow = slow->next; + } + return slow; +} +istream& operator>>(istream &is, node *&head){ + makeLinkedList(head); + return is; +} +ostream& operator<<(ostream &os, node *&head){ + print_linked_list(head); + return os; +} +node* merge_two_lists(node *a, node *b){ + if(a == NULL){ + return b; + } + else if(b == NULL){ + return a; + } + node *c = NULL; + if(a->data < b->data){ + c = a; + c->next = merge_two_lists(a->next, b); + } + else{ + c = b; + c->next = merge_two_lists(a, b->next); + } + return c; +} +node* mergeSort(node* head){ + if(head == NULL || head->next == NULL){ + return head; + } + node *mid = compute_midpoint(head); + node *a = head; + node *b = mid->next; + mid->next = NULL; + a = mergeSort(a); + b = mergeSort(b); + node *c = merge_two_lists(a, b); + return c; +} +int main(){ + node *head = NULL; + node *head2 = NULL; + cin >> head; + cout << head << endl; + node *x = mergeSort(head); + cout << x << endl; + return 0; } \ No newline at end of file diff --git a/Linked List/linked_list_middle.go b/Linked List/linked_list_middle.go new file mode 100644 index 00000000..8542271b --- /dev/null +++ b/Linked List/linked_list_middle.go @@ -0,0 +1,39 @@ +/** + * Given the head of a singly linked list, return the middle node of the linked list. If there are two middle nodes, return the second middle node. + * + * Example #1: + * Input: head = [1,2,3,4,5] + * Output: [3,4,5] + * Explanation: The middle node of the list is node 3. + * + * Example #2: + * Input: head = [1,2,3,4,5,6] + * Output: [4,5,6] + * Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one. + * + * Constraints: + * The number of nodes in the list is in the range [1, 100]. + * 1 <= Node.val <= 100 + * + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + * + * Solution: to find the middle of a linked list, we can use two pointers ("slow" and "fast"). They should both start at head. + * While traversing the list, we move the slow pointer one node at a time and the fast pointer two nodes at a time. + * When the fast pointer reaches the end of the linked list, the slow pointer will be pointing to the middle node. + * + * Time complexity: O(n), space complexity: O(n) + */ + func middleNode(head *ListNode) *ListNode { + slow, fast := head, head + + for (fast != nil && fast.Next != nil) { + slow = slow.Next + fast = fast.Next.Next + } + + return slow +} diff --git a/Linked List/linked_list_middle.js b/Linked List/linked_list_middle.js new file mode 100644 index 00000000..3b06c422 --- /dev/null +++ b/Linked List/linked_list_middle.js @@ -0,0 +1,57 @@ +/* + Given the head of a singly linked list, return the middle node of the linked list. + + If there are two middle nodes, return the second middle node. + + Example 1: + + Input: head = [1,2,3,4,5] + Output: [3,4,5] + Explanation: The middle node of the list is node 3. + + Example 2: + + Input: head = [1,2,3,4,5,6] + Output: [4,5,6] + Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one. + + Constraints: + + The number of nodes in the list is in the range [1, 100]. + 1 <= Node.val <= 100 +*/ + +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +var middleNode = function(head) { + let curr = head // Initialize the current node to the head + let size = 0 // Initialize the size to 0 + + // Traverse the Linked List + while (curr) { + size += 1 // Keep track of the size of the Linked List + curr = curr.next + } // curr is equal to null at the end of the loop + + let mid = Math.floor(size / 2) + 1 // Calculate the middle of the List + let count = 0 + curr = head // Reset current to head + + // Traverse the Linked List + while (curr) { + count += 1 // Keep track of the number of visited nodes in the List + + if (count === mid) return curr // When middle node found, return it + + curr = curr.next + } +}; \ No newline at end of file diff --git a/Linked_List/linked_list_odd_even.cpp b/Linked List/linked_list_odd_even.cpp similarity index 95% rename from Linked_List/linked_list_odd_even.cpp rename to Linked List/linked_list_odd_even.cpp index dccf988e..eca1423d 100644 --- a/Linked_List/linked_list_odd_even.cpp +++ b/Linked List/linked_list_odd_even.cpp @@ -1,64 +1,64 @@ -// OddEven LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - cout << endl; - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -node* oddEvenLinkedList(node *head){ - if(head == NULL || head->next == NULL){ - return head; - } - node *odd = head; - node *even = head->next; - node *evenhead = even; - while(even != NULL && even->next != NULL){ - odd->next = even->next; - odd = odd->next; - even->next = odd->next; - even = even->next; - } - odd->next = evenhead; - return head; -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - oddEvenLinkedList(head); - print_linked_list(head); - return 0; +// OddEven LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + cout << endl; + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +node* oddEvenLinkedList(node *head){ + if(head == NULL || head->next == NULL){ + return head; + } + node *odd = head; + node *even = head->next; + node *evenhead = even; + while(even != NULL && even->next != NULL){ + odd->next = even->next; + odd = odd->next; + even->next = odd->next; + even = even->next; + } + odd->next = evenhead; + return head; +} +int main(){ + node *head = NULL; + makeLinkedList(head); + print_linked_list(head); + oddEvenLinkedList(head); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked List/linked_list_pallindrome.cpp b/Linked List/linked_list_pallindrome.cpp new file mode 100644 index 00000000..3f6b9611 --- /dev/null +++ b/Linked List/linked_list_pallindrome.cpp @@ -0,0 +1,62 @@ +/* + Given the head of a singly linked list, return true if it is a + palindrome + or false otherwise. + + + + Example 1: + + + Input: head = [1,2,2,1] + Output: true + Example 2: + + + Input: head = [1,2] + Output: false + + + Constraints: + + The number of nodes in the list is in the range [1, 105]. + 0 <= Node.val <= 9 + + + Follow up: Could you do it in O(n) time and O(1) space? +*/ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +// Time Complexity : O(n) Space Complexity: O(n) +// TODO: Implement O(n) Time and O(1) space solution +class Solution { +public: + bool isPalindrome(ListNode* head) { + vector contents; + while(head != NULL) { + int val = head->val; + contents.push_back(val); + head = head->next; + } + int low = 0, high = contents.size() - 1; + while(low <= high) { + if(contents[low] == contents[high]){ + low++; + high--; + } + else { + return false; + } + } + return true; + } +}; \ No newline at end of file diff --git a/Linked_List/linked_list_recursive_search.cpp b/Linked List/linked_list_recursive_search.cpp similarity index 95% rename from Linked_List/linked_list_recursive_search.cpp rename to Linked List/linked_list_recursive_search.cpp index ebf8cc9d..39c1cce8 100644 --- a/Linked_List/linked_list_recursive_search.cpp +++ b/Linked List/linked_list_recursive_search.cpp @@ -1,51 +1,51 @@ -// Search recursively in a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -bool recursive_search(node *head, int key){ - if(head == NULL){ - return false; - } - if(head->data == key){ - return true; - } - else{ - recursive_search(head->next, key); - } -} -void insert_at_head(node *&head, int data){ - node *n = new node(data); - n->next = head; - head = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -int main(){ - node *head = NULL; - insert_at_head(head, 1); - insert_at_head(head, 2); - insert_at_head(head, 3); - print_linked_list(head); - cout << endl; - if(recursive_search(head, 3)){ - cout << "Found"; - } - else{ - cout << "Not Found"; - } - return 0; +// Search recursively in a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +bool recursive_search(node *head, int key){ + if(head == NULL){ + return false; + } + if(head->data == key){ + return true; + } + else{ + recursive_search(head->next, key); + } +} +void insert_at_head(node *&head, int data){ + node *n = new node(data); + n->next = head; + head = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +int main(){ + node *head = NULL; + insert_at_head(head, 1); + insert_at_head(head, 2); + insert_at_head(head, 3); + print_linked_list(head); + cout << endl; + if(recursive_search(head, 3)){ + cout << "Found"; + } + else{ + cout << "Not Found"; + } + return 0; } \ No newline at end of file diff --git a/CCTI/linked_list_remove_dups.cpp b/Linked List/linked_list_remove_dups.cpp similarity index 95% rename from CCTI/linked_list_remove_dups.cpp rename to Linked List/linked_list_remove_dups.cpp index ab0ae0ec..a00d3343 100644 --- a/CCTI/linked_list_remove_dups.cpp +++ b/Linked List/linked_list_remove_dups.cpp @@ -1,91 +1,91 @@ -// Remove duplicates from an unsorted linked list -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class Node{ - public: - int data; - Node *next; - - Node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(Node *&head, int data){ - if(head == NULL){ - head = new Node(data); - return; - } - Node *n = new Node(data); - Node *temp = head; - while(temp->next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(Node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void make_linked_list(Node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -void remove_duplicates(Node *&head){ - set S; - Node *temp = head; - Node *prev = NULL; - Node *remove = NULL; - while(temp != NULL){ - if(S.find(temp->data) != S.end()){ - remove = temp; - prev->next = temp->next; - } - else{ - S.insert(temp->data); - prev = temp; - } - temp = temp->next; - delete remove; - } -} -void remove_duplicates_without_buffer(Node *&head){ - Node *current = head; - Node *remove = NULL; - while(current != NULL){ - Node *runner = current; - while(runner->next != NULL){ - if(runner->next->data == current->data){ - remove = runner->next; - runner->next = runner->next->next; - delete remove; - } - else{ - runner = runner->next; - } - } - current = current->next; - } -} -int main(){ - Node *head = NULL; - /* - make_linked_list(head); - print_linked_list(head); - cout << endl; - remove_duplicates(head); - print_linked_list(head); - */ - make_linked_list(head); - print_linked_list(head); - cout << endl; - remove_duplicates_without_buffer(head); - print_linked_list(head); +// Remove duplicates from an unsorted linked list +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class Node{ + public: + int data; + Node *next; + + Node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(Node *&head, int data){ + if(head == NULL){ + head = new Node(data); + return; + } + Node *n = new Node(data); + Node *temp = head; + while(temp->next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(Node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void make_linked_list(Node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +void remove_duplicates(Node *&head){ + set S; + Node *temp = head; + Node *prev = NULL; + Node *remove = NULL; + while(temp != NULL){ + if(S.find(temp->data) != S.end()){ + remove = temp; + prev->next = temp->next; + } + else{ + S.insert(temp->data); + prev = temp; + } + temp = temp->next; + delete remove; + } +} +void remove_duplicates_without_buffer(Node *&head){ + Node *current = head; + Node *remove = NULL; + while(current != NULL){ + Node *runner = current; + while(runner->next != NULL){ + if(runner->next->data == current->data){ + remove = runner->next; + runner->next = runner->next->next; + delete remove; + } + else{ + runner = runner->next; + } + } + current = current->next; + } +} +int main(){ + Node *head = NULL; + /* + make_linked_list(head); + print_linked_list(head); + cout << endl; + remove_duplicates(head); + print_linked_list(head); + */ + make_linked_list(head); + print_linked_list(head); + cout << endl; + remove_duplicates_without_buffer(head); + print_linked_list(head); } \ No newline at end of file diff --git a/Linked List/linked_list_remove_kth_node_from_end.py b/Linked List/linked_list_remove_kth_node_from_end.py new file mode 100644 index 00000000..03790c47 --- /dev/null +++ b/Linked List/linked_list_remove_kth_node_from_end.py @@ -0,0 +1,93 @@ +# PROBLEM TO SOLVE +""" + Remove the Nth node from the end of the linked list + assume input is always valid and that k will be a non-negative number + that is less than the length of the list +""" + +# define node used in linked list +class node: + def __init__(self,v=0,n=None): + self.val = v + self.next = n + +# function to create list of length n, node values start at 1 and incrament +# this is NOT part of the algorithm, just part of the setup +def create_list(n): + if n <= 0: + raise Exception() + head = node(1) + curr = head + for i in range(2,n+1): + next = node(i) + curr.next = next + curr=curr.next + return head +def print_list(head): + while head: + print(head.val) + head = head.next + +# helper function (used in both recursive and iterative) +def __remove_node(prev,curr): + prev.next = curr.next +# recursive function +def recursive(head, n): + # create nested helper function called rec + # this allows us to keep the function signature of recursive clean and consistant with iterative + # in real life the client does not usually care if your function is iterative or recursive + # so you want both functions to have just two arguments when the client calls them + def rec(lag,lead, k): + # move lead up k nodes before we even start moving lag + if k > 0: + k -= 1 + lead = lead.next + else: + if not lead: # base case 1: remove head if lead goes passed last node + return head.next + elif not lead.next: # base case 2: skip kth node if lead is on last node + lag.next = lag.next.next + return head + else: # move both nodes up until lead is at the last node + lead = lead.next + lag = lag.next + return rec(lag,lead,k) # call the recursive case + # if we did not create a helper function, the client would need to call recursive + # with 3 arguments instead of 2 which is confusing. + return rec(head,head,n) + + +# iterative function +def iterative(head,k): + # start lag and lead at head + lag = lead = head + # lead gets a headstart, it moves up k nodes before lag starts moving + for _ in range(k): + lead = lead.next + # if lead made it passed the end, then lag must still be at head + if not lead: + return lag.next + # we want to keep moving both lead and lag by one until lead is at the last node + # keep in mind that we want to stop lag one bofore the node we want to delete. + # this is because need to skip over the kth node from the end + while lead.next: + lead = lead.next + lag = lag.next + # we skip the kth from end node by assiging lag.next to lag.next.next + # pythons built in garbage collection will recognize that the kth node + # is unreachable and will delete it for us, no need to call del! + lag.next = lag.next.next + return head + +# text functions below main +if __name__ == "__main__": + head = create_list(9) + print("before removal") + print_list(head) + print("after removing 2nd from last node iteratively") + rem_head = iterative(head,2) + print_list(rem_head) + head = create_list(9) + print("after removing 3rd from last recursivly") + rem_head = recursive(head,3) + print_list(rem_head) diff --git a/Linked List/linked_list_remove_nth_node_from_end.cpp b/Linked List/linked_list_remove_nth_node_from_end.cpp new file mode 100644 index 00000000..8e2087fb --- /dev/null +++ b/Linked List/linked_list_remove_nth_node_from_end.cpp @@ -0,0 +1,67 @@ +/* +Given the head of a linked list, remove the nth node from the end of the list and return its head. + +Input: head = [1,2,3,4,5], n = 2 +Output: [1,2,3,5] +Example 2: + +Input: head = [1], n = 1 +Output: [] +Example 3: + +Input: head = [1,2], n = 1 +Output: [1] + + +Constraints: + +The number of nodes in the list is sz. +1 <= sz <= 30 +0 <= Node.val <= 100 +1 <= n <= sz + +*/ +#include +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + + +/* +In order to solve this problem in only one pass and O(1) extra space, +however, we would need to find a way to both reach the end of the list with one pointer +and also reach the n'th node from the end simultaneously with a second pointer. +To do that, we can simply stagger our two pointers by n nodes by giving the first pointer +(fast) a head start before starting the second pointer (slow). Doing this will cause +slow to reach the n'th node from the end at the same time that fast reaches the end. +*/ + +class Solution { +public: + ListNode* removeNthFromEnd(ListNode* head, int n) { + ListNode* slow = head; + ListNode* fast = head; + if(head == NULL || head->next == NULL) + return NULL; + while(n--){ + fast = fast->next; + } + if(fast == NULL) + return slow->next; + while(fast->next != NULL){ + slow = slow->next; + fast = fast->next; + } + ListNode* temp = slow->next; + slow->next = slow->next->next; + delete temp; + return head; + } +}; \ No newline at end of file diff --git a/Linked List/linked_list_remove_nth_node_from_end.py b/Linked List/linked_list_remove_nth_node_from_end.py new file mode 100644 index 00000000..d814e50a --- /dev/null +++ b/Linked List/linked_list_remove_nth_node_from_end.py @@ -0,0 +1,16 @@ +class Solution: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + dummy_header = ListNode('*') + dummy_header.next=head + slow = dummy_header + fast = dummy_header + + for _ in range(1,n+2): + fast=fast.next + + while(fast!=None): + slow = slow.next + fast = fast.next + + slow.next = slow.next.next + return dummy_header.next diff --git a/Linked_List/linked_list_reverse.cpp b/Linked List/linked_list_reverse.cpp similarity index 95% rename from Linked_List/linked_list_reverse.cpp rename to Linked List/linked_list_reverse.cpp index 25305aa0..ee05d3fe 100644 --- a/Linked_List/linked_list_reverse.cpp +++ b/Linked List/linked_list_reverse.cpp @@ -1,61 +1,61 @@ -// Reverse a linkedlist iteratively -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -void reverse_linked_list(node *&head){ - node *current = head; - node *next = NULL; - node *prev = NULL; - while(current != NULL){ - next = current->next; - current->next = prev; - prev = current; - current = next; - } - head = prev; -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - cout << endl; - reverse_linked_list(head); - print_linked_list(head); - return 0; +// Reverse a linkedlist iteratively +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +void reverse_linked_list(node *&head){ + node *current = head; + node *next = NULL; + node *prev = NULL; + while(current != NULL){ + next = current->next; + current->next = prev; + prev = current; + current = next; + } + head = prev; +} +int main(){ + node *head = NULL; + makeLinkedList(head); + print_linked_list(head); + cout << endl; + reverse_linked_list(head); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked List/linked_list_reverse.js b/Linked List/linked_list_reverse.js new file mode 100644 index 00000000..ccec12d0 --- /dev/null +++ b/Linked List/linked_list_reverse.js @@ -0,0 +1,47 @@ +//Problem statement-Given the head of a singly linked list, reverse the list, and return the reversed list. + +// Iteratively +* @param {ListNode} head + * @return {ListNode} + */ +var reverseList = function(head) { + // Handling the edge cases if head node is null or when single node is there + if (head === null || head.next === null) { + return head; + } + // Using three pointer approach + let prev = null; + let curr = head; + let fwd = null; + while (curr !== null) { + // Updatng the value of each of the pointers + fwd = curr.next; + curr.next = prev; + prev = curr; + curr = fwd; + } + // Returning the head of the reversed linked list + return prev; +}; +// Time complexity - O(N) +// Space complexity - O(1) + +// Recursively +var reverseList = function(head) { + // Base case- If the head is null or single node is there + if (head === null || head.next === null) { + return head; + } + + // Recursively reverse the remaining list starting from the next node + const reversedList = reverseList(head.next); + + // Reversing the links between the current and next node + head.next.next = head; + head.next = null; + + // Returning the head of the reversed linked list + return reversedList; +}; +// Time complexity - O(N) +// Space complexity - O(N) diff --git a/Linked_List/linked_list_reverse_recursive.cpp b/Linked List/linked_list_reverse_recursive.cpp similarity index 95% rename from Linked_List/linked_list_reverse_recursive.cpp rename to Linked List/linked_list_reverse_recursive.cpp index 5ff12cb3..8375bfa6 100644 --- a/Linked_List/linked_list_reverse_recursive.cpp +++ b/Linked List/linked_list_reverse_recursive.cpp @@ -1,60 +1,60 @@ -// Reverse a linkedlist recursively -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} - -node* recursive_reverse(node *head){ - if(head->next == NULL || head == NULL){ - return head; - } - node *mini_head = recursive_reverse(head->next); - node *current = head; - current->next->next = current; - current->next = NULL; - return mini_head; -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - cout << endl; - head = recursive_reverse(head); - print_linked_list(head); - return 0; +// Reverse a linkedlist recursively +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} + +node* recursive_reverse(node *head){ + if(head->next == NULL || head == NULL){ + return head; + } + node *mini_head = recursive_reverse(head->next); + node *current = head; + current->next->next = current; + current->next = NULL; + return mini_head; +} +int main(){ + node *head = NULL; + makeLinkedList(head); + print_linked_list(head); + cout << endl; + head = recursive_reverse(head); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Leetcode/linked_list_sum_lists.cpp b/Linked List/linked_list_sum_lists.cpp similarity index 96% rename from Leetcode/linked_list_sum_lists.cpp rename to Linked List/linked_list_sum_lists.cpp index e3aeb672..824c4b7f 100644 --- a/Leetcode/linked_list_sum_lists.cpp +++ b/Linked List/linked_list_sum_lists.cpp @@ -1,68 +1,68 @@ -// sum Lists : you have two numbers represented by a linked list -// where each node contains a single digit, write a function -// to add two numbers and returns the sum as linked list -// Program Author: Abhisek Kumar Gupta -#include -using namespace std; -class Node{ - public: - int data; - Node *next; - - Node(int d){ - data = d; - } -}; - -void insert_at_head(Node *&head, int data){ - Node *new_node = new Node(data); - new_node->next = head; - head = new_node; -} -void print_linked_list(Node *head){ - if(head == NULL) - return; - cout << head->data << "->"; - print_linked_list(head->next); -} -void make_linked_list(Node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_head(head, data); - cin >> data; - } -} -Node* add_lists(Node *l1, Node *l2, int carry){ - - if(l1 == NULL && l2 == NULL && carry == 0) - return NULL; - int value = carry; - - if(l1 != NULL) - value += l1->data; - - if(l2 != NULL) - value += l2->data; - - Node *result = new Node(value % 10); - - if(l1 != NULL || l2 != NULL) - result->next = add_lists(l1 == NULL ? NULL : l1->next, l2 == NULL ? NULL : l2->next, value >= 10 ? 1 : 0); - else - result->next = NULL; - - return result; -} -int main(){ - Node *head1 = NULL; - Node *head2 = NULL; - make_linked_list(head1); - make_linked_list(head2); - print_linked_list(head1); - cout << endl; - print_linked_list(head2); - Node *result = add_lists(head1, head2, 0); - cout << endl; - print_linked_list(result); +// sum Lists : you have two numbers represented by a linked list +// where each node contains a single digit, write a function +// to add two numbers and returns the sum as linked list +// Program Author: Abhisek Kumar Gupta +#include +using namespace std; +class Node{ + public: + int data; + Node *next; + + Node(int d){ + data = d; + } +}; + +void insert_at_head(Node *&head, int data){ + Node *new_node = new Node(data); + new_node->next = head; + head = new_node; +} +void print_linked_list(Node *head){ + if(head == NULL) + return; + cout << head->data << "->"; + print_linked_list(head->next); +} +void make_linked_list(Node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_head(head, data); + cin >> data; + } +} +Node* add_lists(Node *l1, Node *l2, int carry){ + + if(l1 == NULL && l2 == NULL && carry == 0) + return NULL; + int value = carry; + + if(l1 != NULL) + value += l1->data; + + if(l2 != NULL) + value += l2->data; + + Node *result = new Node(value % 10); + + if(l1 != NULL || l2 != NULL) + result->next = add_lists(l1 == NULL ? NULL : l1->next, l2 == NULL ? NULL : l2->next, value >= 10 ? 1 : 0); + else + result->next = NULL; + + return result; +} +int main(){ + Node *head1 = NULL; + Node *head2 = NULL; + make_linked_list(head1); + make_linked_list(head2); + print_linked_list(head1); + cout << endl; + print_linked_list(head2); + Node *result = add_lists(head1, head2, 0); + cout << endl; + print_linked_list(result); } \ No newline at end of file diff --git a/Linked_List/linked_list_swap_nodes_in_pair.cpp b/Linked List/linked_list_swap_nodes_in_pair.cpp similarity index 95% rename from Linked_List/linked_list_swap_nodes_in_pair.cpp rename to Linked List/linked_list_swap_nodes_in_pair.cpp index 39cbc3e5..669175cc 100644 --- a/Linked_List/linked_list_swap_nodes_in_pair.cpp +++ b/Linked List/linked_list_swap_nodes_in_pair.cpp @@ -1,58 +1,58 @@ -// Swap adjacant Nodes in pair Recursive -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - cout << endl; - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -node* swapNodesInPairRecursive(node *head){ - if(head == NULL || head->next == NULL){ - return head; - } - node *newHead = swapNodesInPairRecursive(head->next->next); - node *temp = head->next; - temp->next = head; - head->next = newHead; - return temp; -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - head = swapNodesInPairRecursive(head); - print_linked_list(head); - return 0; +// Swap adjacant Nodes in pair Recursive +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + cout << endl; + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +node* swapNodesInPairRecursive(node *head){ + if(head == NULL || head->next == NULL){ + return head; + } + node *newHead = swapNodesInPairRecursive(head->next->next); + node *temp = head->next; + temp->next = head; + head->next = newHead; + return temp; +} +int main(){ + node *head = NULL; + makeLinkedList(head); + print_linked_list(head); + head = swapNodesInPairRecursive(head); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_swap_nodes_in_pair_iterative.cpp b/Linked List/linked_list_swap_nodes_in_pair_iterative.cpp similarity index 95% rename from Linked_List/linked_list_swap_nodes_in_pair_iterative.cpp rename to Linked List/linked_list_swap_nodes_in_pair_iterative.cpp index 88d1efa4..c9e3b332 100644 --- a/Linked_List/linked_list_swap_nodes_in_pair_iterative.cpp +++ b/Linked List/linked_list_swap_nodes_in_pair_iterative.cpp @@ -1,69 +1,69 @@ -// Swap adjacant Nodes in Pair Iterative -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - cout << endl; - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} - -node* swapNodesInPairIterative(node *head){ - node *dummy = new node(0); - node *prev = dummy; - node *curr = head; - node *second = NULL; - node *nextPair = NULL; - while(curr && curr->next){ - nextPair = curr->next->next; - second = curr->next; - - second->next = curr; - curr->next = nextPair; - prev->next = second; - - prev = curr; - curr = nextPair; - } - return dummy->next; - -} -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - head = swapNodesInPairIterative(head); - print_linked_list(head); - return 0; +// Swap adjacant Nodes in Pair Iterative +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + cout << endl; + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} + +node* swapNodesInPairIterative(node *head){ + node *dummy = new node(0); + node *prev = dummy; + node *curr = head; + node *second = NULL; + node *nextPair = NULL; + while(curr && curr->next){ + nextPair = curr->next->next; + second = curr->next; + + second->next = curr; + curr->next = nextPair; + prev->next = second; + + prev = curr; + curr = nextPair; + } + return dummy->next; + +} +int main(){ + node *head = NULL; + makeLinkedList(head); + print_linked_list(head); + head = swapNodesInPairIterative(head); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_take_input.cpp b/Linked List/linked_list_take_input.cpp similarity index 95% rename from Linked_List/linked_list_take_input.cpp rename to Linked List/linked_list_take_input.cpp index e8c85807..ef47f964 100644 --- a/Linked_List/linked_list_take_input.cpp +++ b/Linked List/linked_list_take_input.cpp @@ -1,47 +1,47 @@ -// Finding Midpoint of a LinkedList -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - cout << endl; - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} - -int main(){ - node *head = NULL; - makeLinkedList(head); - print_linked_list(head); - return 0; +// Finding Midpoint of a LinkedList +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + node(int d){ + data = d; + next = NULL; + } +}; +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + cout << endl; + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} + +int main(){ + node *head = NULL; + makeLinkedList(head); + print_linked_list(head); + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_take_input_as_array_operator_overloading.cpp b/Linked List/linked_list_take_input_as_array_operator_overloading.cpp similarity index 95% rename from Linked_List/linked_list_take_input_as_array_operator_overloading.cpp rename to Linked List/linked_list_take_input_as_array_operator_overloading.cpp index ef989709..6caf8eab 100644 --- a/Linked_List/linked_list_take_input_as_array_operator_overloading.cpp +++ b/Linked List/linked_list_take_input_as_array_operator_overloading.cpp @@ -1,66 +1,66 @@ -// Take Input in a LinkedList as an array with "Operator overloading" -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; - -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -istream& operator>>(istream &is, node *&head){ - makeLinkedList(head); - return is; -} -ostream& operator<<(ostream &os, node *&head){ - print_linked_list(head); - return os; -} -int main(){ - - int n; - cin >> n; - node *arr[n]; - for(int i = 0; i < n; i++){ - arr[i] = NULL; - } - for(int i = 0; i < n; i++){ - cin >> arr[i]; - } - cout << endl; - for(int i = 0; i < n; i++){ - cout << arr[i] << endl;; - } - return 0; +// Take Input in a LinkedList as an array with "Operator overloading" +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; + +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +istream& operator>>(istream &is, node *&head){ + makeLinkedList(head); + return is; +} +ostream& operator<<(ostream &os, node *&head){ + print_linked_list(head); + return os; +} +int main(){ + + int n; + cin >> n; + node *arr[n]; + for(int i = 0; i < n; i++){ + arr[i] = NULL; + } + for(int i = 0; i < n; i++){ + cin >> arr[i]; + } + cout << endl; + for(int i = 0; i < n; i++){ + cout << arr[i] << endl;; + } + return 0; } \ No newline at end of file diff --git a/Linked_List/linked_list_take_input_operator_overloading.cpp b/Linked List/linked_list_take_input_operator_overloading.cpp similarity index 95% rename from Linked_List/linked_list_take_input_operator_overloading.cpp rename to Linked List/linked_list_take_input_operator_overloading.cpp index 1bece2f1..3115b970 100644 --- a/Linked_List/linked_list_take_input_operator_overloading.cpp +++ b/Linked List/linked_list_take_input_operator_overloading.cpp @@ -1,70 +1,70 @@ -// Take Input in a LinkedList with "Operator overloading" -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class node{ - public: - int data; - node* next; - - node(int d){ - data = d; - next = NULL; - } -}; - -void insert_at_tail(node *&head, int data){ - if(head == NULL){ - head = new node(data); - return; - } - node *n = new node(data); - node * temp = head; - while(temp -> next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void makeLinkedList(node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -istream& operator>>(istream &is, node *&head){ - makeLinkedList(head); - return is; -} -ostream& operator<<(ostream &os, node *&head){ - print_linked_list(head); - return os; -} -int main(){ - /*node *head = NULL; - node *head2 = NULL; - cin >> head >> head2; - cout << head << endl << head2; - */ - int n; - cin >> n; - node *arr[n]; - for(int i = 0; i < n; i++){ - arr[i] = NULL; - } - for(int i = 0; i < n; i++){ - cin >> arr[i]; - } - cout << endl; - for(int i = 0; i < n; i++){ - cout << arr[i] << endl;; - } - return 0; +// Take Input in a LinkedList with "Operator overloading" +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +class node{ + public: + int data; + node* next; + + node(int d){ + data = d; + next = NULL; + } +}; + +void insert_at_tail(node *&head, int data){ + if(head == NULL){ + head = new node(data); + return; + } + node *n = new node(data); + node * temp = head; + while(temp -> next != NULL){ + temp = temp->next; + } + temp->next = n; +} +void print_linked_list(node *head){ + while(head != NULL){ + cout << head->data << "->"; + head = head->next; + } +} +void makeLinkedList(node *&head){ + int data; + cin >> data; + while(data != -1){ + insert_at_tail(head, data); + cin >> data; + } +} +istream& operator>>(istream &is, node *&head){ + makeLinkedList(head); + return is; +} +ostream& operator<<(ostream &os, node *&head){ + print_linked_list(head); + return os; +} +int main(){ + /*node *head = NULL; + node *head2 = NULL; + cin >> head >> head2; + cout << head << endl << head2; + */ + int n; + cin >> n; + node *arr[n]; + for(int i = 0; i < n; i++){ + arr[i] = NULL; + } + for(int i = 0; i < n; i++){ + cin >> arr[i]; + } + cout << endl; + for(int i = 0; i < n; i++){ + cout << arr[i] << endl;; + } + return 0; } \ No newline at end of file diff --git a/Linked List/recursive and iterative in singly linkedlist.py b/Linked List/recursive and iterative in singly linkedlist.py new file mode 100644 index 00000000..61f0db6f --- /dev/null +++ b/Linked List/recursive and iterative in singly linkedlist.py @@ -0,0 +1,116 @@ +''' +Issue#528 +Date:16/06/2023 +Input: +Linked List: Reverse Linked List Recursively and Iteratively in Python + +Example 1: +Enter the values for the linked list (space-separated): 1 2 3 4 5 +Choose the reversal method (iterative or recursive): iterative +5 4 3 2 1 + +Explanation: +*The `create_linked_list()` function: + - This function prompts the user to enter values for the linked list, which are expected to be space-separated. + - The input values are split into a list of strings using the `split()` method. + - The function then iterates over the input values, creating a new `ListNode` object for each value. + - The `next` pointers of the nodes are appropriately set to form the linked list. + - Finally, the function returns the head of the linked list. + +* The `reverse_linked_list_iterative()` function: + - This function takes the head of a linked list as input. + - It initializes two pointers: `prev` and `current`. `prev` is initially set to `None`, and `current` is set to the head. + - The function iterates over the linked list using a while loop. + - In each iteration, it stores the next node in a variable called `next_node`. + - Then, it updates the `next` pointer of the current node to point to the previous node (`prev`). + - The `prev` pointer is updated to the current node, and the `current` pointer is updated to the next node (`next_node`). + - This process continues until `current` becomes `None`, which means the end of the original linked list has been reached. + - Finally, the function returns the new head of the reversed linked list, which is stored in `prev`. + +* The `reverse_linked_list_recursive()` function: + - This function is a recursive implementation of reversing a linked list. + - It takes the head of a linked list as input. + - First, it checks two base cases: if the head is `None` or the head's `next` pointer is `None`. In these cases, it simply returns the head as it is. + - If the base cases are not met, it recursively calls itself on the next node (`head.next`) to reverse the remaining sublist. + - Once the recursion reaches the last node in the original linked list, it starts updating the `next` pointers to reverse the sublist. + - The `next` pointer of the current node (`head`) is set to the previous node (`head.next.next`), effectively reversing the connection. + - The `next` pointer of the current node is then set to `None` to complete the reversal. + - Finally, the function returns the new head of the reversed linked list. + +Time Complexity: +O(n)(both recursive and iterative method) +Space Complexity: +Iterative approach: O(1) space complexity. +Recursive approach: O(n) space complexity. +''' +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + +def create_linked_list(): + # Take user input for the linked list values + values = input("Enter the values for the linked list (space-separated): ").split() + + # Create the linked list from the input values + head = None + prev = None + + for val in values: + node = ListNode(int(val)) + + if not head: + head = node + else: + prev.next = node + + prev = node + + return head + +def reverse_linked_list_iterative(head): + prev = None + current = head + + while current: + next_node = current.next + current.next = prev + prev = current + current = next_node + + return prev + +def reverse_linked_list_recursive(head): + if not head or not head.next: + return head + + reversed_list = reverse_linked_list_recursive(head.next) + head.next.next = head + head.next = None + + return reversed_list + +def reverse_linked_list(head, method='iterative'): + if method == 'iterative': + return reverse_linked_list_iterative(head) + elif method == 'recursive': + return reverse_linked_list_recursive(head) + else: + raise ValueError('Invalid method specified.') + +# Create the linked list based on user input +head = create_linked_list() + +# Ask the user to choose the reversal method +method = input("Choose the reversal method (iterative or recursive): ") + +# Reverse the linked list using the chosen method +reversed_list = reverse_linked_list(head, method=method) + +# Traverse the reversed linked list and print the values +current = reversed_list +while current: + print(current.val, end=' ') + current = current.next + + diff --git a/Linked List/reverse_linked_list.go b/Linked List/reverse_linked_list.go new file mode 100644 index 00000000..881fbe0d --- /dev/null +++ b/Linked List/reverse_linked_list.go @@ -0,0 +1,77 @@ +// Implementation of reversing a linked list recursively and iteratively +/* + In this code, we define a ListNode struct which represents a node in the linked list. We then define two functions: + ReverseListIteratively and ReverseListRecursively which reverse the linked list iteratively and recursively, respectively. + In ReverseListIteratively, we declare two pointers prev and curr, initialize curr to head, and iterate over the + linked list until curr becomes nil. In each iteration, we store the Next pointer of curr in a temporary variable + nextTemp, point curr.Next to prev, update prev to curr, and update curr to nextTemp. Finally, we return prev, + which now points to the new head of the reversed linked list. + + In ReverseListRecursively, we first check if head is nil or if head.Next is nil, in which case we return head itself. + Otherwise, we call ReverseListRecursively on head.Next, which returns the new head of the reversed linked list. + We then set head.Next.Next to head and head.Next to nil. Finally, we return the new head. + + In the main function, we create a linked list 1 -> 2 -> 3 -> 4 -> 5, reverse it both iteratively and recursively, + and print out the reversed linked lists. +*/ +package main + +import "fmt" + +// ListNode represents a node in the linked list +type ListNode struct { + Val int + Next *ListNode +} + +// ReverseListIteratively reverses a linked list iteratively +func ReverseListIteratively(head *ListNode) *ListNode { + var prev, curr *ListNode + curr = head + + for curr != nil { + nextTemp := curr.Next + curr.Next = prev + prev = curr + curr = nextTemp + } + + return prev +} + +// ReverseListRecursively reverses a linked list recursively +func ReverseListRecursively(head *ListNode) *ListNode { + if head == nil || head.Next == nil { + return head + } + + newHead := ReverseListRecursively(head.Next) + head.Next.Next = head + head.Next = nil + + return newHead +} + +func main() { + // Create a linked list: 1 -> 2 -> 3 -> 4 -> 5 + head := &ListNode{1, &ListNode{2, &ListNode{3, &ListNode{4, &ListNode{5, nil}}}}} + + // Reverse the linked list iteratively + reversedListIteratively := ReverseListIteratively(head) + fmt.Println("Reversed linked list (iteratively):") + for reversedListIteratively != nil { + fmt.Printf("%d ", reversedListIteratively.Val) + reversedListIteratively = reversedListIteratively.Next + } + fmt.Println() + + head = &ListNode{1, &ListNode{2, &ListNode{3, &ListNode{4, &ListNode{5, nil}}}}} + // Reverse the linked list recursively + reversedListRecursively := ReverseListRecursively(head) + fmt.Println("Reversed linked list (recursively):") + for reversedListRecursively != nil { + fmt.Printf("%d ", reversedListRecursively.Val) + reversedListRecursively = reversedListRecursively.Next + } + fmt.Println() +} diff --git a/Linked List/reverse_linked_list.java b/Linked List/reverse_linked_list.java new file mode 100644 index 00000000..05606bbd --- /dev/null +++ b/Linked List/reverse_linked_list.java @@ -0,0 +1,49 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +/*Explaination of the code which reverses the linked list +* --> Initially we are given the head of the linked list. The approach used here is that +* for reversing the linked list we can take the user of three pointers +* +* --> These pointers are named as prev, curr, and right. Initially prev points to NULL, curr +* points to the current node (node of which the pointer has to be reversed) and the right node +* which always points to the node next to the current +* +* --> Idea here is that at each pass we will be reversing the pointer of the current node and +* move all the pointers forward by one step +* +* --> So initially, the current node points to head node, it's pointer is reversed and is made to +* point to he NULL, since now this first node becomes the last node. +* +* --> To move forward, prev is now at the location of current node, current node moves by +* one step, by making it point to the location where right is pointing now. (Using right pointer +* since the track of the next node is lost as we have reveresed the pointer). +* +* --> Loop stops when the current node becomes null. At the itereation, last node is being pointed +* by prev, which is now the first node logically, so assign head to prev. +* +* --> Example input: 1->2->3->4->5 +* Output : 5->4->3->2->1 +* */ +class Solution { + public ListNode reverseList(ListNode head) { + ListNode prev=null; + ListNode curr=head; + while(curr!=null){ + ListNode right=curr.next; + curr.next=prev; + prev=curr; + curr=right; + } + head=prev; + return head; + } +} \ No newline at end of file diff --git a/Linked List/singly_linked_list.cpp b/Linked List/singly_linked_list.cpp new file mode 100644 index 00000000..f65aecb0 --- /dev/null +++ b/Linked List/singly_linked_list.cpp @@ -0,0 +1,155 @@ +// Implementation of singly LinkedList + +// Author : Mohit Singh +/*Search for a node in the list +You can determine and retrieve a specific node from the front, end, or anywhere in the list. +The worst-case​ Time Complexity for retrieving a node from anywhere in the list is O(n). + +Add a node to the list +You can add a node at the front, end, or anywhere in the linked list. +The worst-case Time Complexities for performing these operations are: +Add an item to the front of the list: O(1) +Add an item to the end of the list: O(n) +Add an item a​nywhere in the list: O(n) + +Remove a node from the list +You can remove a node from the front, end, or anywhere in the list. +The worst-case Time Complexities for performing this operation are: +Remove an item from the front of the list: O(1) +Remove an item from the end of the list: O(n) +Remove an item from anywhere in the list: O(n) + +Time complexity : O(n) +Space complexity : O(n) +*/ + +#include +using namespace std; + +// Making a node struct containing an int data and a pointer +// to next node +struct Node +{ + int data; + Node *next; + + // Parameterised constructor with default argument + Node(int val = 0) : data(val), next(nullptr) {} + // Parameterise constructor + Node(int val, Node *tempNext) : data(val), next(tempNext) {} +}; + +class LinkedList +{ + // Head pointer + Node *head; + +public: + // default constructor. Initializing head pointer + LinkedList() : head(nullptr) + { + } + + // inserting elements (At start of the list) + void insert(int val) + { + // make a new node + Node *new_node = new Node(val); + + // If list is empty, make the new node, the head + if (head == nullptr) + { + head = new_node; + } + // else, make the new_node the head and its next, the previous + // head + else + { + new_node->next = head; + head = new_node; + } + } + + // loop over the list. return true if element found + bool search(int val) + { + Node *temp = head; + while (temp != nullptr) + { + if (temp->data == val) + return true; + temp = temp->next; + } + return false; + } + + void remove(int val) + { + Node *temp = head; + // If the head is to be deleted + if (temp != nullptr && temp->data == val) + { + head = temp->next; + delete temp; + return; + } + // Else loop over the list and search for the node to delete + else + { + Node *curr = head; + while (temp != nullptr && temp->data != val) + { + // When node is found, delete the node and modify the pointers + curr = temp; + temp = temp->next; + } + // If values is not found in the linked list + if (!temp) + { + cout << "Value not found" << endl; + return; + } + + curr->next = temp->next; + delete temp; + } + } + + void display() + { + Node *temp = head; + while (temp != nullptr) + { + cout << temp->data << " "; + temp = temp->next; + } + cout << endl; + } +}; + +int main() +{ + + LinkedList l; + // inserting elements + l.insert(6); + l.insert(9); + l.insert(1); + l.insert(3); + l.insert(7); + cout << "Current Linked List: "; + l.display(); + + cout << "Deleting 1: "; + l.remove(1); + l.display(); + + cout << "Deleting 13: "; + l.remove(13); + + cout << "Searching for 7: "; + cout << l.search(7) << endl; + + cout << "Searching for 13: "; + cout << l.search(13) << endl; +} \ No newline at end of file diff --git a/Linked List/singly_linked_list.go b/Linked List/singly_linked_list.go new file mode 100644 index 00000000..13f87e40 --- /dev/null +++ b/Linked List/singly_linked_list.go @@ -0,0 +1,297 @@ +package main + +import ( + "errors" + "fmt" +) + +/* + insertAtTail (val) + insertAtHead (val) + deleteAtTail () + deleteAtHead () + getLength () + print () + getAtIdx (idx) + replaceAtIdx (val, idx) + deleteAtIdx (idx) + search (val) + toArray () + reverse () +*/ + +// struct for a node type, we use interface{} to allow for any type of value, you could replace this +// with another value if you want to have more control over the type of value that can be inserted +type node struct { + val interface{} + next *node +} + +// struct for the linked list type +type linkedList struct { + head *node + tail *node + length int +} + +// constructor for a node type +func newNode(val interface{}) *node { + return &node{val: val, next: nil} +} + +// constructor for a linked list type +func newLinkedList() *linkedList { + return &linkedList{head: nil, tail: nil, length: 0} +} + +// function for linked lists to check if the linked list is empty +func (l *linkedList) IsEmpty() bool { + return l.head == nil +} + +// function for linked lists to insert a value at the tail of the linked list +func (l *linkedList) insertAtTail(val interface{}) { + node := newNode(val) + if l.IsEmpty() { + l.head = node + l.tail = l.head + l.length += 1 + return + } else { + node := node + l.tail.next = node + l.tail = node + l.length += 1 + } +} + +// function for linked lists to insert a value at the head of the linked list + +func (l *linkedList) insertAtHead(val interface{}) { + // create a new node + node := newNode(val) + // check if the list is empty + if l.IsEmpty() { + // if it is, set the head and tail to the new node and increase the length + l.head = node + l.tail = l.head + l.length += 1 + return + } else { + // if it is not, set the new node's next to the current head and set the head to the new node + node.next = l.head + l.head = node + l.length += 1 + } + +} + +func (l *linkedList) print() { + c := l.head + for c != nil { + fmt.Printf("%v", c.val) + if c.next == nil { + fmt.Println() + break + } + c = c.next + + fmt.Print(" -> ") + } + fmt.Printf("Current Length: %v\n", l.length) + +} + +// function for linked lists to delete a node at the tail of the linked list +func (l *linkedList) deleteAtTail() { + if l.IsEmpty() { + fmt.Println("Empty List") + return + } + + if l.head.next == nil { + l.head = nil + l.tail = nil + l.length -= 1 + return + } + + c := l.head + for { + if c.next.next == nil { + c.next = nil + l.tail = c + l.length -= 1 + return + } + c = c.next + } + +} + +// function for linked lists to delete a node at the head of the linked list +func (l *linkedList) deleteAtHead() { + if l.IsEmpty() { + fmt.Println("Empty List") + return + } + + if l.head.next == nil { + l.head = nil + l.tail = nil + l.length -= 1 + return + } + + l.head = l.head.next + l.length -= 1 +} + +// function for linked lists to get the length of the linked list +func (l *linkedList) getLength() int { + return l.length +} + +// function of a linked list to get the value at a given index +func (l *linkedList) getAtIdx(idx int) (interface{}, error) { + if idx >= l.length { + return nil, errors.New("index out of range") + } + + c := l.head + for i := 0; i < idx; i++ { + c = c.next + } + + return c.val, nil +} + +// function of a linked list to replace the value at a given index +// index starts at 0 +func (l *linkedList) replaceAtIdx(val interface{}, idx int) error { + if idx >= l.length { + return errors.New("index out of range") + } + + c := l.head + for i := 0; i < idx; i++ { + c = c.next + } + + c.val = val + return nil +} + +// function of a linked list to delete the value at a given index +func (l *linkedList) deleteAtIdx(idx int) error { + if idx >= l.length { + return errors.New("index out of range") + } + + c := l.head + for i := 0; i < idx-1; i++ { + c = c.next + } + + c.next = c.next.next + l.length -= 1 + return nil +} + +// function to find the index of a given value +// returns -1 and an error if the value is not found +func (l *linkedList) search(val interface{}) (int, error) { + c := l.head + for i := 0; i < l.length; i++ { + if c.val == val { + return i, nil + } + c = c.next + } + + return -1, errors.New("value not found") +} + +// function to convert a linked list to an array +func (l *linkedList) toArray() []interface{} { + arr := make([]interface{}, l.length) + c := l.head + for i := 0; i < l.length; i++ { + arr[i] = c.val + c = c.next + } + + return arr +} + +// function to reverse the linked list +// This is a recursive implementation to show something different +func (l *linkedList) reverse() error { + if l.IsEmpty() { + return errors.New("Empty List") + } + l.head = reverseListRecursive(l.head) + return nil +} + +// recursive function to reverse a linked list recursively +func reverseListRecursive(head *node) *node { + if head == nil || head.next == nil { + return head + } + + rest := reverseListRecursive(head.next) + head.next.next = head + head.next = nil + + return rest +} + +// example of a linked list +func main() { + list := newLinkedList() + + list.insertAtHead(1) + list.insertAtTail(2) + list.insertAtTail(3) + list.insertAtTail(4) + list.insertAtTail(5) + list.insertAtTail(6) + list.insertAtTail(7) + list.insertAtTail(8) + list.insertAtTail(9) + list.insertAtTail(10) + list.insertAtTail(11) + list.print() + + list.deleteAtTail() + list.print() + + err := list.replaceAtIdx(9000, 4) + if err != nil { + fmt.Println(err) + } + list.print() + + i, err := list.search(9000) + if err != nil { + fmt.Println(err) + } else { + fmt.Println(i) + } + + err = list.reverse() + if err != nil { + fmt.Println(err) + } + + list.print() + idx, err := list.search(3) + if err != nil { + fmt.Println(err) + } else { + fmt.Println(idx) + } + fmt.Println(list.toArray()) + +} diff --git a/Linked List/singly_linked_list.java b/Linked List/singly_linked_list.java new file mode 100644 index 00000000..32a6d998 --- /dev/null +++ b/Linked List/singly_linked_list.java @@ -0,0 +1,92 @@ +// Linked list: Implement Singly linked list in Java #982 + +/* +APPROACH :Node Class: The Node class is defined to represent a single node in the linked list. It has two instance variables: data to store the value and next to store the reference to the next node. + +Insertion at the Start: The insertStart method allows inserting a new node at the beginning of the linked list. It creates a new node with the given data value and sets its next reference to the current head. Then, it updates the head to point to the newly inserted node. + +Deletion from the Start: The delete method removes the first node from the linked list. It checks if the head is null, indicating an empty list. If not, it updates the head to point to the next node, effectively removing the first node. + +Display: The display method is used to print the elements of the linked list. It starts from the head node and iterates through the list by moving to the next node until the current node becomes null. During each iteration, it prints the data value of the current node. + +Main Method: The main method demonstrates the usage of the linked list implementation. It creates an empty list by initializing the head to null. It then performs several insertions using the insertStart method to add nodes at the beginning of the list. After that, it calls the display method to print the elements of the list. Next, it performs several deletions using the delete method to remove nodes from the beginning of the list. Finally, it calls the display method again to print the updated list. +*/ + +import java.lang.*; + +// Node Class +class Node { + int data; + Node next; + + Node(int x) // parameterized constructor + { + data = x; + next = null; + } +} + +class Main +{ + static Node insertStart(Node head, int data) + { + // Creating newNode memory & assigning data value + Node newNode = new Node(data); + + // assigning this newNode's next as current head node + newNode.next = head; + // re-assigning head to this newNode + head = newNode; + + return head; + } + + public static Node delete(Node head) + { + if (head == null){ + System.out.println("List is empty, not possible to delete"); + return head; + } + + System.out.println("Deleted: " + head.data); + // move head to next node + head = head.next; + + return head; + } + + static void display(Node node) { + + //as linked list will end when Node is Null + while (node != null) { + System.out.print(node.data + " "); + node = node.next; + } + System.out.println(""); + } + + public static void main(String args[]) + { + Node head = null; + head = insertStart(head,6); + head = insertStart(head,5); + head = insertStart(head,4); + head = insertStart(head,3); + head = insertStart(head,2); + head = insertStart(head,1); + + display(head); + + head = delete(head); + head = delete(head); + head = delete(head); + + display(head); + + + } +} + +/* +the space complexity is O(n), and the time complexity for each operation is either O(1) or O(n), depending on the specific operation being performed. +*/ diff --git a/Linked List/singly_linked_list.py b/Linked List/singly_linked_list.py new file mode 100644 index 00000000..b2326ee9 --- /dev/null +++ b/Linked List/singly_linked_list.py @@ -0,0 +1,47 @@ +''' +This is an implementation of a singly linked list in Python with a Node class and a LinkedList class. The Node class represents each element of the linked list and the LinkedList class has methods to manipulate the list, including append, prepend, delete_node, and print_list. +''' + +class Node: + def __init__(self, data): + self.data = data + self.next = None # The next node in the list + +class LinkedList: + def __init__(self): + self.head = None # The first node in the list + + def append(self, data): + new_node = Node(data) + if not self.head: # If the list is empty, set the new node as the head + self.head = new_node + return + curr_node = self.head + while curr_node.next: # Traverse to the last node in the list + curr_node = curr_node.next + curr_node.next = new_node # Set the next node of the last node to the new node + + def prepend(self, data): + new_node = Node(data) + new_node.next = self.head # Set the next node of the new node to the current head + self.head = new_node # Set the new node as the new head + + def delete_node(self, data): + if not self.head: # If the list is empty, do nothing + return + if self.head.data == data: # If the head is the node to delete, set the next node as the new head + self.head = self.head.next + return + curr_node = self.head + while curr_node.next: # Traverse the list until the last node + if curr_node.next.data == data: # If the next node is the node to delete, set the next node of the current node to the node after the next node + curr_node.next = curr_node.next.next + return + curr_node = curr_node.next + + def print_list(self): + curr_node = self.head + while curr_node: # Traverse the list and print each node's data + print(curr_node.data) + curr_node = curr_node.next + diff --git a/Linked_List/sll.go b/Linked List/sll.go similarity index 100% rename from Linked_List/sll.go rename to Linked List/sll.go diff --git a/Linked_List/sort_linked_list.cpp b/Linked List/sort_linked_list.cpp similarity index 100% rename from Linked_List/sort_linked_list.cpp rename to Linked List/sort_linked_list.cpp diff --git a/Linked_List/linked_list_remove_dups.cpp b/Linked_List/linked_list_remove_dups.cpp deleted file mode 100644 index ab0ae0ec..00000000 --- a/Linked_List/linked_list_remove_dups.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Remove duplicates from an unsorted linked list -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class Node{ - public: - int data; - Node *next; - - Node(int d){ - data = d; - next = NULL; - } -}; -void insert_at_tail(Node *&head, int data){ - if(head == NULL){ - head = new Node(data); - return; - } - Node *n = new Node(data); - Node *temp = head; - while(temp->next != NULL){ - temp = temp->next; - } - temp->next = n; -} -void print_linked_list(Node *head){ - while(head != NULL){ - cout << head->data << "->"; - head = head->next; - } -} -void make_linked_list(Node *&head){ - int data; - cin >> data; - while(data != -1){ - insert_at_tail(head, data); - cin >> data; - } -} -void remove_duplicates(Node *&head){ - set S; - Node *temp = head; - Node *prev = NULL; - Node *remove = NULL; - while(temp != NULL){ - if(S.find(temp->data) != S.end()){ - remove = temp; - prev->next = temp->next; - } - else{ - S.insert(temp->data); - prev = temp; - } - temp = temp->next; - delete remove; - } -} -void remove_duplicates_without_buffer(Node *&head){ - Node *current = head; - Node *remove = NULL; - while(current != NULL){ - Node *runner = current; - while(runner->next != NULL){ - if(runner->next->data == current->data){ - remove = runner->next; - runner->next = runner->next->next; - delete remove; - } - else{ - runner = runner->next; - } - } - current = current->next; - } -} -int main(){ - Node *head = NULL; - /* - make_linked_list(head); - print_linked_list(head); - cout << endl; - remove_duplicates(head); - print_linked_list(head); - */ - make_linked_list(head); - print_linked_list(head); - cout << endl; - remove_duplicates_without_buffer(head); - print_linked_list(head); -} \ No newline at end of file diff --git a/Linked_List/tempCodeRunnerFile.cpp b/Linked_List/tempCodeRunnerFile.cpp deleted file mode 100644 index 766f4d7b..00000000 --- a/Linked_List/tempCodeRunnerFile.cpp +++ /dev/null @@ -1 +0,0 @@ - print_linked_list(head); diff --git a/Math/Basic_operations.py b/Math/Basic_operations.py new file mode 100644 index 00000000..e4d8cf96 --- /dev/null +++ b/Math/Basic_operations.py @@ -0,0 +1,34 @@ +def divide(dividend, divisor): + if divisor == 0: + raise ZeroDivisionError("Division by zero") + if dividend == 0: + return 0 + + negative = (dividend < 0) ^ (divisor < 0) + dividend = abs(dividend) + divisor = abs(divisor) + + quotient = 0 + while dividend >= divisor: + dividend -= divisor + quotient += 1 + + if negative: + quotient = -quotient + + INT_MAX = 2**31 - 1 + INT_MIN = -2**31 + if quotient > INT_MAX: + return INT_MAX + elif quotient < INT_MIN: + return INT_MIN + else: + return quotient + +# Take user input for dividend and divisor +dividend = int(input("Enter the dividend: ")) +divisor = int(input("Enter the divisor: ")) + +# Perform the division and print the result +result = divide(dividend, divisor) +print(f"The result of {dividend} divided by {divisor} is: {result}") diff --git a/Math/Count.go b/Math/Count.go new file mode 100644 index 00000000..2a89980c --- /dev/null +++ b/Math/Count.go @@ -0,0 +1,20 @@ +class Solution { +public: + int countNumbersWithUniqueDigits(int n) { + if (n == 0) { + return 1; // There's only one number with 0 digits, which is 0. + } + + int result = 10; // For n = 1, there are 10 numbers with unique digits (0-9). + int uniqueDigits = 9; + int availableDigits = 9; + + for (int i = 2; i <= n && availableDigits > 0; i++) { + uniqueDigits *= availableDigits; + result += uniqueDigits; + availableDigits--; + } + + return result; + } +}; diff --git a/Math/Factorial.cpp b/Math/Factorial.cpp new file mode 100644 index 00000000..ac6a0445 --- /dev/null +++ b/Math/Factorial.cpp @@ -0,0 +1,43 @@ +/* +Explanation: +- The `factorial` function takes an integer `num` as input and calculates its factorial. The factorial of a non-negative integer N is the product of all positive integers less than or equal to N. +- In the function, we have a base case that handles the special case when `num` is 0. The factorial of 0 is defined as 1, so we return 1 for `num` equal to 0. +- For non-zero `num`, we use recursion to calculate the factorial. We call the `factorial` function with `num - 1`, and then multiply the result by `num`. +- The recursion continues until we reach the base case when `num` becomes 0, and the recursive calls start to return their values and calculate the final factorial. + +Time Complexity: The time complexity of the `factorial` function is O(N), where N is the value of the input number. This is because the function makes N recursive calls to calculate the factorial. + +Space Complexity: The space complexity is O(N) due to the recursion stack space used during the recursive calls. Each recursive call adds a new stack frame, and in the worst case, there will be N stack frames corresponding to the N recursive calls. + +Sample Input: +Number: 5 + +Sample Output: +Factorial of 5 is 120 +*/ + +#include + +// Function to calculate factorial +unsigned long long factorial(int num) { + // Base case: factorial of 0 is 1 + if (num == 0) { + return 1; + } + + // Recursive case: calculate factorial using recursion + return num * factorial(num - 1); +} + +int main() { + // Sample input + int num = 5; + + // Calculate the factorial of the number + unsigned long long result = factorial(num); + + // Print the result + std::cout << "Factorial of " << num << " is " << result << std::endl; + + return 0; +} diff --git a/Math/Factorial.go b/Math/Factorial.go new file mode 100644 index 00000000..a3617fce --- /dev/null +++ b/Math/Factorial.go @@ -0,0 +1,43 @@ +/* +Approach and Explanation: +The factorial of a non-negative integer `n` is given by the product of all positive integers less than or equal to `n`. The factorial function is typically implemented using recursion. In this implementation, we check the base case where the input `n` is less than or equal to 1, and in that case, we return 1. Otherwise, we recursively call the factorial function with `n-1` and multiply it with `n`. This process continues until the base case is reached. + +The code prompts the user to enter a number, reads the input, and then calls the `factorial` function to calculate the factorial of the entered number. The result is then printed on the console. + +Time and Space Complexity: +The time complexity of this implementation is O(n) because the recursive function is called once for each integer from `n` to 1. + +The space complexity is O(n) as well since each recursive call adds a new frame to the call stack. + +Sample Input and Output: +Enter a number: 5 +The factorial of 5 is: 120 +*/ + + +package main + +import ( + "fmt" +) + +// Function to calculate the factorial of a number +func factorial(n int) int { + if n <= 1 { + return 1 + } + return n * factorial(n-1) +} + +func main() { + // Getting user input + var number int + fmt.Print("Enter a number: ") + fmt.Scanln(&number) + + // Calculating and printing the factorial + result := factorial(number) + fmt.Printf("The factorial of %d is: %d\n", number, result) +} + + diff --git a/Math/Factorial.java b/Math/Factorial.java new file mode 100644 index 00000000..d07e8153 --- /dev/null +++ b/Math/Factorial.java @@ -0,0 +1,37 @@ +/* +What is a factorial of a number? +The factorial of a non-negative integer n is the product of all positive integers less than or equal to n. It is denoted by n!. For example, the factorial of 5 is 5! = 5 x 4 x 3 x 2 x 1 = 120. + +Approach: +The recursive approach is used to calculate the factorial. The function `factorial()` takes an integer `n` as input. In each recursive call, we check if `n` is either 0 or 1 (base case), and if so, we return 1. Otherwise, we multiply `n` with the factorial of `n-1` and return the result. + +Time Complexity: O(n) +The time complexity of this algorithm is O(n) because the recursion depth is equal to n, and in each recursion, we perform a constant-time operation. + +Space Complexity: O(n) +The space complexity is O(n) because, in the worst-case scenario, the recursion depth can reach up to n. So, n frames will be added to the call stack. + +Sample Input: 5 +Sample Output: Factorial of 5 is: 120 + +*/ + +public class Factorial { + // Recursive function to calculate the factorial + public static int factorial(int n) { + // Base case: if n is 0 or 1, the factorial is always 1 + if (n == 0 || n == 1) { + return 1; + } + + // Recursive case: multiply n with factorial of (n-1) + return n * factorial(n - 1); + } + + public static void main(String[] args) { + int number = 5; // Sample input: calculate factorial of 5 + int result = factorial(number); + System.out.println("Factorial of " + number + " is: " + result); + } +} + diff --git a/Math/Factorial.py b/Math/Factorial.py new file mode 100644 index 00000000..7f1a64b3 --- /dev/null +++ b/Math/Factorial.py @@ -0,0 +1,30 @@ +''' +Approach: +We will take the number as input from the user and calculate its factorial. +To calculate the factorial, we will start from 1 and multiply it with all the numbers from 1 to n. +Finally, we will return the factorial. + +Time Complexity: O(n) +Space Complexity: O(1) + +Sample Input: 5 +Sample Output: Factorial of 5 is 120 +''' + +# Initializing the factorial to 1 +fact = 1 +def factorial(num): + # If the number is 0 or 1, then the factorial is 1. + if num == 0 or num == 1: + return 1 + # Calculating factorial by multiplying every number from 1 to num + for i in range(1, num+1): + fact *= i + return fact + +#Taking input from user +num = int(input("Enter a number: ")) +#Calculating and printing the factorial of the number +print("Factorial of", num, "is", factorial(num)) + + diff --git a/Math/Hamming_distance.py b/Math/Hamming_distance.py new file mode 100644 index 00000000..7e800b4f --- /dev/null +++ b/Math/Hamming_distance.py @@ -0,0 +1,34 @@ +''' + The Hamming distance between two integers is the number of positions at which the corresponding bits are different. + + Given two integers x and y, return the Hamming distance between them. + + Example 1: + Input: x = 1, y = 4 + Output: 2 + Explanation: + 1 (0 0 0 1) + 4 (0 1 0 0) + ↑ ↑ + The above arrows point to positions where the corresponding bits are different. + + Example 2: + Input: x = 3, y = 1 + Output: 1 + + Constraints: + 0 <= x, y <= 231 - 1 +''' +class Solution: + def totalHammingDistance(self, nums: List[int]) -> int: + '''By using bit manipulation, as all the array elements are 32-bit array elements, we calculate all the number of set bits and unset bits as we need to consider the permutations, we take setbits*unsetbits.''' + hamming_dist,n = 0,len(nums) + for i in range(32): + count = 0 + for element in nums: + #Right shifting the element by the index and performing &1 lets us know if a bit is set or not + if((element>>i)&1): + count+=1 + #Adding all the combinations where there are set and unset bits. + hamming_dist+=count*(n-count) + return hamming_dist \ No newline at end of file diff --git a/Math/Hammingdistance.cpp b/Math/Hammingdistance.cpp new file mode 100644 index 00000000..7c9904ee --- /dev/null +++ b/Math/Hammingdistance.cpp @@ -0,0 +1,34 @@ +#include + +class Solution { +public: + int totalHammingDistance(std::vector& nums) { + // Get the number of elements in the input vector + int n = nums.size(); + // Initialize the variable to store the total Hamming distance + int ans = 0; + + // Iterate through each bit position (from 0 to 31) + for (int i = 0; i < 32; i++) { + // Initialize a variable to count the number of set bits at the current bit position + int count = 0; + + // Iterate through all elements in the vector + for (int k = 0; k < n; k++) { + // Count the number of set bits at the current bit position for each element + count += (nums[k] >> i) & 1; + } + + // Update the total Hamming distance by adding the product of set bits and unset bits + ans += count * (n - count); + } + + // Return the total Hamming distance + return ans; + } +}; + +/* +This program defines a Solution class with a totalHammingDistance method that calculates the total Hamming distance for the input vector nums. The outer loop iterates over each bit position (from 0 to 31), and the inner loop counts the number of set bits at that bit position for all elements in the vector. It then updates the ans variable by adding the product of the count of set bits and the count of unset bits at that position. + +This code efficiently calculates the total Hamming distance for a vector of integers by considering each bit position separately. */ diff --git a/Math/K_closest.cpp b/Math/K_closest.cpp new file mode 100644 index 00000000..b3f334ee --- /dev/null +++ b/Math/K_closest.cpp @@ -0,0 +1,88 @@ +/* + Author: SUYASH SINGH + +Problem statement:-Find K Closest Elements +Given a sorted integer array arr, two integers k and x, return the k closest integers to x in the array. The result should also be sorted in ascending order. + +An integer a is closer to x than an integer b if: + +|a - x| < |b - x|, or +|a - x| == |b - x| and a < b +Explaination: + +Input: points = [[1,3],[-2,2]], k = 1 +Output: [[-2,2]] +Explanation: +The distance between (1, 3) and the origin is sqrt(10). +The distance between (-2, 2) and the origin is sqrt(8). +Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. +We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. +Example 2: +Input: points = [[3,3],[5,-1],[-2,4]], k = 2 +Output: [[3,3],[-2,4]] +Explanation: The answer [[-2,4],[3,3]] would also be accepted. +*/ + +#include +#include +#include +#include + +using namespace std; + +class Solution { +public: + vector> kClosest(vector>& points, int k) { + // Answer vector + vector> result(k); + // Max heap storage initialization + priority_queue> maxHeap; + // Construction of max heap + for (auto& p : points) { + int x = p[0], y = p[1]; + // Calculate the squared distance from the origin using the formula x^2 + y^2 + // Store the distance along with the coordinates (x, y) in the maxHeap + maxHeap.push({x * x + y * y, x, y}); + // If the size of the maxHeap exceeds k, remove the point with the maximum distance + if (maxHeap.size() > k) { + maxHeap.pop(); + } + } + // Extract the k closest points from the maxHeap and store them in the result vector + for (int i = k - 1; i >= 0; --i) { + vector top = maxHeap.top(); + maxHeap.pop(); + result[i] = {top[1], top[2]}; + } + + // Return the result vector containing the k closest points + + return result; + } +}; + +int main() { + vector> points = {{1, 3}, {-2, 2}, {5, -1}, {0, 0}, {3, 4}}; + int k = 3; + + Solution solution; + vector> closestPoints = solution.kClosest(points, k); + + cout << "The " << k << " closest points to the origin are:\n"; + for (const auto& point : closestPoints) { + cout << "(" << point[0] << ", " << point[1] << ")\n"; + } + + return 0; +}/* +Time Complexity: + +Constructing the maxHeap: the overall time complexity is O(N log K). +Extracting the k closest points: O(K log K). We extract the top element from the maxHeap k times, each operation taking O(log K) time. Hence, the time complexity is O(K log K). +Therefore, the overall time complexity of the solution is O(N log K), where N is the number of points and K is the value of k. + +Space Complexity: + +The maxHeap stores at most k elements, so the space complexity for the maxHeap is O(K). +The result vector stores k closest points, resulting in O(K) space. +Apart from these, the solution uses a constant amount of space for variables and temporary storage.*/ diff --git a/Math/K_closest_points_to_origin.java b/Math/K_closest_points_to_origin.java new file mode 100644 index 00000000..3bf38816 --- /dev/null +++ b/Math/K_closest_points_to_origin.java @@ -0,0 +1,79 @@ +/** + Time Complexity: O(nlog(n)), Space Complexity: O(n) + + Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). + The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)^2 + (y1 - y2)^2). + You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). + + Example 1: + Input: points = [[1,3],[-2,2]], k = 1 + Output: [[-2,2]] + + Explanation: + The distance between (1, 3) and the origin is sqrt(10). + The distance between (-2, 2) and the origin is sqrt(8). + Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. + We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. + + + Example 2: + Input: points = [[3,3],[5,-1],[-2,4]], k = 2 + Output: [[3,3],[-2,4]] + + Explanation: The answer [[-2,4],[3,3]] would also be accepted. + +**/ + + +class Solution { + public int[][] kClosest(int[][] points, int k) { + + + /** + The solution is very simple, for each point(x, y) we calculate the euclidean distance between + (x, y) and the origin point (0, 0), then add all the distances in Priority Queue (Minimum Heap). + The priority queue will sort them in increasing order. + + + NOTE that we need to return the points not the distances, that's why we make Priority Queue of Pair>. + **/ + + PriorityQueue>> pq = + new PriorityQueue<>(new SortBySmallerDistanceComparator()); + + for (int i = 0; i < points.length; ++i) + { + int x = points[i][0], y = points[i][1]; + double distance = Math.sqrt(x * x + y * y); + pq.add(new Pair(distance, new Pair(x, y))); + } + + int [][]closestK = new int[k][2]; + + for(int i = 0; i < k; ++i) + { + closestK[i][0] = pq.peek().getValue().getKey(); + closestK[i][1] = pq.peek().getValue().getValue(); + pq.poll(); + + } + + return closestK; + + } + + + /* + Compartor to sort the points based on the distance (smaller). + */ + + class SortBySmallerDistanceComparator implements Comparator>> + { + @Override + public int compare(Pair> a, + Pair> b) { + return (a.getKey() - b.getKey()) <= 0 ? -1 : 1; + } + } + +} diff --git a/Math/K_closest_points_to_origin.py b/Math/K_closest_points_to_origin.py new file mode 100644 index 00000000..77aa76b2 --- /dev/null +++ b/Math/K_closest_points_to_origin.py @@ -0,0 +1,53 @@ +''' + Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). + + The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). + + You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). + + Example 1: + Input: points = [[1,3],[-2,2]], k = 1 + Output: [[-2,2]] + Explanation: + The distance between (1, 3) and the origin is sqrt(10). + The distance between (-2, 2) and the origin is sqrt(8). + Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. + We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. + + Example 2: + Input: points = [[3,3],[5,-1],[-2,4]], k = 2 + Output: [[3,3],[-2,4]] + Explanation: The answer [[-2,4],[3,3]] would also be accepted. + + Constraints: + 1 <= k <= points.length <= 104 + -104 < xi, yi < 104 +''' +import math +class Solution: + def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: + distance_arr=[] + #calculating the distance from center + for i in points: + distance_arr.append(math.sqrt(pow(i[0],2)+pow(i[1],2))) + #Mapping the values of distance and point + #Also considering the collision if two points have same distance from origin + hash_map={} + for i in range(len(points)): + if(distance_arr[i] in hash_map): + hash_map[distance_arr[i]].append(points[i]) + else: + hash_map[distance_arr[i]] = [points[i]] + + #sorting distances + distance_arr.sort() + + #Retrieving the values in the order of distance from distance_arr + final_arr,count = [],0 + for i in range(len(points)): + for j in hash_map[distance_arr[i]]: + final_arr.append(j) + count+=1 + if(count>=k): + break + return final_arr \ No newline at end of file diff --git a/Math/Number_of_Substrings_With_Only_1s.cpp b/Math/Number_of_Substrings_With_Only_1s.cpp new file mode 100644 index 00000000..b118f7d5 --- /dev/null +++ b/Math/Number_of_Substrings_With_Only_1s.cpp @@ -0,0 +1,50 @@ +/* +Given a binary string s, return the number of substrings with all characters 1's. Since the answer may be too large, return it modulo 109 + 7. + + + +Example 1: + +Input: s = "0110111" +Output: 9 +Explanation: There are 9 substring in total with only 1's characters. +"1" -> 5 times. +"11" -> 3 times. +"111" -> 1 time. +Example 2: + +Input: s = "101" +Output: 2 +Explanation: Substring "1" is shown 2 times in s. +Example 3: + +Input: s = "111111" +Output: 21 +Explanation: Each substring contains only 1's characters. + + +Constraints: + +1 <= s.length <= 105 +s[i] is either '0' or '1'. + +Approach: First we count continuos number of ones and if we have n continuos 1's than total substring = (n*(n+1))/2. Using this we can count all substring and add them and return. +using this property (a*b)%mod = ((a%mod)*(b%mod))%mod +*/ +class Solution { +public: + int mod = 1000000007; + int numSub(string s) { + long long cnt = 0,ans=0; + for(int i = 0;i INT_MAX || x < INT_MIN) // checking the integer is in range of -2^31 <= x <= 2^31 - 1 + return 0; + while(x != 0){ + if(ans > INT_MAX/10 || ans < INT_MIN/10) // -2^31 <= x <= 2^31 - 1 this condition for border values of range + return 0; + ans = ans*10; // we increaing the unit position each itereation + ans += x%10; // here we finding the last unit elemet so that we can add in ans + x /=10; // decreamenting the value or we can say elemenating the last unit element that we are find + + } + return ans; // returning the ans; + } diff --git a/Math/Sum_four.cpp b/Math/Sum_four.cpp new file mode 100644 index 00000000..977210f2 --- /dev/null +++ b/Math/Sum_four.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +using namespace std; + +vector> fourSum(vector& nums, int target) { + vector> result; + + int n = nums.size(); + + if (n < 4) { + return result; + } + + sort(nums.begin(), nums.end()); + + for (int i = 0; i < n - 3; i++) { + // Avoid duplicates + if (i > 0 && nums[i] == nums[i - 1]) { + continue; + } + + for (int j = i + 1; j < n - 2; j++) { + // Avoid duplicates + if (j > i + 1 && nums[j] == nums[j - 1]) { + continue; + } + + int left = j + 1; + int right = n - 1; + + while (left < right) { + int sum = nums[i] + nums[j] + nums[left] + nums[right]; + + if (sum < target) { + left++; + } else if (sum > target) { + right--; + } else { + result.push_back({nums[i], nums[j], nums[left], nums[right]}); + + // Avoid duplicates + while (left < right && nums[left] == nums[left + 1]) { + left++; + } + while (left < right && nums[right] == nums[right - 1]) { + right--; + } + + left++; + right--; + } + } + } + } + + return result; +} + +int main() { + vector nums = {1, 0, -1, 0, -2, 2}; + int target = 0; + + vector> result = fourSum(nums, target); + + for (vector& quad : result) { + cout << "["; + for (int i = 0; i < 4; i++) { + cout << quad[i]; + if (i < 3) { + cout << ", "; + } + } + cout << "]" << endl; + } + + return 0; +} diff --git a/Math/circle.cpp b/Math/circle.cpp new file mode 100644 index 00000000..4e2567dc --- /dev/null +++ b/Math/circle.cpp @@ -0,0 +1,28 @@ +class Solution { + public int[] countPoints(int[][] points, int[][] queries) { + int[] answer = new int[queries.length]; + + for (int j = 0; j < queries.length; j++) { + int centerX = queries[j][0]; + int centerY = queries[j][1]; + int radius = queries[j][2]; + + for (int i = 0; i < points.length; i++) { + int pointX = points[i][0]; + int pointY = points[i][1]; + + // Check if the point (pointX, pointY) is inside the circle. + if (isInsideCircle(pointX, pointY, centerX, centerY, radius)) { + answer[j]++; + } + } + } + + return answer; + } + + private boolean isInsideCircle(int x, int y, int centerX, int centerY, int radius) { + int distanceSquared = (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY); + return distanceSquared <= radius * radius; + } +} diff --git a/Math/count_numbers_with_unique_digits.java b/Math/count_numbers_with_unique_digits.java new file mode 100644 index 00000000..fc395738 --- /dev/null +++ b/Math/count_numbers_with_unique_digits.java @@ -0,0 +1,58 @@ +/* Implement countNumbersWithUniqueDigits(n), which calculates the number of +all numbers with unique digits, x, where 0 <= x < 10^n. + +Explanation: +- For n=0, the answer is 1 (only the number 0, here considered to have 0 digits). +- For n=1, the answer is the number of one-digit numbers with unique digits (9), + plus the result for n=0 (1). +- For n=2, the answer is the number of two-digit numbers with unique digits (between + 10 and 99), plus the result for n=1. The number of two-digit numbers with unique digits + is 9*9 (9 possible first digits between 1-9, then 9 possible second digits between 0-9 + that are different from the first digit). +- For n=3, the answer is the number of three-digit number with unique digits (between 100 + and 999), plus the result for n=2. The number of three-digit numbers with unique digits + is 9*9*8 (9 possible first digits between 1-9, then 9 possible second digits between 0-9 + that are different from the first digit, then 8 possible third digits between 0-9 that are + different from the first two). +- And so on, using recursive calls of the same function. +Note: There are no numbers with 11 or more digits that have unique digits. So for n>=10, the +result will be the same for any n. + +Example 1: +Input: n=3 +Output: 739 + +Example 2: +Input: n=6 +Output: 168571 + +Example 3: +Input: n=8 +Output: 2345851 + +Constraints: +n >= 0 + +Time complexity: O(n) +Space complexity: O(n) + +*/ + +class Solution { + + public int countNumbersWithUniqueDigits(int n) { + + if (n==0) { + return 1; + } + + int counter = 9; + for (int i=1; i=2 multiplied by any integer j>=2 is not prime. + So the corresponding value of i*j in the array is changed to true. + This will only affect cells with an index number higher than the + current i, as i*j>i for any j>=2.*/ + for (int j=2; i*j 0 <= n <= 8 +*/ + +class Solution { +public: + +/* + By using concepts of permutation and combination, we can count the unique numbers for N digits. + For the first digit we can choose any 1 digit from 1-9 + For the latter digits we can choose any 1 digit from 0-9 except the digits already used + +*/ + int countNumbersWithUniqueDigits(int n) { + int ans=0; + for(int i=0;i 0, + the function initializes the count to 10 (digits 0-9), and then iterates through each number of digits from 2 to n. + For each number of digits, the function calculates the number of possible unique numbers and adds it to the total count. + The number of possible unique numbers for i digits is calculated by first choosing the first digit (9 choices) and then + choosing each subsequent digit (8 choices for the second digit, 7 choices for the third digit, and so on). + The product of all these choices is the total number of possible unique numbers for i digits. + This calculation is repeated for each number of digits from 2 to n, and the total count is returned at the end. + + For example, if n = 2, the function should return 91. There are 10 possible numbers with 1 digit (0-9), and + 91 possible numbers with 2 digits (10-99) that have unique digits +*/ +package main + +import ( + "fmt" +) + +func countNumbersWithUniqueDigits(n int) int { + // Base case: n = 0, only 1 possible number (0) + if n == 0 { + return 1 + } + + // For n > 0, initialize count to 10 (digits 0-9) + count := 10 + + // Calculate count for 2 to n digits + for i := 2; i <= n; i++ { + // For each number of digits, calculate the number of possible unique numbers + // First digit can be 1-9 (9 choices), subsequent digits can be any of the remaining 9-1 = 8 choices + // Multiply the choices together to get the total number of possible unique numbers + // Add this count to the total count + currCount := 9 + for j := 1; j < i; j++ { + currCount *= 10 - j + } + count += currCount + } + + return count +} + +func main() { + // Test case with n = 2 + fmt.Println(countNumbersWithUniqueDigits(2)) // Output: 91 +} diff --git a/Math/factorial.py b/Math/factorial.py new file mode 100644 index 00000000..7f1a64b3 --- /dev/null +++ b/Math/factorial.py @@ -0,0 +1,30 @@ +''' +Approach: +We will take the number as input from the user and calculate its factorial. +To calculate the factorial, we will start from 1 and multiply it with all the numbers from 1 to n. +Finally, we will return the factorial. + +Time Complexity: O(n) +Space Complexity: O(1) + +Sample Input: 5 +Sample Output: Factorial of 5 is 120 +''' + +# Initializing the factorial to 1 +fact = 1 +def factorial(num): + # If the number is 0 or 1, then the factorial is 1. + if num == 0 or num == 1: + return 1 + # Calculating factorial by multiplying every number from 1 to num + for i in range(1, num+1): + fact *= i + return fact + +#Taking input from user +num = int(input("Enter a number: ")) +#Calculating and printing the factorial of the number +print("Factorial of", num, "is", factorial(num)) + + diff --git a/Math/factorial_iterative.js b/Math/factorial_iterative.js new file mode 100644 index 00000000..4e5971d0 --- /dev/null +++ b/Math/factorial_iterative.js @@ -0,0 +1,19 @@ +// Program to find factorial till a given number +function factorialIterative(number) { + var factorialResult = 1; + + if (number == 0) { + return 0; + } + + for (i = 1; i <= number; i++) { + factorialResult *= i; + } + return factorialResult; +} + +// Driver code +console.log(factorialIterative(2)); +console.log(factorialIterative(3)); +console.log(factorialIterative(4)); +console.log(factorialIterative(5)); diff --git a/Math/find_longest_increasing_subsequence.cpp b/Math/find_longest_increasing_subsequence.cpp new file mode 100644 index 00000000..dbd928be --- /dev/null +++ b/Math/find_longest_increasing_subsequence.cpp @@ -0,0 +1,55 @@ +/* + Given an integer array nums, return the length of the longest strictly increasing subsequence. + + Example 1: + Input: nums = [10,9,2,5,3,7,101,18] + Output: 4 + Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. + + Example 2: + Input: nums = [0,1,0,3,2,3] + Output: 4 + Explanation: The longest increasing subsequence is [0,1,2,3], therefore the length is 4. + + Example 3: + Input: nums = [7,7,7,7,7,7,7] + Output: 1 + + Explanation of the approach: + 1. We will use the concept of Dynamic Programming to solve this problem. + 2. We will create a dp array of size n, where n is the size of the given array. + 3. We will initialize the dp array with 1, as the length of the longest increasing subsequence ending at any index is atleast 1. + 4. We will iterate over the array from the second index to the last index. + 5. For each index, we will iterate over the array from the first index to the current index. + 6. If the current element is greater than the element at the inner loop, then we will update the dp array at the current index with the maximum of the current value and the value at the inner loop + 1. + 7. We will return the maximum value in the dp array. + + Time Complexity: O(N^2), where N is the size of the given array. + Space Complexity: O(N), where N is the size of the given array. + +*/ + +class Solution{ +public: + int lengthOfLIS(vector &nums) + { + // Initializing the dp array with 1, as the length of the longest increasing subsequence ending at any index is atleast 1. + vector dp(nums.size(), 1); + // Iterating over the array from the second index to the last index. + for (int i = 1; i < nums.size(); i++) + { + // Iterating over the array from the first index to the current index. + for (int j = 0; j < i; j++) + { + // If the current element is greater than the element at the inner loop, then we will update the dp array at the current index with the maximum of the current value and the value at the inner loop + 1. + // This is because, if the current element is greater than the element at the inner loop, then we can add the current element to the increasing subsequence ending at the inner loop element, and the length of the increasing subsequence ending at the current element will be the length of the increasing subsequence ending at the inner loop element + 1. + // We will take the maximum of the current value and the value at the inner loop + 1, because we want to find the length of the longest increasing subsequence ending at the current element. + // We will update the dp array at the current index with the maximum of the current value and the value at the inner loop + 1. + if (nums[i] > nums[j]) + dp[i] = max(dp[i], dp[j] + 1); + } + } + // Returning the maximum value in the dp array. + return *max_element(dp.begin(), dp.end()); + } +}; \ No newline at end of file diff --git a/Math/hamming_distance.cpp b/Math/hamming_distance.cpp new file mode 100644 index 00000000..f19a033d --- /dev/null +++ b/Math/hamming_distance.cpp @@ -0,0 +1,52 @@ +/* +Example: +Input: nums = [4,14,2] +Output: 6 + +Explanation: +In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 (just +showing the four bits relevant in this case). +The answer will be: +HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6. + +Detailed Explanation: +The totalHammingDistance function takes a reference to a vector of integers nums, and returns the total Hamming distance between all possible pairs of elements in the vector. + +In the function, we initialize the answer ans to 0, and the size of the vector n is obtained using the size() method of the vector. We then loop through all 32 bits (since we are dealing with 32-bit integers), and for each bit position i, we count the number of elements in nums with the i-th bit set by looping through all elements and checking if the bit is set using bitwise AND operator &. + +The count c is then multiplied by (n-c), which gives the number of pairs with different bits at the i-th position. This count is added to the answer ans. Finally, the function returns the total Hamming distance. +The main function is just an example usage, where we create a vector nums and call the totalHammingDistance method of a Solution object to get the total Hamming distance between all possible pairs of elements in nums. +*/ +// code: + +#include +#include + +using namespace std; + +class Solution { +public: + int totalHammingDistance(vector& nums) { + int ans = 0; + int n = nums.size(); + for(int i = 0; i < 32; i++) { + int c = 0; + for(int j = 0; j < n; j++) { + if((nums[j] & (1 << i))) { + c++; + } + } + ans += (c * (n - c)); + } + return ans; + } +}; + +int main() { + // Example usage + vector nums = {4, 14, 2}; + Solution s; + int result = s.totalHammingDistance(nums); + return 0; +} + diff --git a/Math/is_power_of_two.js b/Math/is_power_of_two.js new file mode 100644 index 00000000..47b63566 --- /dev/null +++ b/Math/is_power_of_two.js @@ -0,0 +1,50 @@ +// Whether the provided number is power of two + +// Time complexity - O(log n) +function isPowerOfTwo(inputNumber) { + if (inputNumber <= 1) { + return false; + } + + var dividedNumber = inputNumber; + + while (dividedNumber !== 1) { + if (dividedNumber % 2 !== 0) { + return false; + } + dividedNumber = dividedNumber / 2; + } + return true; +} + +// Time complexity - O(1) +function isPowerOfTwoConst(inputNumber) { + if (inputNumber <= 1) { + return false; + } + + return ( + parseInt(Math.ceil(Math.log(n) / Math.log(2))) == + parseInt(Math.floor(Math.log(n) / Math.log(2))) + ); +} + +// Time complexity - O(log n) +function isPowerOfTwoConst(inputNumber) { + if (inputNumber <= 1) { + return false; + } + + return ( + parseInt(Math.ceil(Math.log(n) / Math.log(2))) == + parseInt(Math.floor(Math.log(n) / Math.log(2))) + ); +} + +//Driver code +console.log(isPowerOfTwo(8)); +console.log(isPowerOfTwo(1)); +console.log(isPowerOfTwo(7)); +console.log(isPowerOfTwo(6)); +console.log(isPowerOfTwo(16)); +console.log(isPowerOfTwo(32)); diff --git a/Math/is_prime.js b/Math/is_prime.js new file mode 100644 index 00000000..a0954483 --- /dev/null +++ b/Math/is_prime.js @@ -0,0 +1,44 @@ +// Whether the given number is a prime nujmber or not +// Time complexity O(n) +function isPrime(input) { + for (i = 2; i < input; i++) { + if (input % i == 0) { + return "The number is not a prime number"; + break; + } else { + continue; + } + } + return "Given number is a prime number"; +} + +// Driver code +console.log(isPrime(6)); + +// Time complexity: O(sqrt(n)) +// Space complexity: O(1) +const isPrime = (num) => { + for (let i = 2, s = Math.sqrt(num); i <= s; i++) { + if (num % i === 0) return false; + } + return num > 1; +}; + +console.log(isPrime(10)); + +// Time complexity - O(logn) +function isPrimeLogn(input) { + for (i = 2; i < Math.sqrt(input); i++) { + if (input % i == 0) { + return "The number is not a prime number"; + break; + } else { + continue; + } + } + return "Given number is a prime number"; +} + +// Driver code +console.log(isPrime(6)); +console.log(isPrimeLogn(7)); diff --git a/Math/k_closest_points_to_origin.cpp b/Math/k_closest_points_to_origin.cpp new file mode 100644 index 00000000..3c790b39 --- /dev/null +++ b/Math/k_closest_points_to_origin.cpp @@ -0,0 +1,45 @@ +/* + Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). + You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). + + Input: points = [[1,3],[-2,2]], k = 1 + Output: [[-2,2]] + + Input: points = [[3,3],[5,-1],[-2,4]], k = 2 + Output: [[3,3],[-2,4]] + + Constraints: + > 1 <= k <= points.length <= 104 + > -104 < xi, yi < 104 + +*/ +class Solution { +public: + vector> kClosest(vector>& points, int k) { + // Lets define some variables we will be needing + vector> ans; + vector> sorted; + + // For each point in the list points lets store distance_from_origin and point index as a pair + for(int i=0;i -using namespace std; -int modular_expo(int x, int n, int mod){ - int ans = 1; - while(n >= 1){ - if(n % 2 == 0){ - x = ((x % mod) * (x % mod)) % mod; - n /= 2; - } - else{ - ans = ((ans % mod) * (x % mod)) % mod; - n--; - } - } - return ans; -} -int main(){ - int mod = 1000000007; - cout << modular_expo(5, 3, mod); -return 0; -} +// Modular exponentiation is exponentiation performed over a modulus. +// It is useful in computer science, especially in the field of public-key cryptography, +// where it is used in both Diffie-Hellman Key Exchange and RSA public/private keys. +// Sample Input : 10 3 +// Output : 1000 +// Iterative approach +#include +using namespace std; +int modular_expo(int x, int n, int mod){ + int ans = 1; + while(n >= 1){ + if(n % 2 == 0){ + x = ((x % mod) * (x % mod)) % mod; + n /= 2; + } + else{ + ans = ((ans % mod) * (x % mod)) % mod; + n--; + } + } + return ans; +} +int main(){ + int mod = 1000000007; + cout << modular_expo(5, 3, mod); +return 0; +} diff --git a/Math/num_points_inside_a_circle.cpp b/Math/num_points_inside_a_circle.cpp new file mode 100644 index 00000000..c1060234 --- /dev/null +++ b/Math/num_points_inside_a_circle.cpp @@ -0,0 +1,60 @@ +/* + You are given an array points where points[i] = [xi, yi] is the coordinates of the ith point on a 2D plane. Multiple points can have the same coordinates. + You are also given an array queries where queries[j] = [xj, yj, rj] describes a circle centered at (xj, yj) with a radius of rj. + For each query queries[j], compute the number of points inside the jth circle. Points on the border of the circle are considered inside. + Return an array answer, where answer[j] is the answer to the jth query. + + Example 1: + Input: points = [[1,3],[3,3],[5,3],[2,2]], queries = [[2,3,1],[4,3,1],[1,1,2]] + Output: [3,2,2] + Explanation: + queries[0] is the first circle [2,3,1], which include the points [1,3], [2,2] and [3,3]. + queries[1] is the second circle [4,3,1], which include the points [3,3] and [5,3]. + and queries[2] is the third circle [1,1,2], which include the point [1,3] and [2,2]. + + + Example 2: + Input: points = [[1,1],[2,2],[3,3],[4,4],[5,5]], queries = [[1,2,2],[2,2,2],[4,3,2],[4,3,3]] + Output: [2,3,2,4] + Explanation: + queries[0] is the first circle [1,2,2], which include the points [1,1] and [2,2]. + queries[1] is the second circle [2,2,2], which include the points [1,1], [2,2] and [3,3]. + queries[2] is the third circle [4,3,2], which include the points [3,3] and [4,4]. + and queries[3] is the fourth circle [4,3,3], which include the points [2,2], [3,3], [4,4] and [5,5]. + + Explanation of the approach: + 1. We will use the formula of distance between two points to find the distance between the center of the circle and the given point. + 2. If the distance is less than or equal to the radius of the circle, then the point lies inside the circle. + + Time Complexity: O(N * Q), where N is the number of points and Q is the number of queries. + Space Complexity: O(1) +*/ + +class Solution +{ +public: + vector countPoints(vector> &points, vector> &queries) + { + vector ans; + // Iterating over the queries. + for (int i = 0; i < queries.size(); i++) + { + // Initializing the count to 0. This will store the number of points inside the circle. + int count = 0; + // Iterating over the points. + for (int j = 0; j < points.size(); j++) + { + // Calculating the distance between the center of the circle and the given point. + int distance = pow(points[j][0] - queries[i][0], 2) + pow(points[j][1] - queries[i][1], 2); + // If the distance is less than or equal to the radius of the circle, then the point lies inside the circle. + if (distance <= pow(queries[i][2], 2)) + count++; // If the point lies inside the circle, then increment the count. + } + // Pushing the count to the answer vector. + ans.push_back(count); + } + return ans; + } + + // Further reading: https://www.geeksforgeeks.org/queries-points-number-points-lie-given-circle/ +}; \ No newline at end of file diff --git a/Math/num_points_inside_a_circle.js b/Math/num_points_inside_a_circle.js new file mode 100644 index 00000000..7c12f6dd --- /dev/null +++ b/Math/num_points_inside_a_circle.js @@ -0,0 +1,45 @@ +// Program Author : TheCodeVenturer[Niraj Modi] +/* + Program Definition : Queries on Number of Points inside a circle + Description : you are given n points in a cartesian plane [points] and a queries array + Where each element inside it is an array each consisting of [xj, yj, rj] where, + (xj,yj) is centre of the circle and rj is radius of the circle + Now, you are required to find no.of points inside the circle + + Approach: + The only possible soln somehow will be that you have to calculate distance of each point from the centre of each circle + and if the distance <= radius then the point is inside the the circle else not + + Distance Formula = ((x1-x2)**2 + (y1-y2)**2)**(1/2) + Complexity: + The time complexity of this solution is O(m*n) where m = points.length and n = queries.length + The space complexity is O(1),we are not using any Auxiliary Spaces. + Sample input/outputs: + Example 1: + Input: + points = [[1,3],[3,3],[5,3],[2,2]] + queries = [[2,3,1],[4,3,1],[1,1,2]] + Output: [3,2,2] + Example 1: + Input: + points = [[1,1],[2,2],[3,3],[4,4],[5,5]] + queries = [[1,2,2],[2,2,2],[4,3,2],[4,3,3]] + Output: [2,3,2,4] + */ + +var countPoints = function(points, queries) { + sol=[] // Initialising solution array + for(let query of queries){ + var count=0 //no.of points inside that circle is initially zero + for(let point of points){ + d = Math.sqrt((query[0]-point[0])**2+(query[1]-point[1])**2) //calculating distance between the points + if(d<=query[2])count++ //if it is <= radius it is inside the circle + } + sol.push(count) //finally add the solution to solution array + } + return sol +}; + +points = [[1,3],[3,3],[5,3],[2,2]] +queries = [[2,3,1],[4,3,1],[1,1,2]] +console.log(countPoints(points,queries)) \ No newline at end of file diff --git a/Math/num_points_inside_a_circle.py b/Math/num_points_inside_a_circle.py new file mode 100644 index 00000000..9c646e91 --- /dev/null +++ b/Math/num_points_inside_a_circle.py @@ -0,0 +1,44 @@ +# Program Author : TheCodeVenturer[Niraj Modi] +''' + Program Definition : Queries on Number of Points inside a circle + Description : you are given n points in a cartesian plane [points] and a queries array + Where each element inside it is an array each consisting of [xj, yj, rj] where, + (xj,yj) is centre of the circle and rj is radius of the circle + Now, you are required to find no.of points inside the circle + + Approach: + The only possible soln somehow will be that you have to calculate distance of each point from the centre of each circle + and if the distance <= radius then the point is inside the the circle else not + + Distance Formula = ((x1-x2)**2 + (y1-y2)**2)**(1/2) + Complexity: + The time complexity of this solution is O(m*n) where m = points.length and n = queries.length + The space complexity is O(1),we are not using any Auxiliary Spaces. + Sample input/outputs: + Example 1: + Input: + points = [[1,3],[3,3],[5,3],[2,2]] + queries = [[2,3,1],[4,3,1],[1,1,2]] + Output: [3,2,2] + Example 1: + Input: + points = [[1,1],[2,2],[3,3],[4,4],[5,5]] + queries = [[1,2,2],[2,2,2],[4,3,2],[4,3,3]] + Output: [2,3,2,4] +''' +class Solution: + def countPoints(self, points: list[list[int]], queries: list[list[int]]) -> list[int]: + sol :list = [] #Initialising solution array + for x1,y1,r in queries: + count:int=0 #no.of points inside that circle is initially zero + for x2,y2 in points: + d = ((x2-x1)**2 + (y2-y1)**2)**(1/2) #calculating distance between the points + if(d<=r): #if it is <= radius it is inside the circle + count+=1 + sol.append(count) #finally add the count to solution array + return sol + + +points = [[1,3],[3,3],[5,3],[2,2]] +queries = [[2,3,1],[4,3,1],[1,1,2]] +print(Solution().countPoints(points,queries)) \ No newline at end of file diff --git a/Math/num_steps_reduce_to_zero.Go b/Math/num_steps_reduce_to_zero.Go new file mode 100644 index 00000000..97c309b9 --- /dev/null +++ b/Math/num_steps_reduce_to_zero.Go @@ -0,0 +1,44 @@ +/* + Given an integer num, return the number of steps to reduce it to zero. + + In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. + + Example 1: + Input: num = 14 + Output: 6 + Explanation: + Step 1) 14 is even; divide by 2 and obtain 7. + Step 2) 7 is odd; subtract 1 and obtain 6. + Step 3) 6 is even; divide by 2 and obtain 3. + Step 4) 3 is odd; subtract 1 and obtain 2. + Step 5) 2 is even; divide by 2 and obtain 1. + Step 6) 1 is odd; subtract 1 and obtain 0. + + Example 2: + Input: num = 8 + Output: 4 + Explanation: + Step 1) 8 is even; divide by 2 and obtain 4. + Step 2) 4 is even; divide by 2 and obtain 2. + Step 3) 2 is even; divide by 2 and obtain 1. + Step 4) 1 is odd; subtract 1 and obtain 0. + + Example 3: + Input: num = 123 + Output: 12 + + Constraints: 0 <= num <= 106 +*/ +func numberOfSteps(num int) int { + var steps = 0 + for num != 0 { + if num % 2 == 0 { + num /= 2 + steps++ + } else { + num -= 1 + steps++ + } + } + return steps +} diff --git a/Math/num_steps_reduce_to_zero.cpp b/Math/num_steps_reduce_to_zero.cpp new file mode 100644 index 00000000..85618df1 --- /dev/null +++ b/Math/num_steps_reduce_to_zero.cpp @@ -0,0 +1,55 @@ +/* + Given an integer num, return the number of steps to reduce it to zero. + + In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. + + Example 1: + Input: num = 14 + Output: 6 + Explanation: + Step 1) 14 is even; divide by 2 and obtain 7. + Step 2) 7 is odd; subtract 1 and obtain 6. + Step 3) 6 is even; divide by 2 and obtain 3. + Step 4) 3 is odd; subtract 1 and obtain 2. + Step 5) 2 is even; divide by 2 and obtain 1. + Step 6) 1 is odd; subtract 1 and obtain 0. + + Example 2: + Input: num = 8 + Output: 4 + Explanation: + Step 1) 8 is even; divide by 2 and obtain 4. + Step 2) 4 is even; divide by 2 and obtain 2. + Step 3) 2 is even; divide by 2 and obtain 1. + Step 4) 1 is odd; subtract 1 and obtain 0. + + Example 3: + Input: num = 123 + Output: 12 + + Constraints: 0 <= num <= 106 +*/ +class Solution { +public: + int numberOfSteps(int num) { + // We initialize the step counter. + int steps = 0; + // While the number is still not zero... + while (num != 0) { + // ...we check if it is odd or even. + // If it is even... + if (num % 2 == 0) { + // ...we divide it by 2 and increase the step counter. + num /= 2; + steps++; + // If it is odd... + } else { + // ...we substract 1 and increase the step counter. + num -= 1; + steps++; + } + } + // Finally, we return the step counter. + return steps; + } +}; diff --git a/Math/num_steps_reduce_to_zero.java b/Math/num_steps_reduce_to_zero.java new file mode 100644 index 00000000..5630ae9a --- /dev/null +++ b/Math/num_steps_reduce_to_zero.java @@ -0,0 +1,54 @@ +/* + Given an integer num, return the number of steps to reduce it to zero. + + In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. + + Example 1: + Input: num = 14 + Output: 6 + Explanation: + Step 1) 14 is even; divide by 2 and obtain 7. + Step 2) 7 is odd; subtract 1 and obtain 6. + Step 3) 6 is even; divide by 2 and obtain 3. + Step 4) 3 is odd; subtract 1 and obtain 2. + Step 5) 2 is even; divide by 2 and obtain 1. + Step 6) 1 is odd; subtract 1 and obtain 0. + + Example 2: + Input: num = 8 + Output: 4 + Explanation: + Step 1) 8 is even; divide by 2 and obtain 4. + Step 2) 4 is even; divide by 2 and obtain 2. + Step 3) 2 is even; divide by 2 and obtain 1. + Step 4) 1 is odd; subtract 1 and obtain 0. + + Example 3: + Input: num = 123 + Output: 12 + + Constraints: 0 <= num <= 106 +*/ +class Solution { + public int numberOfSteps(int num) { + // We initialize the step counter. + int steps = 0; + // While the number is still not zero... + while (num != 0) { + // ...we check if it is odd or even. + // If it is even... + if (num % 2 == 0) { + // ...we divide it by 2 and increase the step counter. + num /= 2; + steps++; + // If it is odd... + } else { + // ...we substract 1 and increase the step counter. + num -= 1; + steps++; + } + } + // Finally, we return the step counter. + return steps; + } +} diff --git a/Math/num_steps_reduce_to_zero.js b/Math/num_steps_reduce_to_zero.js new file mode 100644 index 00000000..28ba8951 --- /dev/null +++ b/Math/num_steps_reduce_to_zero.js @@ -0,0 +1,56 @@ +/* + Given an integer num, return the number of steps to reduce it to zero. + + In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. + + Example 1: + Input: num = 14 + Output: 6 + Explanation: + Step 1) 14 is even; divide by 2 and obtain 7. + Step 2) 7 is odd; subtract 1 and obtain 6. + Step 3) 6 is even; divide by 2 and obtain 3. + Step 4) 3 is odd; subtract 1 and obtain 2. + Step 5) 2 is even; divide by 2 and obtain 1. + Step 6) 1 is odd; subtract 1 and obtain 0. + + Example 2: + Input: num = 8 + Output: 4 + Explanation: + Step 1) 8 is even; divide by 2 and obtain 4. + Step 2) 4 is even; divide by 2 and obtain 2. + Step 3) 2 is even; divide by 2 and obtain 1. + Step 4) 1 is odd; subtract 1 and obtain 0. + + Example 3: + Input: num = 123 + Output: 12 + + Constraints: 0 <= num <= 106 +*/ +/** + * @param {number} num + * @return {number} + */ +var numberOfSteps = function (num) { + // We initialize the step counter. + let steps = 0; + // While the number is still not zero... + while (num != 0) { + // ...we check if it is odd or even. + // If it is even... + if (num % 2 == 0) { + // ...we divide it by 2 and increase the step counter. + num /= 2; + steps++; + // If it is odd... + } else { + // ...we substract 1 and increase the step counter. + num -= 1; + steps++; + } + } + // Finally, we return the step counter. + return steps; +}; diff --git a/Math/num_steps_reduce_to_zero.py b/Math/num_steps_reduce_to_zero.py new file mode 100644 index 00000000..2f4c4b70 --- /dev/null +++ b/Math/num_steps_reduce_to_zero.py @@ -0,0 +1,46 @@ +''' + Given an integer num, return the number of steps to reduce it to zero. + + In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. + + Example 1: + Input: num = 14 + Output: 6 + Explanation: + Step 1) 14 is even; divide by 2 and obtain 7. + Step 2) 7 is odd; subtract 1 and obtain 6. + Step 3) 6 is even; divide by 2 and obtain 3. + Step 4) 3 is odd; subtract 1 and obtain 2. + Step 5) 2 is even; divide by 2 and obtain 1. + Step 6) 1 is odd; subtract 1 and obtain 0. + + Example 2: + Input: num = 8 + Output: 4 + Explanation: + Step 1) 8 is even; divide by 2 and obtain 4. + Step 2) 4 is even; divide by 2 and obtain 2. + Step 3) 2 is even; divide by 2 and obtain 1. + Step 4) 1 is odd; subtract 1 and obtain 0. + + Example 3: + Input: num = 123 + Output: 12 + + Constraints: 0 <= num <= 106 +''' +class Solution(object): + def numberOfSteps(self, num): + """ + :type num: int + :rtype: int + """ + steps = 0 + while num != 0: + if num % 2 == 0: + num = num / 2 + steps = steps + 1 + else: + num = num - 1 + steps = steps + 1 + return steps diff --git a/Math/number_of_substrings_with_only_1s.js b/Math/number_of_substrings_with_only_1s.js new file mode 100644 index 00000000..f5624013 --- /dev/null +++ b/Math/number_of_substrings_with_only_1s.js @@ -0,0 +1,83 @@ +/* + PROBLEM STATEMENT: + + Given a binary string s, return the number of substrings with all characters 1's. Since the answer may be too large, return it modulo 109 + 7. + + EXAMPLE 1: + Input: s = "0110111" + Output: 9 + Explanation: There are 9 substring in total with only 1's characters. + "1" -> 5 times. + "11" -> 3 times. + "111" -> 1 time. + EXAMPLE 2: + Input: s = "101" + Output: 2 + Explanation: Substring "1" is shown 2 times in s. + + CONSTRAINT: + Constraints: + 1 <= s.length <= 105 + s[i] is either '0' or '1'. +*/ + +/* Explaination + + The function begins by initializing two variables - "count" and "answer" - to 0. + "count" will be used to keep track of the current number of consecutive 1's in the string, + while "answer" will eventually store the final answer. + +The constant "mod" is defined to be 10^9+7, which will be used to take modulo of the answer +at each step, in order to prevent overflow. + +The function then iterates through the input string using a for loop. +For each character in the string, it checks if it is a 1 or a 0. If it is a 1, +the "count" variable is incremented by 1. If it is a 0, the "count" variable is reset to 0. + +At each step of the loop, the value of "count" is added to "answer" and then taken modulo "mod". +This is done to count the number of substrings with all 1's and prevent overflow of the result. + +After the loop has finished, the final value of "answer" is returned as the solution to the problem. + + +*/ + + +var numSub = function(s) { + // count stores the current number of consecutive 1's + var count=0; + // answer storese the final answer to be returned + var answer=0; + // we mod the sum at each step so that they don't overflow + const mod=1000000007; + // iterate the entire string + for(let i=0;i= divisor) { + dividend -= divisor; + quotient++; + } + + if (negativeResult) { + quotient = -quotient; + } + + // Check for overflow + if (quotient < -(2**31) || quotient > 2**31 - 1) { + return 2**31 - 1; // Return INT_MAX for overflow + } + + return quotient; +} + +// Example usage: +const dividend = 10; +const divisor = 3; +const result = divide(dividend, divisor); +console.log(result); // Output should be 3 diff --git a/Math/palindrome_number.java b/Math/palindrome_number.java new file mode 100644 index 00000000..6e91df90 --- /dev/null +++ b/Math/palindrome_number.java @@ -0,0 +1,48 @@ +/* Question : Given an integer x, return true if x is a palindrome, and false otherwise. + +Example 1: + +Input: x = 121 +Output: true +Explanation: 121 reads as 121 from left to right and from right to left. +Example 2: + +Input: x = -121 +Output: false +Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. +Example 3: + +Input: x = 10 +Output: false +Explanation: Reads 01 from right to left. Therefore it is not a palindrome. + + +Constraints: + +-231 <= x <= 231 - 1 */ + +class Solution { + public boolean isPalindrome(int x) { + if(x < 0){ + return false; + } + + //Checking if the number is equal to its reverse, if true, it is a palindrome + return x == rev(x); + } + + + // Function to find reverse of a number + + public int reverse(int n){ + int reverse = 0; + while(n != 0){ + int digit = n % 10; + rev = digit + reverse * 10; + n /= 10; + } + + return reverse; + } +} + diff --git a/Math/pallindrome_number.cpp b/Math/pallindrome_number.cpp new file mode 100644 index 00000000..6f925fe6 --- /dev/null +++ b/Math/pallindrome_number.cpp @@ -0,0 +1,40 @@ +/* + Given an integer x, return true if x is a palindrome, and false otherwise. + + Example 1: + Input: x = 121 + Output: true + Explanation: 121 reads as 121 from left to right and from right to left. + + Example 2: + Input: x = -121 + Output: false + Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. + + Example 3: + Input: x = 10 + Output: false + + Explanation: Reads 01 from right to left. Therefore it is not a palindrome. + + + Constraints: + + -231 <= x <= 231 - 1 +*/ + +class Solution { +public: + bool isPalindrome(int x) { + int temp = x; + if(x < 0) + return false; + long long new_num = 0; + while(x) { + int rem = x % 10; + new_num = (new_num * 10) + rem; + x /= 10; + } + return new_num == temp; + } +}; \ No newline at end of file diff --git a/Math/pallindrome_number.py b/Math/pallindrome_number.py new file mode 100644 index 00000000..7b04f550 --- /dev/null +++ b/Math/pallindrome_number.py @@ -0,0 +1,36 @@ +''' + Given an integer x, return true if x is a + palindrome + , and false otherwise. + + Example 1: + Input: x = 121 + Output: true + Explanation: 121 reads as 121 from left to right and from right to left. + + Example 2: + Input: x = -121 + Output: false + Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. + + Example 3: + Input: x = 10 + Output: false + Explanation: Reads 01 from right to left. Therefore it is not a palindrome. + Constraints: + -231 <= x <= 231 - 1 + + Follow up: Could you solve it without converting the integer to a string? +''' +class Solution: + def isPalindrome(self, x: int) -> bool: + #check if the number is less than zero. + if x < 0: + return False + #Then, calculating the reverse of the number to see if it is equal to the original number. + rev = 0 + original_value = x + while x != 0: + rev = rev * 10 + x % 10 + x = x // 10 + return rev == original_value \ No newline at end of file diff --git a/Math/powXn.cpp b/Math/powXn.cpp new file mode 100644 index 00000000..07a355d8 --- /dev/null +++ b/Math/powXn.cpp @@ -0,0 +1,49 @@ +/* +Implement pow(x, n), which calculates x raised to the power n (i.e., xn). + + + +Example 1: + +Input: x = 2.00000, n = 10 +Output: 1024.00000 +Example 2: + +Input: x = 2.10000, n = 3 +Output: 9.26100 +Example 3: + +Input: x = 2.00000, n = -2 +Output: 0.25000 +Explanation: 2-2 = 1/22 = 1/4 = 0.25 + + +Constraints: + +-100.0 < x < 100.0 +-231 <= n <= 231-1 +n is an integer. +-104 <= xn <= 104 + +if it we solve it though simply by recursion it will give time complexity of O(n) + so we will use divide and conquer approach that leads to O(logn) time complexity + eg. 3^7 dive seven by two 333 * 333 3 than in 333 divide by two 3 * 3 3 + on conquering 93 * 93 than again conquer 27 * 27 * 3 final output is 218 +*/ + +class Solution { +public: + double pow(double x, int n) + { + if(x==0)return 0; + if(n==0)return 1; + double res=pow(x,n/2); + res=res*res; + if(n%2)res=x*res; + return res; + } + double myPow(double x, int n) { + if(n<0)return pow(1/x,n); + return pow(x,n); + } +}; \ No newline at end of file diff --git a/Math/powXn.js b/Math/powXn.js new file mode 100644 index 00000000..d867d1a3 --- /dev/null +++ b/Math/powXn.js @@ -0,0 +1,19 @@ +// Implement pow(x, n), which calculates x raised to the power n (i.e., xn). +// Time complexity: O(log n) | Space complexity: O(log n) + +function pow(x, n) { + // Any number raised to 0 is 1) + if (n === 0) return 1 + // Power of a negative number is the reciprocal of power of positive number + if (n < 0) return 1 / pow(x, -n) + // If n is even, calculate pow(x, n / 2) and return the square of it + if (n % 2 === 0) return pow(x, n / 2) * pow(x, n / 2) + // If n is odd, return x * pow(x, n - 1) (x raised to odd power n can be represented as x * x^(n-1)) + return x * pow(x, n - 1) +} + +// Sample inputs and outputs +console.log(pow(2, 10)) // 1024 +console.log(pow(2, -3)) // 0.125 +console.log(pow(3, 3)) // 27 +console.log(pow(3, 0)) // 1 \ No newline at end of file diff --git a/Math/prime_factorization.cpp b/Math/prime_factorization.cpp index 7e87cfe6..2d204776 100644 --- a/Math/prime_factorization.cpp +++ b/Math/prime_factorization.cpp @@ -1,48 +1,48 @@ -// Prime factorisation is a method to find the prime factors of a given number, say a composite number. -// These factors are nothing but the prime numbers. A prime number is a number -// which has only two factors, i.e. 1 and the number itself. For example, 2 is a prime number which has two factors, 2 × 1 -// Sample Input: 10 -// Output -// 2->1 Times -// 5->1 Times -#include -using namespace std; - -int f[100], expo[100], len = -1; -void prime_factor(int n){ - int d = 2; - if(n == 1){ - len++; - f[len] = 2; - expo[len] = 0; - return; - } - while(n > 1 && 1ll * d * d <= n){ - int k = 0; - while(n % d == 0){ - n = n / d; - k++; - } - if(k > 0){ - len++; - f[len] = d; - expo[len] = k; - } - d++; - } - if(n > 1){ - len++; - f[len] = n; - expo[len] = 1; - } -} -int main(){ - int n; - cin >> n; - prime_factor(n); - for(int i = 0; i <= len; i++){ - cout << f[i] << "->" << expo[i] << " Times" << endl; - } - -return 0; -} +// Prime factorisation is a method to find the prime factors of a given number, say a composite number. +// These factors are nothing but the prime numbers. A prime number is a number +// which has only two factors, i.e. 1 and the number itself. For example, 2 is a prime number which has two factors, 2 × 1 +// Sample Input: 10 +// Output +// 2->1 Times +// 5->1 Times +#include +using namespace std; + +int f[100], expo[100], len = -1; +void prime_factor(int n){ + int d = 2; + if(n == 1){ + len++; + f[len] = 2; + expo[len] = 0; + return; + } + while(n > 1 && 1ll * d * d <= n){ + int k = 0; + while(n % d == 0){ + n = n / d; + k++; + } + if(k > 0){ + len++; + f[len] = d; + expo[len] = k; + } + d++; + } + if(n > 1){ + len++; + f[len] = n; + expo[len] = 1; + } +} +int main(){ + int n; + cin >> n; + prime_factor(n); + for(int i = 0; i <= len; i++){ + cout << f[i] << "->" << expo[i] << " Times" << endl; + } + +return 0; +} diff --git a/Math/shuffle_an_array.cpp b/Math/shuffle_an_array.cpp new file mode 100644 index 00000000..413408af --- /dev/null +++ b/Math/shuffle_an_array.cpp @@ -0,0 +1,70 @@ +/* + Given an integer array nums, design an algorithm to randomly shuffle the array. All permutations of the array should be equally likely as a result of the shuffling. + + Implement the Solution class: + Solution(int[] nums) Initializes the object with the integer array nums. + int[] reset() Resets the array to its original configuration and returns it. + int[] shuffle() Returns a random shuffling of the array. + + Input + ["Solution", "shuffle", "reset", "shuffle"] + [[[1, 2, 3]], [], [], []] + Output + [null, [3, 1, 2], [1, 2, 3], [1, 3, 2]] + + Constraints: + > 1 <= nums.length <= 50 + > -106 <= nums[i] <= 106 + > All the elements of nums are unique. + > At most 104 calls in total will be made to reset and shuffle. +*/ + +/* + APPROACH + + 1. Use two lists to store the original nums array. + 2. We can keep one array as it is and return it whenever the reset() function is called. + 3. We use the second list to shuffle the permutation of elements and return a shuffled array when shuffle() function is called. + 4. For shuffling, choose 2 indices on random basis from the array and swap them. Repeat for n times. (n=size of the array) + +*/ +// O(N) Time-Complexity and O(N) Space-Complexity Solution +class Solution { +public: + // lets make 2 variables, one to store the shuffled version of the given array and the other to store the original version + vector shuffled; + vector original; + Solution(vector& nums) { + // Push the elements of given array to required variables as Initialization + for(int i=0;i reset() { + // Return the variable storing the original version of the array + return original; + } + + vector shuffle() { + int N=shuffled.size(); + // shuffle the variable storing the shuffled version of the array randomly + for(int i=0;i param_1 = obj->reset(); + * vector param_2 = obj->shuffle(); + */ \ No newline at end of file diff --git a/Math/shuffle_an_array.py b/Math/shuffle_an_array.py new file mode 100644 index 00000000..dbc4f8ac --- /dev/null +++ b/Math/shuffle_an_array.py @@ -0,0 +1,27 @@ +#Problem statement - Given an integer array nums, design an algorithm to randomly shuffle the array. +# All permutations of the array should be equally likely as a result of the shuffling. +# Complete the provided functions + +class Solution: + + def __init__(self, nums: List[int]): + #the value of nums is stored as an instance variable that can be accessed and used by other methods within the class + self.nums = nums + + + def reset(self) -> List[int]: + #returning the original array after reseting it + return self.nums + + def shuffle(self) -> List[int]: + s = [x for x in self.nums] + random.shuffle(s) + return s +# Time complexity - O(N), where N is the length of the input list self.nums +#Space compelxity - O(N) + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(nums) +# param_1 = obj.reset() +# param_2 = obj.shuffle() diff --git a/Math/sieve_of_eratosthenes.cpp b/Math/sieve_of_eratosthenes.cpp index 9d95ecdb..79b5a43b 100644 --- a/Math/sieve_of_eratosthenes.cpp +++ b/Math/sieve_of_eratosthenes.cpp @@ -1,61 +1,38 @@ -// In mathematics, the sieve of Eratosthenes is an ancient algorithm for finding all prime numbers up to any given limit. -// It does so by iteratively marking as composite (i.e., not prime) the multiples of each prime, -// starting with the first prime number, 2. The multiples of a given prime are generated as a sequence of numbers -// starting from that prime, with constant difference between them that is equal to that prime. -// This is the sieve's key distinction from using trial division to sequentially test each candidate -// number for divisibility by each prime. Once all the multiples of each discovered prime have been marked as composites, -// the remaining unmarked numbers are primes. Source(https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) - -// Sample INput: 100 -// Output : -// 3 -// 5 -// 7 -// 11 -// 13 -// 17 -// 19 -// 23 -// 29 -// 31 -// 37 -// 41 -// 43 -// 47 -// 53 -// 59 -// 61 -// 67 -// 71 -// 73 -// 79 -// 83 -// 89 -// 97 -#include -using namespace std; -const int nmax = 100001; -bool is_prime[nmax]; -void seive_of_ero(int n){ - for(int i = 2; i <= n; i++){ - is_prime[i] = true; - } - for(int i = 2; i <= n / 2; i++){ - if(is_prime[i]){ - for(int j = i * 2; j <= n; j += i){ - is_prime[j] = false; - } - } - } -} -int main(){ - int n; - cin >> n; - seive_of_ero(n); - for(int i = 0; i <= 500; i++){ - if(is_prime[i]) - cout << i << " " << endl; - } - -return 0; -} +// In mathematics, the sieve of Eratosthenes is an ancient algorithm for finding all prime numbers up to any given limit. +// It does so by iteratively marking as composite (i.e., not prime) the multiples of each prime, +// starting with the first prime number, 2. The multiples of a given prime are generated as a sequence of numbers +// starting from that prime, with constant difference between them that is equal to that prime. +// This is the sieve's key distinction from using trial division to sequentially test each candidate +// number for divisibility by each prime. Once all the multiples of each discovered prime have been marked as composites, +// the remaining unmarked numbers are primes. Source(https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) + +// Sample INput: 100 +// Output : 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 + +#include +using namespace std; +const int nmax = 100001; +bool is_prime[nmax]; +void seive_of_ero(int n){ + for(int i = 2; i <= n; i++){ + is_prime[i] = true; + } + for(int i = 2; i <= n / 2; i++){ + if(is_prime[i]){ + for(int j = i * 2; j <= n; j += i){ + is_prime[j] = false; + } + } + } +} +int main(){ + int n; + cin >> n; + seive_of_ero(n); + for(int i = 0; i <= 500; i++){ + if(is_prime[i]) + cout << i << " " << endl; + } + +return 0; +} diff --git a/Math/substrings_with_1s.py b/Math/substrings_with_1s.py new file mode 100644 index 00000000..f7981a13 --- /dev/null +++ b/Math/substrings_with_1s.py @@ -0,0 +1,31 @@ +'''Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Number of Substrings With Only 1s in Python +Issue Number : #518 +Problem statement : + +Explanation of the below Python code : + +The implementation is quite similar to the C++ implementation. We iterate over the string s using a for loop and keep track of the count of consecutive 1's using the variable count. Whenever we encounter a '0', we calculate the number of substrings that can be formed using the formula n*(n+1)/2, add it to the final answer ans, and reset the value of count to 0. Finally, we calculate the number of substrings for the last substring of consecutive 1's, add it to the final answer ans, and return the result modulo 10^9 + 7. Note that we have used the integer division operator // to perform the division in Python. + + + +''' + +----------------------------------------------------------------------------------------------//Python code begins here-------------------------------------------------------------------------------------------------------------------------------------- +class Solution: + def numSub(self, s: str) -> int: + count = 0 # count the number of consecutive ones + ans = 0 # variable to store the final answer + for i in range(len(s)): + if s[i] == '1': + count += 1 + else: + # calculate the number of possible substrings that can be formed + # from the current consecutive ones and add it to the final answer + ans = (ans + (count*(count+1))//2) % (10**9 + 7) + count = 0 + # handle the case when the string ends with consecutive ones + ans = (ans + (count*(count+1))//2) % (10**9 + 7) + return ans diff --git a/Math/unique_digits.cpp b/Math/unique_digits.cpp new file mode 100644 index 00000000..30a2bb5c --- /dev/null +++ b/Math/unique_digits.cpp @@ -0,0 +1,39 @@ +/*Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Count Numbers with Unique Digits in C++ +Issue Number : #500 +Problem statement : + +Explanation of the below C++ code : + +In this implementation, we first handle the base case of n = 0 by returning 1. Then, we initialize the answer ans to 10 since there are 10 unique digits between 0 and 9. We also initialize the variables unique_digits and available_digits to 9 since we can't use 0 as the first digit. + +Next, we enter a loop that runs n-1 times (since we have already considered the case of i = 1). In each iteration of the loop, we compute unique_digits as the product of the current value of unique_digits and available_digits. We then add unique_digits to the answer ans and decrement available_digits. This is because we can't use the digits that have already been used for the previous digits. + +Finally, we return the value of ans. + +The time complexity of this algorithm is O(n), where n is the input parameter representing the number of digits. + +The space complexity of this algorithm is O(1), as we are only using a constant amount of extra memory to store the variables ans, unique_digits, and available_digits, regardless of the input size. + +*/ +-------------------------------------------------------------------------//C++ code begins here---------------------------------------------------------------------------- + +class Solution { +public: + int countNumbersWithUniqueDigits(int n) { + if (n == 0) { + return 1; // return 1 for n = 0 + } + int ans = 10; // start with 10 unique digits, as we can have numbers 0-9 + int unique_digits = 9; // start with 9 digits, as we cannot use 0 as first digit + int available_digits = 9; // remaining available digits + while (n-- > 1 && available_digits > 0) { + unique_digits *= available_digits; // calculate number of unique numbers that can be formed + ans += unique_digits; // add number of unique numbers to the answer + available_digits--; // reduce available digits by 1 + } + return ans; // return final answer + } +}; diff --git a/Math/unique_digits.java b/Math/unique_digits.java new file mode 100644 index 00000000..aa1b7fc2 --- /dev/null +++ b/Math/unique_digits.java @@ -0,0 +1,43 @@ +/*Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Count Numbers with Unique Digits in Java +Issue Number : #501 +Problem statement : + +Explanation of the below Java code : + +We handle the base case of n = 0 by returning 1. We initialize the answer ans to 10 since there are 10 unique digits between 0 and 9. We also initialize the variables unique_digits and available_digits to 9 since we can't use 0 as the first digit. + +We then enter a loop that runs n-1 times (since we have already considered the case of i = 1). In each iteration of the loop, we compute unique_digits as the product of the current value of unique_digits and available_digits. We then add unique_digits to the answer ans and decrement available_digits. This is because we can't use the digits that have already been used for the previous digits. + +Finally, we return the value of ans. + +The time complexity of the given code is O(n), where n is the input parameter, because the loop runs n-1 times. + +The space complexity is O(1), because the amount of memory used by the algorithm does not depend on the size of the input n. The algorithm only uses a few constant amount of variables for computation, such as ans, unique_digits and available_digits. + +Therefore, the time complexity of the code is linear and the space complexity is constant. + + +*/ + +--------------------------------------------------------------------------------------------------------//Java code begins here--------------------------------------------------------------------------------------------------------------------------------- + + +class Solution { + public int countNumbersWithUniqueDigits(int n) { + if (n == 0) { + return 1; // return 1 for n = 0 + } + int ans = 10; // start with 10 unique digits, as we can have numbers 0-9 + int unique_digits = 9; // start with 9 digits, as we cannot use 0 as first digit + int available_digits = 9; // remaining available digits + while (n-- > 1 && available_digits > 0) { + unique_digits *= available_digits; // calculate number of unique numbers that can be formed + ans += unique_digits; // add number of unique numbers to the answer + available_digits--; // reduce available digits by 1 + } + return ans; // return final answer + } +} diff --git a/Math/unique_digits.py b/Math/unique_digits.py new file mode 100644 index 00000000..ef4b25c7 --- /dev/null +++ b/Math/unique_digits.py @@ -0,0 +1,31 @@ +''' + Given an integer n, return the count of all numbers with unique digits, x, where 0 <= x < 10n. + + Example 1: + Input: n = 2 + Output: 91 + Explanation: The answer should be the total numbers in the range of 0 ≤ x < 100, excluding 11,22,33,44,55,66,77,88,99 + + Example 2: + Input: n = 0 + Output: 1 + + Constraints: + 0 <= n <= 8 +''' +class Solution: + def countNumbersWithUniqueDigits(self, n: int) -> int: + '''For n = 0, ans = 1 + For n = 1, ans = 10 + If we take 2 digit number, we have 9 options for first digit (1 - 9) + and 9 options for second digit(0 & all other digits except the one taken as first digit (to keep digits unique)) therefore ans += (9 * 9) + Similarly if we take 3 digit number we have 8 options for third digit, therefore ans += (9 * 9 * 8)''' + if(n==0): return 1 + if(n==1): return 10 + unique = 10 + digits = 9 + for i in range(2,n+1): + digits*=(10-i+1) + unique+=digits + return unique + \ No newline at end of file diff --git a/Math/unique_integers_that_sum_up_to_0.cpp b/Math/unique_integers_that_sum_up_to_0.cpp new file mode 100644 index 00000000..982fe3b0 --- /dev/null +++ b/Math/unique_integers_that_sum_up_to_0.cpp @@ -0,0 +1,43 @@ +/* + Given an integer n, return any array containing n unique integers such that they add up to 0. + + Example 1: + Input: n = 5 + Output: [-7,-1,1,3,4] + Explanation: These arrays also are accepted [-5,-1,1,2,3] , [-3,-1,2,-2,4]. + + Example 2: + Input: n = 3 + Output: [-1,0,1] + + Example 3: + Input: n = 1 + Output: [0] + + Constraints: + 1 <= n <= 1000 +*/ + +class Solution { +public: + vector sumZero(int n) { + int A[n]; + for(int i = 0; i < n; i++){ A[i] = 0; } + int start = 0; + int end = n - 1; + int i = 1; + // populate start and end with n and -n + while(start < end) { + A[start] = i; + A[end] = -i; + i++; + start++; + end--; + } + vector R; + for(int a: A){ + R.push_back(a); + } + return R; + } +}; \ No newline at end of file diff --git a/Misc/tictactoe.java b/Misc/tictactoe.java new file mode 100644 index 00000000..3d2bd98c --- /dev/null +++ b/Misc/tictactoe.java @@ -0,0 +1,118 @@ +/* + This Java code defines a simple command-line Tic-Tac-Toe game that allows two players (X and O) to take turns making moves on a 3x3 game board. Here's an explanation of the code: + +1. Constants: + - `X` and `O` are integer constants representing the two players, with `X` assigned the value `1` and `O` assigned the value `-1`. + - `EMPTY` is an integer constant representing an empty cell on the game board, with a value of `0`. + +2. Game Board: + - The game board is represented by a 3x3 integer array called `board`. + - The `player` variable indicates which player's turn it is. It is initially set to `X`. + +3. Constructor: + - The `TicTacToe` class has a constructor that initializes a new game by clearing the board and setting the `player` to `X`. + +4. `clearBoard` Method: + - The `clearBoard` method initializes the game board by setting all cells to `EMPTY` and resets the `player` to `X`. + +5. `putMark` Method: + - The `putMark` method allows a player to make a move at a specified position (i, j). + - It checks for valid coordinates and an empty cell. If the move is valid, the player's mark is placed, and the turn is switched to the other player. + - If the move is invalid, an `IllegalArgumentException` is thrown. + +6. `isWin` Method: + - The `isWin` method checks if a player has won the game. It returns `true` if any of the winning conditions (e.g., a row, column, or diagonal with all marks of the same player) are met. Otherwise, it returns `false`. + +7. `winner` Method: + - The `winner` method determines the winner of the game. It checks for both `X` and `O` as potential winners using the `isWin` method and returns the result (positive for `X`, negative for `O`, or `0` for a tie). + +8. `toString` Method: + - The `toString` method converts the current game state to a human-readable string, representing the game board. It uses "X" and "O" to denote player marks and empty spaces as " " (space). + +9. `main` Method: + - In the `main` method, a new Tic-Tac-Toe game is created. + - A series of moves are made using `putMark`, and the game state is printed after each move. + - The final outcome of the game is determined using the `winner` method, and the result is displayed as "X wins," "O wins," or "Tie." + +The code provides a basic implementation of a command-line Tic-Tac-Toe game with validation for player moves and win conditions. It demonstrates how to represent the game board, make moves, and check for a win or tie in the game. + */ +public class TicTacToe { + public static final int X = 1, O = -1; + public static final int EMPTY = 0; + private int board[][] = new int[3][3]; + private int player; + + public TicTacToe() { + clearBoard(); + } + public void clearBoard() { + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + board[i][j] = EMPTY; + player = X; + } + + public void putMark(int i, int j) throws IllegalArgumentException { + if ((i < 0) || (i > 2) || (j < 0) || (j > 2)) + throw new IllegalArgumentException("Invalid board position"); + if (board[i][j] != EMPTY) + throw new IllegalArgumentException("Board Position occupied"); + board[i][j] = player; + player = -player; // switch players + } + + public boolean isWin(int mark) { + return ((board[0][0] + board[0][1] + board[0][2] == mark * 3) + || (board[1][0] + board[1][1] + board[1][2] == mark * 3) + || (board[2][0] + board[2][1] + board[2][2] == mark * 3) + || (board[0][0] + board[1][0] + board[2][0] == mark * 3) + || (board[0][1] + board[1][1] + board[2][1] == mark * 3) + || (board[0][2] + board[1][2] + board[2][2] == mark * 3) + || (board[0][0] + board[1][1] + board[2][2] == mark * 3) + || (board[2][0] + board[1][1] + board[0][2] == mark * 3) + ); + } + public int winner() { + if(isWin(X)) + return (X); + else if (isWin(O)) + return (O); + else + return (0); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + switch (board[i][j]) { + case X: sb.append("X"); break; + case O: sb.append("O"); break; + case EMPTY: sb.append(" "); break; + } + if (j < 2) sb.append("|"); + } + if(i < 2) sb.append("\n-----\n"); + } + return sb.toString(); + } + + public static void main(String[] args) { + TicTacToe game = new TicTacToe(); + game.putMark(0, 0); game.putMark(0, 2); + game.putMark(2, 2); game.putMark(1, 2); + game.putMark(2, 0); game.putMark(1, 1); + game.putMark(1, 0); + + + System.out.println(game); + int winningPlayer = game.winner(); + String[] outcome = {"O wins", "Tie", "X wins"}; + System.out.println(outcome[1 + winningPlayer]); + } + + +} + { + +} diff --git a/OOP/basic.cpp b/OOP/basic.cpp deleted file mode 100644 index fe8f4a42..00000000 --- a/OOP/basic.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -class Car{ - public: - int price; - int model_no; - char name[20]; -}; -int main(){ - Car C; - cout << sizeof(C) << "\n"; - cout << sizeof(Car); - return 0; -} \ No newline at end of file diff --git a/Patterns/CompleteSquare.java b/Patterns/CompleteSquare.java new file mode 100644 index 00000000..9bae7b0f --- /dev/null +++ b/Patterns/CompleteSquare.java @@ -0,0 +1,27 @@ +/* + pattern to print: +for input:5 + * * * * * + * * * * * + * * * * * + * * * * * + * * * * * + + APPROACH: + 1) Identify the numbers of rows for the outer for loop + 2) Identify the relation of column with respect to row. + 3) for each loop , print the respective element. + */ +public class CompleteSquare { + + public static void main(String[] args) { + int size = 5; + for(int row=1;row <= size;row++) { + for (int col = 1;col<=size;col++) { + System.out.print("* "); + } + System.out.println(); + } + } +} + diff --git a/Patterns/HollowPattern.cpp b/Patterns/HollowPattern.cpp new file mode 100644 index 00000000..a3ad90f3 --- /dev/null +++ b/Patterns/HollowPattern.cpp @@ -0,0 +1,53 @@ +Program: To generate a hollow pattern using asterisks ('*'). The program will take an integer 'n' as input, which represents the size of the pattern, and it will produce a hollow pattern of the given size. + +Approach: +1. We will use nested loops to iterate through rows and columns and print '*' at specific positions to form the hollow pattern. +2. For each row, we will check if it's the first row, last row, first column, or last column. If so, we will print a '*' character. Otherwise, we will print a space ' '. +3. This way, we will create the desired hollow pattern. + +Time Complexity: O(n^2) +- The program uses two nested loops to print the pattern, one for rows and one for columns. So, the time complexity is proportional to the square of 'n'. + +Space Complexity: O(1) +- The program uses a constant amount of extra space, as it only prints characters and does not use any additional data structures that depend on the input size 'n'. + +Sample Input: 5 +Sample Output: +* * * * * +* * +* * +* * +* * * * * + +In this example, the input 'n' is 5, and the program generates a hollow pattern of size 5x5 using asterisks. +*/ + +#include +using namespace std; + +void printHollowPattern(int n) { + // Iterate through each row + for (int i = 0; i < n; i++) { + // Iterate through each column in the row + for (int j = 0; j < n; j++) { + // Check if it's the first row, last row, first column, or last column + if (i == 0 || i == n - 1 || j == 0 || j == n - 1) { + cout << "* "; + } else { + cout << " "; + } + } + // Move to the next line after each row is printed + cout << endl; + } +} + +int main() { + int size; + cout << "Enter the size of the pattern: "; + cin >> size; + + printHollowPattern(size); + return 0; +} + diff --git a/Patterns/HollowPattern.go b/Patterns/HollowPattern.go new file mode 100644 index 00000000..d6fd1d24 --- /dev/null +++ b/Patterns/HollowPattern.go @@ -0,0 +1,54 @@ +/* +Program: To generate a hollow pattern using asterisks ('*'). The program will take an integer 'n' as input, which represents the size of the pattern, and it will produce a hollow pattern of the given size. + +Approach: +1. We will use nested loops to iterate through rows and columns and print '*' at specific positions to form the hollow pattern. +2. For each row, we will check if it's the first row, last row, first column, or last column. If so, we will print a '*' character. Otherwise, we will print a space ' '. +3. This way, we will create the desired hollow pattern. + +Time Complexity: O(n^2) +- The program uses two nested loops to print the pattern, one for rows and one for columns. So, the time complexity is proportional to the square of 'n'. + +Space Complexity: O(1) +- The program uses a constant amount of extra space, as it only prints characters and does not use any additional data structures that depend on the input size 'n'. + +Sample Input: 5 +Sample Output: +* * * * * +* * +* * +* * +* * * * * + +In this example, the input 'n' is 5, and the program generates a hollow pattern of size 5x5 using asterisks. +*/ + +package main + +import "fmt" + +func printHollowPattern(n int) { + // Iterate through each row + for i := 0; i < n; i++ { + // Iterate through each column in the row + for j := 0; j < n; j++ { + // Check if it's the first row, last row, first column, or last column + if i == 0 || i == n-1 || j == 0 || j == n-1 { + fmt.Print("* ") + } else { + fmt.Print(" ") + } + } + // Move to the next line after each row is printed + fmt.Println() + } +} + +func main() { + var size int + fmt.Print("Enter the size of the pattern: ") + fmt.Scan(&size) + + printHollowPattern(size) +} + diff --git a/Patterns/HollowPattern.java b/Patterns/HollowPattern.java new file mode 100644 index 00000000..bcede112 --- /dev/null +++ b/Patterns/HollowPattern.java @@ -0,0 +1,53 @@ +/* +Program: To generate a hollow pattern using asterisks ('*'). The program will take an integer 'n' as input, which represents the size of the pattern, and it will produce a hollow pattern of the given size. + +Approach: +1. We will use nested loops to iterate through rows and columns and print '*' at specific positions to form the hollow pattern. +2. For each row, we will check if it's the first row, last row, first column, or last column. If so, we will print a '*' character. Otherwise, we will print a space ' '. +3. This way, we will create the desired hollow pattern. + +Time Complexity: O(n^2) +- The program uses two nested loops to print the pattern, one for rows and one for columns. So, the time complexity is proportional to the square of 'n'. + +Space Complexity: O(1) +- The program uses a constant amount of extra space, as it only prints characters and does not use any additional data structures that depend on the input size 'n'. + +Sample Input: 6 +Sample Output: +* * * * * * +* * +* * +* * +* * +* * * * * * + +*/ + +import java.util.Scanner; + +public class HollowPattern { + public static void printHollowPattern(int n) { + // Iterate through each row + for (int i = 0; i < n; i++) { + // Iterate through each column in the row + for (int j = 0; j < n; j++) { + // Check if it's the first row, last row, first column, or last column + if (i == 0 || i == n - 1 || j == 0 || j == n - 1) { + System.out.print("* "); + } else { + System.out.print(" "); + } + } + // Move to the next line after each row is printed + System.out.println(); + } + } + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + System.out.print("Enter the size of the pattern: "); + int size = scanner.nextInt(); + + printHollowPattern(size); + } +} diff --git a/Patterns/HollowPattern.py b/Patterns/HollowPattern.py new file mode 100644 index 00000000..03a33b40 --- /dev/null +++ b/Patterns/HollowPattern.py @@ -0,0 +1,52 @@ +''' +Problem: Python program to generate a hollow pattern using asterisks ('*'). The program will take an integer 'n' as input, which represents the size of the pattern, and it will produce a hollow pattern of the given size. + +Approach: +1. We will iterate through rows and columns and print '*' at specific positions to form the hollow pattern. +2. For each row, we will check if it's the first row, last row, first column, or last column. If so, we will print a '*' character. Otherwise, we will print a space ' '. +3. This way, we will create the desired hollow pattern. + +Time Complexity: O(n^2) +- The program uses two nested loops to print the pattern, one for rows and one for columns. So, the time complexity is proportional to the square of 'n'. + +Space Complexity: O(1) +- The program uses a constant amount of extra space, as it only prints characters and does not use any additional data structures that depend on the input size 'n'. + +Sample Input: 8 +Sample Output: +``` +* * * * * * * * +* * +* * +* * +* * +* * +* * +* * * * * * * * +``` + +In this example, the input 'n' is 8, and the program generates a hollow pattern of size 8x8 using asterisks. +''' + +def print_hollow_pattern(n): + # Iterate through each row + for i in range(n): + # Iterate through each column in the row + for j in range(n): + # Check if it's the first row, last row, first column, or last column + if i == 0 or i == n - 1 or j == 0 or j == n - 1: + print('*', end=' ') + else: + print(' ', end=' ') + # Move to the next line after each row is printed + print() + +# Sample input: n = 5 +# Expected output: +# * * * * * +# * * +# * * +# * * +# * * * * * +print_hollow_pattern(5) + diff --git a/Patterns/Ladder_Pattern.java b/Patterns/Ladder_Pattern.java new file mode 100644 index 00000000..4ef6518b --- /dev/null +++ b/Patterns/Ladder_Pattern.java @@ -0,0 +1,57 @@ +/* +Problem: Given an integer N, the task is to print the ladder with N steps using ‘*’. The ladder will be with the gap of 3 spaces between two side rails. + +Approach: +First, we take input from the user for the number of steps in the ladder. +We then iterate from 1 to N, where N is the number of steps, to print each step of the ladder. +Inside the loop, we call the printSideRail() function to print a side rail at the beginning of each step. +We then call the printStep(stepNumber) function to print the actual step. This function prints stepNumber asterisks with 3 spaces between each asterisk. +Finally, we again call the printSideRail() function to print a side rail at the end of each step and move to the next line to start a new step. + +Note: The number of spaces between the asterisks in each step can be adjusted by modifying the value inside the 'printSideRail()' and 'printStep(stepNumber)' functions. + +Time complexity: O(N) for given input N steps +Auxiliary Space: O(1) as constant extra space is used + +*/ + +import java.util.Scanner; + +public class LadderPattern { + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + System.out.print("Enter the number of steps in the ladder: "); + int N = sc.nextInt(); + + // Print the ladder with N steps + for (int i = 1; i <= N; i++) { + printSideRail(); + printStep(i); + printSideRail(); + System.out.println(); + } + } + + // Function to print a side rail + public static void printSideRail() { + System.out.print("* "); + } + + // Function to print a step + public static void printStep(int stepNumber) { + for (int i = 1; i <= stepNumber; i++) { + System.out.print("* "); + } + } +} + +/* + +Enter the number of steps in the ladder: 5 +* * +* * * +* * * * +* * * * * +* * * * * * + +*/ diff --git a/Patterns/ReverseRightTrianglePattern.java b/Patterns/ReverseRightTrianglePattern.java new file mode 100644 index 00000000..5b63df1d --- /dev/null +++ b/Patterns/ReverseRightTrianglePattern.java @@ -0,0 +1,34 @@ +/* + Reverse Right Triangular Pattern + Approach: + 1) Identify the numbers of rows for the outer for loop + 2) Identify the relation of column with respect to row. + In this case col = size-row+1 + 3) for each loop , print the respective element. + + TIME COMPLEXITY: O(N^2) + SPACE COMPLEXITY: O(1) + Eg: For size = 5, + OUTPUT: + * * * * * + * * * * + * * * + * * + * + */ +public class ReverseRightTrianglePattern { + + public static void main(String[] args) { + int size = 5; + pattern(size); + } + + public static void pattern(int size) { + for(int row=1;row<=size;row++) { + for(int col=1;col<=size-row+1;col++) { + System.out.print("* "); + } + System.out.println(); + } + } +} \ No newline at end of file diff --git a/Patterns/RightPascalTriangle.java b/Patterns/RightPascalTriangle.java new file mode 100644 index 00000000..06c02079 --- /dev/null +++ b/Patterns/RightPascalTriangle.java @@ -0,0 +1,38 @@ +/* + Right Pascal Triangle pattern in java + Approach: + 1) Identify the numbers of rows for the outer for loop + 2) Identify the relation of column with respect to row. + In this case col = size-row+1 + 3) for each loop , print the respective element. + + TIME COMPLEXITY: O(N^2) + SPACE COMPLEXITY: O(1) + Eg: For size = 5, + OUTPUT: + * + * * + * * * + * * * * + * * * + * * + * + */ +public class RightPascalTriangle { + + public static void main(String[] args) { + int size = 4; + pattern(size); + } + + public static void pattern(int size) { + for(int row=0;row< 2*size-1;row++){ + int totalcol = row < size?row:2*size-row-2; + for(int col =0;col <=totalcol;col++){ + System.out.print("* "); + } + System.out.println(); + } + } +} + diff --git a/Patterns/StarDiamond.java b/Patterns/StarDiamond.java new file mode 100644 index 00000000..487b8252 --- /dev/null +++ b/Patterns/StarDiamond.java @@ -0,0 +1,38 @@ +/* + DIAMOND PATTERN + * + * * + * * * + * * * * + * * * + * * + * + + APPROACH: + 1) Identify the numbers of rows for the outer for loop + 2) Identify the relation of column with respect to row. + 3) for each loop , print the respective element. + */ +public class StarDiamond { + + public static void main(String[] args) { + int size = 5; + pattern(size); + } + + public static void pattern(int size) { + for (int row=1;row< 2*size;row++) { + int totalcolsinrow = row <= size ? row:2*size-row; + int spaces = size-totalcolsinrow; + + for(int i =1;i<=spaces;i++) { + System.out.print(" "); + } + for(int col =1;col<=totalcolsinrow;col++) { + System.out.print("* "); + } + System.out.println(); + } + } +} + diff --git a/Patterns/diamond_pattern.cpp b/Patterns/diamond_pattern.cpp new file mode 100644 index 00000000..fad0b218 --- /dev/null +++ b/Patterns/diamond_pattern.cpp @@ -0,0 +1,78 @@ +/* +Approach: +1. We use two nested loops to iterate through each row and each column of the diamond pattern. +2. The first loop is used to iterate through the rows of the diamond. It starts from 0 and goes up to n-1 rows for the upper half of the diamond. +3. Inside the first loop, we use a nested loop to print spaces before the pattern of each row. The number of spaces decreases by 1 as we move down the rows. +4. Again inside the first loop, we use another nested loop to print the pattern of asterisks (*) for each row. The number of asterisks increases by 2 as we move down the rows. +5. After printing each row, we move to the next line using `cout << endl;`. +6. Now, we need to print the lower half of the diamond. For this, we use another loop that starts from n-2 (as we have already printed the top-most row in the first loop) and moves down to 0 rows. +7. Inside this loop, we print the spaces and pattern the same way as in the first loop. +8. Finally, we have the main function where we take input from the user for the number of rows and call the `printDiamondPattern` function. + +Time Complexity: O(n^2) - as we use nested loops to iterate through each row and each column of the diamond pattern. + +Space Complexity: O(1) - as we only use a constant amount of additional space for variables. + +Sample Input: +Enter the number of rows: 5 + +Sample Output: + * + *** + ***** + ******* +********* + ******* + ***** + *** + * +*/ + + +#include +using namespace std; + +void printDiamondPattern(int n) { + // Print the upper half of the diamond + for (int i = 0; i < n; i++) { + // Print spaces before the pattern for each row + for (int j = 0; j < n - i - 1; j++) { + cout << " "; + } + + // Print the pattern for each row + for (int j = 0; j < 2 * i + 1; j++) { + cout << "*"; + } + + cout << endl; // Move to the next line + } + + // Print the lower half of the diamond + for (int i = n-2; i >= 0; i--) { + // Print spaces before the pattern for each row + for (int j = 0; j < n - i - 1; j++) { + cout << " "; + } + + // Print the pattern for each row + for (int j = 0; j < 2 * i + 1; j++) { + cout << "*"; + } + + cout << endl; // Move to the next line + } +} + +int main() { + int n; + cout << "Enter the number of rows: "; + cin >> n; + + printDiamondPattern(n); + + return 0; +} + + + diff --git a/Patterns/diamond_pattern.go b/Patterns/diamond_pattern.go new file mode 100644 index 00000000..629f7389 --- /dev/null +++ b/Patterns/diamond_pattern.go @@ -0,0 +1,74 @@ +/* +In this code, the user enters a number, and the program will print a diamond pattern with that number as the widest point. + +Explanation of the approach: +- We define two helper functions: `printSpaces` and `printStars`. These functions are used to print the desired spaces and stars, respectively. +- The `printDiamondPattern` function takes an integer `n` as input. It prints the upper half and lower half of the diamond pattern using a combination of spaces and stars. +- In the upper half, the number of spaces in each row decreases by 1, while the number of stars increases by 2. +- In the lower half, the number of spaces in each row increases by 1, while the number of stars decreases by 2. +- The `main` function prompts the user to enter a number and calls the `printDiamondPattern` function to print the diamond pattern. + +Time Complexity: The time complexity of this code is O(n^2), where n is the number entered by the user. This is because there are two nested loops used to print the diamond pattern. + +Space Complexity: The space complexity of this code is O(1), as there are no additional data structures used that depend on the input size. + +Sample Input: +Enter a number for the widest point of the diamond: 5 + +Sample Output: + * + *** + ***** + ******* +********* + ******* + ***** + *** + * + +*/ + + +package main + +import ( + "fmt" + "math" +) + +func printSpaces(num int) { + for i := 1; i <= num; i++ { + fmt.Print(" ") + } +} + +func printStars(num int) { + for i := 1; i <= num; i++ { + fmt.Print("* ") + } +} + +func printDiamondPattern(n int) { + // upper half of the diamond + for i := 0; i < n; i++ { + printSpaces(n - i - 1) + printStars(2*i + 1) + fmt.Println() + } + + // lower half of the diamond + for i := 0; i < n-1; i++ { + printSpaces(i + 1) + printStars(2*(n-i-1) - 1) + fmt.Println() + } +} + +func main() { + var num int + fmt.Print("Enter a number for the widest point of the diamond: ") + fmt.Scan(&num) + + printDiamondPattern(num) +} + diff --git a/Patterns/diamond_pattern.java b/Patterns/diamond_pattern.java new file mode 100644 index 00000000..e712a138 --- /dev/null +++ b/Patterns/diamond_pattern.java @@ -0,0 +1,61 @@ +/* +Approach: +1. We take the number of rows for the diamond pattern as input. +2. We start by printing the upper half of the diamond pattern. In each row, we first print the required spaces and then print the required number of asterisks. +3. After the upper half, we print the lower half of the diamond pattern using a similar logic. + +Time Complexity: The time complexity of this code is O(n^2) as we use nested loops to print the pattern. + +Space Complexity: The space complexity of this code is O(1) as we are not using any additional data structures. + +Sample Input: 5 + +Sample Output: + * + *** + ***** + ******* +********* + ******* + ***** + *** + * +*/ + +public class DiamondPattern { + + public static void main(String[] args) { + int n = 5; // Specify the number of rows for the diamond pattern + printDiamondPattern(n); + } + + public static void printDiamondPattern(int n) { + if (n <= 0) { + System.out.println("Number of rows should be positive."); + return; + } + + // Printing upper half of the diamond pattern + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n - i; j++) { + System.out.print(" "); + } + for (int k = 1; k <= i * 2 - 1; k++) { + System.out.print("*"); + } + System.out.println(); + } + + // Printing lower half of the diamond pattern + for (int i = n - 1; i >= 1; i--) { + for (int j = 1; j <= n - i; j++) { + System.out.print(" "); + } + for (int k = 1; k <= i * 2 - 1; k++) { + System.out.print("*"); + } + System.out.println(); + } + } + + diff --git a/Patterns/diamond_pattern.py b/Patterns/diamond_pattern.py new file mode 100644 index 00000000..c3850abe --- /dev/null +++ b/Patterns/diamond_pattern.py @@ -0,0 +1,52 @@ +''' +Approach: +- The code above prints a diamond pattern with `n` rows. The approach used is to iterate through each row of the diamond pattern, calculate the number of spaces and asterisks in each row based on the current row number, and then print the spaces and asterisks accordingly. +- In the upper half of the diamond, the number of spaces decreases by 1 and the number of asterisks increases by 2 in each row. In the lower half of the diamond, the number of spaces increases by 1 and the number of asterisks decreases by 2 in each row. + +Sample input: `4`, the function `print_diamond_pattern(4)` + +Sample Output: + * + *** + ***** +******* + ***** + *** + * + +Time Complexity: The time complexity of this code is O(n^2), where n is the input parameter representing the number of rows in the diamond pattern. This is because we need to loop through each row and perform a constant number of operations for each row. + +Space Complexity: The space complexity of this code is O(1), as we are not using any additional data structures whose space requirements depend on the input size. + +NOTE: This code assumes that the input `n` is a positive integer greater than 0. It does not handle invalid inputs or edge cases where `n` is negative or zero. +''' + +def print_diamond_pattern(n): + # Calculate the number of rows in the diamond pattern + num_rows = 2 * n - 1 + + # Iterate through each row of the diamond pattern + for i in range(num_rows): + + # Calculate the number of spaces and asterisks in each row + if i < n: # Upper half of the diamond + num_spaces = n - i - 1 + num_asterisks = 2 * i + 1 + else: # Lower half of the diamond + num_spaces = i - n + 1 + num_asterisks = 2 * (num_rows - i) - 1 + + # Print the spaces before the asterisks + for _ in range(num_spaces): + print(" ", end="") + + # Print the asterisks + for _ in range(num_asterisks): + print("*", end="") + + # Move to the next line + print() + +# Testing the function with a sample input +print_diamond_pattern(4) + diff --git a/Patterns/numerical_pattern.java b/Patterns/numerical_pattern.java new file mode 100644 index 00000000..bcde260c --- /dev/null +++ b/Patterns/numerical_pattern.java @@ -0,0 +1,49 @@ +/* +Problem: +This program prints a pattern of numbers in an organized way. +The numbers start from 1 and increase in a triangular pattern. +Each row in the pattern contains the numbers from 1 to the row number. +The row numbers increase from 1 to the given input number. + +Approach: +- Use nested loops to iterate through each row and each number within the row. +- The outer loop controls the row number and runs from 1 to the input number. +- The inner loop prints the numbers for each row and runs from 1 to the row number. +- Print each number followed by a space. +- After each row is printed, insert a new line. + +Time Complexity: O(n^2), where n is the given input number. This is because we have nested loops that iterate n times. +Space Complexity: O(1) as no extra space is required other than the given input. + +Sample Input 1: +5 +Sample Output 1: +1 +1 2 +1 2 3 +1 2 3 4 +1 2 3 4 5 + +Sample Input 2: +3 +Sample Output 2: +1 +1 2 +1 2 3 +*/ + +public class NumericalPattern { + public static void printPattern(int n) { + for (int i = 1; i <= n; i++) { // iterate through each row + for (int j = 1; j <= i; j++) { // iterate through each number within the row + System.out.print(j + " "); // print the number followed by a space + } + System.out.println(); // insert a new line after each row + } + } + + public static void main(String[] args) { + int n = 5; // sample input + printPattern(n); + } +} diff --git a/Patterns/triangular_pattern.java b/Patterns/triangular_pattern.java new file mode 100644 index 00000000..ca32f798 --- /dev/null +++ b/Patterns/triangular_pattern.java @@ -0,0 +1,43 @@ +/* +Approach: +- Initialize the `rows` variable to determine the number of rows in the pattern. +- Initialize the `count` variable to keep track of the current number. +- Use nested `for` loops to iterate through the rows and columns. +- Print the current `count` value and increment it after printing. +- Move to the next line after each row by using `System.out.println()`. + +Time Complexity: The nested loops iterate through the rows and columns of the pattern, resulting in a time complexity of O(rows^2). + +Space Complexity: The space complexity is O(1) as we only require a few variables to store the number of rows, current count, and loop indices. + +Sample Input (rows = 5): +5 + +Sample Output: +1 +2 3 +4 5 6 +7 8 9 10 +11 12 13 14 15 +*/ + +//Java code for generating a number pattern in a triangular pattern, starting from 1: + +public class triangular_pattern { + public static void main(String[] args) { + int rows = 5; // number of rows in the pattern + + int count = 1; // variable to keep track of the current number + + // loop through the rows + for (int i = 1; i <= rows; i++) { + // loop through the columns + for (int j = 1; j <= i; j++) { + System.out.print(count + " "); + count++; + } + System.out.println(); // move to the next line after each row + } + } +} + diff --git a/Patterns/triangular_pattern.py b/Patterns/triangular_pattern.py new file mode 100644 index 00000000..85b788e1 --- /dev/null +++ b/Patterns/triangular_pattern.py @@ -0,0 +1,42 @@ +''' +Explanation: +1. The code defines a function `print_triangular_pattern()` that takes a parameter `n`. This parameter determines the number of rows in the triangular pattern. +2. Inside the function, `num` is initialized to 1, which is the starting number of the pattern. +3. The code then uses nested loops. The outer loop runs from 1 to `n`, which represents the number of rows. +4. The inner loop runs from 1 to the current row number (i). It prints the numbers in each row in increasing order. +5. After printing all the numbers in a row, the code moves to the next line using `print()` to create a new row. +6. Ultimately, the pattern is printed as a set of rows, forming a triangular shape. + +Time Complexity: The time complexity of this code is O(n^2), where n is the number of rows. This is because we have two nested loops, one iterating n times and the other iterating up to the current row number. + +Space Complexity: The space complexity of this code is O(1), as we are not using any additional data structures that depend on the input size. + +Sample Input: +n = 5 + +Sample Output: +1 +2 3 +4 5 6 +7 8 9 10 +11 12 13 14 15 + +''' + +#code in Python that prints a number pattern in a triangular pattern starting from 1: + +def print_triangular_pattern(n): + num = 1 # Initialize the starting number + + for i in range(1, n + 1): # Iterate through each row + for j in range(1, i + 1): # Print numbers in each row + print(num, end=" ") + num += 1 # Increment the number for the next iteration + print() # Move to the next line after each row + + +# Example usage +n = 5 +print_triangular_pattern(n) + + diff --git a/Priority Queues/In_Place_HeapSort.cpp b/Priority Queues/In_Place_HeapSort.cpp new file mode 100644 index 00000000..dbd62754 --- /dev/null +++ b/Priority Queues/In_Place_HeapSort.cpp @@ -0,0 +1,139 @@ +/* +Given an integer array of size N. Sort this array (in decreasing order) using heap sort. +Note: Space complexity should be O(1). +Input Format: + +The first line of input contains an integer, that denotes the value of the size of the array or N. +The following line contains N space separated integers, that denote the value of the elements of the array. + +Output Format : + +The first and only line of output contains array elements after sorting. The elements of the array in the output are separated by single space. + +Constraints : + +1 <= n <= 10^6 +Time Limit: 1 sec + +Sample Input 1: + +6 +2 6 8 5 4 3 + +Sample Output 1: + +8 6 5 4 3 2 + +Explaination : + The code implements the heap sort algorithm, which is a sorting algorithm that works by first building a max heap. A max heap is a binary tree where the value of each parent node is greater than or equal to the value of each of its child nodes. + + The heap sort algorithm works by repeatedly swapping the root element of the heap with the last element of the array and then rebuilding the heap without the root element. This process is repeated until the array is sorted. + + The code first defines a function called heapSort(). This function takes two parameters: an array of integers and the size of the array. The function builds a max heap from the bottom up and then sorts the array by repeatedly swapping the root element with the last element and rebuilding the heap. + + The main function of the code prompts the user to enter the size of the array and then prompts the user to enter the elements of the array. The main function then calls the heapSort() function and prints the sorted array. + + The intuition behind the heap sort algorithm is that it takes advantage of the fact that a max heap is already partially sorted. By repeatedly swapping the root element with the last element of the array and rebuilding the heap, the algorithm is able to sort the array in a relatively efficient way. + + The heap sort algorithm is a divide-and-conquer algorithm, which means that it breaks the problem of sorting an array into smaller and smaller subproblems until they are trivial to solve. This makes the algorithm very efficient for sorting large arrays. +*/ +#include + +using namespace std; + +void heapSort(int arr[], int n) +{ + // This function sorts an array using the heap sort algorithm. + // The algorithm works by first building a max heap. + // Then, the root of the heap is swapped with the last element of the array. + // The heap is then rebuilt without the root element. + // This process is repeated until the array is sorted. + + for (int i = 1; i < n; i++) + { + // Recursively build a max heap from the bottom up. + int childIndex = i; + while (childIndex > 0) + { + int parentIndex = (childIndex - 1) / 2; + if (arr[childIndex] < arr[parentIndex]) + { + // Swap the child and parent. + int temp = arr[childIndex]; + arr[childIndex] = arr[parentIndex]; + arr[parentIndex] = temp; + } + else + { + // The heap is already a max heap. + break; + } + childIndex = parentIndex; + } + } + + // Sort the array by repeatedly swapping the root element with the last element and rebuilding the heap. + int j = n; + while (j > 1) + { + // Swap the root element with the last element. + int temp = arr[0]; + arr[0] = arr[j - 1]; + arr[j - 1] = temp; + j--; + + // Rebuild the heap without the root element. + int parentIndex = 0; + int rightChildIndex = 2 * parentIndex + 2; + int leftChildIndex = 2 * parentIndex + 1; + while (leftChildIndex < j) + { + // Find the minimum child. + int minIndex = parentIndex; + if (arr[leftChildIndex] < arr[minIndex]) + { + minIndex = leftChildIndex; + } + if (rightChildIndex < j && arr[rightChildIndex] < arr[minIndex]) + { + minIndex = rightChildIndex; + } + + // Swap the minimum child with the parent. + int temp = arr[minIndex]; + arr[minIndex] = arr[parentIndex]; + arr[parentIndex] = temp; + + if (parentIndex == minIndex) + { + // The heap is already a max heap. + break; + } + parentIndex = minIndex; + rightChildIndex = 2 * parentIndex + 2; + leftChildIndex = 2 * parentIndex + 1; + } + } +} + +int main() +{ + int size; + cin >> size; + + int *input = new int[size]; + + for (int i = 0; i < size; i++) + { + cin >> input[i]; + } + + heapSort(input, size); + + for (int i = 0; i < size; i++) + { + cout << input[i] << " "; + } + + delete[] input; +} diff --git a/Priority Queues/buy_the_ticket.cpp b/Priority Queues/buy_the_ticket.cpp new file mode 100644 index 00000000..ab35aefa --- /dev/null +++ b/Priority Queues/buy_the_ticket.cpp @@ -0,0 +1,139 @@ +/* +You want to buy a ticket for a well-known concert which is happening in your city. But the number of tickets available is limited. Hence the sponsors of the concert decided to sell tickets to customers based on some priority. +A queue is maintained for buying the tickets and every person is attached with a priority (an integer, 1 being the lowest priority). +The tickets are sold in the following manner - + +1. The first person (pi) in the queue requests for the ticket. +2. If there is another person present in the queue who has higher priority than pi, then ask pi to move at end of the queue without giving him the ticket. +3. Otherwise, give him the ticket (and don't make him stand in queue again). + +Giving a ticket to a person takes exactly 1 minute and it takes no time for removing and adding a person to the queue. And you can assume that no new person joins the queue. +Given a list of priorities of N persons standing in the queue and the index of your priority (indexing starts from 0). Find and return the time it will take until you get the ticket. +Input Format: + +The first line of input contains an integer, that denotes the value of total number of people standing in queue or the size of the array of priorities. Let us denote it with the symbol N. +The following line contains N space separated integers, that denote the value of the elements of the array of priorities. +The following contains an integer, that denotes the value of index of your priority. Let us denote it with symbol k. + +Output Format : + +The first and only line of output contains the time required for you to get the ticket. + +Constraints: + +Time Limit: 1 sec + +Sample Input 1 : + +3 +3 9 4 +2 + +Sample Output 1 : + +2 + +Sample Output 1 Explanation : + +Person with priority 3 comes out. But there is a person with higher priority than him. So he goes and then stands in the queue at the end. Queue's status : {9, 4, 3}. Time : 0 secs. +Next, the person with priority 9 comes out. And there is no person with higher priority than him. So he'll get the ticket. Queue's status : {4, 3}. Time : 1 secs. +Next, the person with priority 4 comes out (which is you). And there is no person with higher priority than you. So you'll get the ticket. Time : 2 secs. + +Sample Input 2 : + +5 +2 3 2 2 4 +3 + +Sample Output 2 : + +4 + +Explaination : + The code first defines two data structures: a priority queue and a queue. The priority queue is used to store the priorities of all the people in the queue, and the queue is used to store the indices of the people in the queue. + + The function buyTicket() takes three parameters: an array of priorities, the number of people in the queue, and the index of the person who is buying the ticket. The function first pushes all the priorities into the priority queue. Then, it pushes all the indices into the queue. + + The function then enters a loop. In each iteration of the loop, the function checks if the priority of the person at the front of the queue is equal to the priority of the person who is buying the ticket. If it is, the function increments the time by 1, pops the person at the front of the queue, and pops the person at the front of the queue. If it is not, the function pops the person at the front of the queue and pushes them back into the queue. + + The function continues looping until the person who is buying the ticket is at the front of the queue. The function then returns the time it took for the person to buy the ticket. + + The main function of the code first prompts the user to enter the number of people in the queue. Then, it creates an array of priorities and prompts the user to enter the priorities of all the people in the queue. Finally, it prompts the user to enter the index of the person who is buying the ticket, and it prints the time it took for the person to buy the ticket. + +*/ + +#include +#include +#include +using namespace std; + +int buyTicket(int *arr, int n, int k) +{ + // Initialize a priority queue to store the priorities of all the people in the queue. + priority_queue pq; + // Initialize a queue to store the indices of all the people in the queue. + queue indices; + // Push all the priorities into the priority queue. + for (int i = 0; i < n; i++) + { + pq.push(arr[i]); + } + // Push all the indices into the queue. + for (int i = 0; i < n; i++) + { + indices.push(i); + } + // Initialize the time it took for the person to buy the ticket. + int time = 0; + // Loop until the person who is buying the ticket is at the front of the queue. + while (!indices.empty()) + { + // Check if the priority of the person at the front of the queue is equal to the priority of the person who is buying the ticket. + if (arr[indices.front()] == pq.top() && indices.front() == k) + { + time++; + pq.pop(); + indices.pop(); + break; + } + // If the priorities match, increment the time by 1 and break out of the loop. + if (arr[indices.front()] == pq.top()) + { + time++; + pq.pop(); + indices.pop(); + } + // If the priorities do not match, pop the person at the front of the queue and push them back into the queue. + else + { + int temp = indices.front(); + indices.pop(); + indices.push(temp); + } + } + // Return the time it took for the person to buy the ticket. + return time; +} + +int main() +{ + // Prompt the user to enter the number of people in the queue. + int n; + cin >> n; + // Create an array of priorities and prompt the user to enter the priorities of all the people in the queue. + int *arr = new int[n]; + for (int i = 0; i < n; i++) + { + cin >> arr[i]; + } + + // Prompt the user to enter the index of the person who is buying the ticket. + int k; + cin >> k; + + // Print the time it took for the person to buy the ticket. + cout << buyTicket(arr, n, k); + + // Delete the array of priorities. + delete[] arr; +} \ No newline at end of file diff --git a/Priority Queues/check_max_heap.cpp b/Priority Queues/check_max_heap.cpp new file mode 100644 index 00000000..7bbdc45d --- /dev/null +++ b/Priority Queues/check_max_heap.cpp @@ -0,0 +1,84 @@ +/* +Given an array of integers, check whether it represents max-heap or not. Return true if the given array represents max-heap, else return false. +Input Format: + +The first line of input contains an integer, that denotes the value of the size of the array. Let us denote it with the symbol N. +The following line contains N space separated integers, that denote the value of the elements of the array. + +Output Format : + +The first and only line of output contains true if it represents max-heap and false if it is not a max-heap. + +Constraints: + +1 <= N <= 10^5 +1 <= Ai <= 10^5 +Time Limit: 1 sec + +Sample Input 1: + +8 +42 20 18 6 14 11 9 4 + +Sample Output 1: + +true + +Explaination : + The code first defines a function called isMaxHeap(). This function takes two parameters: an array of integers and the size of the array. The function recursively checks if the array is a max heap. A max heap is a binary tree where the value of each parent node is greater than or equal to the value of each of its child nodes. + + The function starts by checking the last element of the array. If the parent of the last element is smaller than the last element, the function returns false. Otherwise, the function recursively checks the parent of the last element. This process continues until the function reaches the root of the tree. + + If the function reaches the root of the tree and all of the parent nodes are greater than or equal to their child nodes, the function returns true. Otherwise, the function returns false. + + The main function of the code prompts the user to enter the size of the array and then prompts the user to enter the elements of the array. The main function then calls the isMaxHeap() function and prints the result. + +*/ + +#include +using namespace std; + +bool isMaxHeap(int arr[], int n) +{ + + // This function recursively checks if the array is a max heap. + // The function starts at the last element of the array and checks if the parent is smaller than the child. + // If the parent is smaller than the child, the function returns false. + // Otherwise, the function recursively checks the parent of the child. + + int childIndex = n - 1; + while (childIndex >= 0) + { + int parentIndex = (childIndex - 1) / 2; + // Check if the parent is smaller than the child. + if (arr[parentIndex] < arr[childIndex]) + { + return false; + } + // Recursively check the parent of the child. + childIndex--; + } + // The array is a max heap. + return true; +} + +int main() +{ + + // Prompt the user to enter the size of the array. + int n; + cin >> n; + + // Create an array and prompt the user to enter the elements of the array. + int *arr = new int[n]; + for (int i = 0; i < n; i++) + { + cin >> arr[i]; + } + + // Check if the array is a max heap. + cout << (isMaxHeap(arr, n) ? "true\n" : "false\n"); + + // Delete the array. + delete[] arr; +} diff --git a/Priority Queues/kth_largest_element.cpp b/Priority Queues/kth_largest_element.cpp new file mode 100644 index 00000000..e8ddac1b --- /dev/null +++ b/Priority Queues/kth_largest_element.cpp @@ -0,0 +1,100 @@ +/* +Given an array A of random integers and an integer k, find and return the kth largest element in the array. +Note: Try to do this question in less than O(N * logN) time. +Input Format : + +The first line of input contains an integer, that denotes the value of the size of the array. Let us denote it with the symbol N. +The following line contains N space separated integers, that denote the value of the elements of the array. +The following contains an integer, that denotes the value of k. + +Output Format : + +The first and only line of output contains the kth largest element + +Constraints : + +1 <= N, Ai, k <= 10^5 +Time Limit: 1 sec + +Sample Input 1 : + +6 +9 4 8 7 11 3 +2 + +Sample Output 1 : + +9 + +Sample Input 2 : + +8 +2 6 10 11 13 4 1 20 +4 + +Sample Output 2 : + +10 + +Explaination : + The code uses a min-heap priority queue (pq) to find the kth largest element in the given array. It initializes the priority queue with the first k elements of the array. Then, it iterates over the remaining elements of the array and compares each element with the smallest element (top) of the priority queue. If the current element is larger, it replaces the smallest element in the priority queue. Finally, it returns the top element of the priority queue, which will be the kth largest element. + + Note: The code dynamically allocates memory for the array using new and deallocates it using delete[] to ensure proper memory management. + +*/ +#include +#include +#include +using namespace std; + +// Function to find the kth largest element in an array +int kthLargest(int *arr, int n, int k) +{ + // Create a min-heap priority queue + priority_queue, greater> pq; + + // Insert the first k elements into the priority queue + for (int i = 0; i < k; i++) + { + pq.push(arr[i]); + } + + // Iterate over the remaining elements in the array + for (int i = k; i < n; i++) + { + // If the current element is greater than the smallest element in the priority queue (top), + // replace the smallest element with the current element + if (arr[i] > pq.top()) + { + pq.push(arr[i]); + pq.pop(); + } + } + + // The top element of the priority queue will be the kth largest element + return pq.top(); +} + +int main() +{ + int n; + cin >> n; + + // Dynamically allocate memory for the array + int *arr = new int[n]; + + // Read the elements of the array from the user + for (int i = 0; i < n; i++) + { + cin >> arr[i]; + } + + int k; + cin >> k; + + // Call the kthLargest function to find the kth largest element + cout << kthLargest(arr, n, k); + + // Deallocate the memory for the array + delete[] arr; +} diff --git a/Priority Queues/kth_smallest_element.cpp b/Priority Queues/kth_smallest_element.cpp new file mode 100644 index 00000000..3cb84fc5 --- /dev/null +++ b/Priority Queues/kth_smallest_element.cpp @@ -0,0 +1,118 @@ +/* +You are given with an integer k and an array of integers that contain numbers in random order. Write a program to find k smallest numbers from given array. You need to save them in an array and return it. +Time complexity should be O(n * logk) and space complexity should not be more than O(k). +Note: Order of elements in the output is not important. +Input Format : + +The first line of input contains an integer, that denotes the value of the size of the array. Let us denote it with the symbol N. +The following line contains N space separated integers, that denote the value of the elements of the array. +The following line contains an integer, that denotes the value of k. + +Output Format : + +The first and only line of output print k smallest elements. The elements in the output are separated by a single space. + +Constraints: + +Time Limit: 1 sec + +Sample Input 1 : + +13 +2 12 9 16 10 5 3 20 25 11 1 8 6 +4 + +Sample Output 1 : + +1 2 3 5 + +Explaination : + + The code first defines a function called kSmallest(). This function takes three parameters: an array of integers, the size of the array, and the number of smallest elements to find. The function uses a priority queue to find the k smallest elements in the array. + + The code then defines a main function. The main function prompts the user to enter the size of the array and the number of smallest elements to find. The main function then creates an array of integers and prompts the user to enter the elements of the array. The main function then calls the kSmallest() function and prints the k smallest elements in the array. + + The kSmallest() function works as follows: + + The function first creates a priority queue and pushes the first k elements of the array onto the priority queue. + The function then iterates through the remaining elements of the array. If the current element is less than the top element of the priority queue, the function pushes the current element onto the priority queue and pops the top element of the priority queue. + The function then returns a vector of the elements in the priority queue. + + +*/ +#include +#include +#include +#include +using namespace std; + +// This function finds the k smallest elements in an array. +vector kSmallest(int arr[], int n, int k) +{ + + // Create a priority queue and push the first k elements of the array onto the priority queue. + priority_queue pq; + for (int i = 0; i < k; i++) + { + pq.push(arr[i]); + } + + // Iterate through the remaining elements of the array. + // If the current element is less than the top element of the priority queue, + // push the current element onto the priority queue and pop the top element of the priority queue. + for (int i = k; i < n; i++) + { + if (arr[i] < pq.top()) + { + pq.push(arr[i]); + pq.pop(); + } + } + + // Return a vector of the elements in the priority queue. + vector vec; + while (!pq.empty()) + { + vec.push_back(pq.top()); + pq.pop(); + } + return vec; +} + +// This is the main function. +int main() +{ + + // Prompt the user to enter the size of the array and the number of smallest elements to find. + int size; + cin >> size; + + // Create an array of integers. + int *input = new int[size]; + + // Prompt the user to enter the elements of the array. + for (int i = 0; i < size; i++) + { + cin >> input[i]; + } + + // Prompt the user to enter the number of smallest elements to find. + int k; + cin >> k; + + // Call the `kSmallest()` function and print the k smallest elements in the array. + vector output = kSmallest(input, size, k); + + // Sort the output vector in ascending order. + sort(output.begin(), output.end()); + + // Print the output vector. + for (int i = 0; i < output.size(); i++) + { + cout << output[i] << " "; + } + + // Delete the array. + delete[] input; + return 0; +} diff --git a/Priority Queues/merge_k_sorted_arrays.cpp b/Priority Queues/merge_k_sorted_arrays.cpp new file mode 100644 index 00000000..36174e52 --- /dev/null +++ b/Priority Queues/merge_k_sorted_arrays.cpp @@ -0,0 +1,132 @@ +/* +Given k different arrays, which are sorted individually (in ascending order). You need to merge all the given arrays such that output array should be sorted (in ascending order). +Hint : Use Heaps. +Input Format: + +The first line of input contains an integer, that denotes value of k. +The following lines of input represent k sorted arrays. Each sorted array uses two lines of input. The first line contains an integer, that denotes the size of the array. The following line contains elements of the array. + +Output Format: + +The first and only line of output contains space separated elements of the merged and sorted array, as described in the task. + +Constraints: + +0 <= k <= 1000 +0 <= n1, n2 <= 10000 +Time Limit: 1 second + +Sample Input 1: + +4 +3 +1 5 9 +2 +45 90 +5 +2 6 78 100 234 +1 +0 + +Sample Output 1: + +0 1 2 5 6 9 45 78 90 100 234 + +Explaination : + The intuition behind the code is to use a min-heap (priority queue) to merge K sorted arrays efficiently. + + By taking the first element from each array and inserting it into the priority queue, the smallest element is always at the top. This ensures that when elements are extracted from the priority queue, they are in sorted order. + + The code iteratively extracts the smallest element from the priority queue, adds it to the merged sorted array, and checks if there are remaining elements in the array from which the extracted element came. If so, the next element from that array is inserted into the priority queue. + + This process continues until all elements from all arrays are processed and added to the merged sorted array. + + By utilizing the min-heap property of the priority queue, the code efficiently merges the sorted arrays in a way that the overall time complexity is optimized. The priority queue allows for easy retrieval of the smallest element, resulting in an overall time complexity of O(N log K), where N is the total number of elements across all arrays and K is the number of arrays. +*/ +#include +#include +#include +using namespace std; + +vector mergeKSortedArrays(vector *> input) +{ + // Create a min-heap priority queue to store elements from different arrays + priority_queue>, vector>>, greater>>> pq; + + // Insert the first element from each array into the priority queue + for (int i = 0; i < input.size(); i++) + { + pair> p; + p.first = input[i]->at(0); // value of the element + p.second.first = i; // array number + p.second.second = 0; // index of the element + pq.push(p); + } + + // Initialize a vector to store the merged sorted array + vector ans; + + // Process the priority queue until it becomes empty + while (!pq.empty()) + { + pair> p = pq.top(); + pq.pop(); + + // Add the minimum element to the result vector + ans.push_back(p.first); + + int arrNo = p.second.first; // array number + int index = p.second.second + 1; // index of the next element in the array + + // If there are still elements remaining in the array, add the next element to the priority queue + if (index < input[arrNo]->size()) + { + p.first = input[arrNo]->at(index); // value of the element + p.second.second = index; // update the index + pq.push(p); + } + } + + // Return the merged sorted array + return ans; +} + +int main() +{ + int k; + cin >> k; // Number of sorted arrays + + vector *> input; // Vector to store the arrays + + // Read the size and elements of each array + for (int j = 1; j <= k; j++) + { + int size; + cin >> size; // Size of the array + + // Create a dynamically allocated vector for the current array + vector *current = new vector; + + // Read the elements of the array + for (int i = 0; i < size; i++) + { + int a; + cin >> a; // Element of the array + current->push_back(a); // Add the element to the current array + } + + // Add the current array to the vector of arrays + input.push_back(current); + } + + // Merge the sorted arrays using the mergeKSortedArrays function + vector output = mergeKSortedArrays(input); + + // Print the merged sorted array + for (int i = 0; i < output.size(); i++) + { + cout << output[i] << " "; + } + + return 0; +} diff --git a/Priority Queues/running_median.cpp b/Priority Queues/running_median.cpp new file mode 100644 index 00000000..628ae5c7 --- /dev/null +++ b/Priority Queues/running_median.cpp @@ -0,0 +1,146 @@ +/* +You are given a stream of 'N' integers. For every 'i-th' integer added to the running list of integers, print the resulting median. +Print only the integer part of the median. +Input Format : + +The first line of input contains an integer 'N', denoting the number of integers in the stream. + +The second line of input contains 'N' integers separated by a single space. + +Output Format : + +Print the running median for every integer added to the running list in one line (space-separated). + +Input Constraints + +0 <= N <= 10 ^ 5 +1 <= ARR[i] <= 10 ^ 5 + +Time Limit: 1 sec + +Sample Input 1 : + +6 +6 2 1 3 7 5 + +Sample Output 1 : + +6 4 2 2 3 4 + +Explanation of Sample Output 1 : + +S = {6}, median = 6 +S = {6, 2} -> {2, 6}, median = 4 +S = {6, 2, 1} -> {1, 2, 6}, median = 2 +S = {6, 2, 1, 3} -> {1, 2, 3, 6}, median = 2 +S = {6, 2, 1, 3, 7} -> {1, 2, 3, 6, 7}, median = 3 +S = {6, 2, 1, 3, 7, 5} -> {1, 2, 3, 5, 6, 7}, median = 4 + +Sample Input 2 : + +5 +5 4 3 2 1 + +Sample Output 2 : + +5 4 4 3 3 + +Explaination : + + The code aims to efficiently find and print the median of a stream of integers as they are read one by one. It achieves this by using two priority queues: `min` and `max`. These priority queues divide the elements encountered so far into the lower and higher halves, allowing for the calculation of the median. + + When reading the integers one by one, the code follows these steps: + + 1. For the first element, it is pushed into the `max` priority queue. At this point, the median is simply the first element. + + 2. For subsequent elements, the code compares each element with the current median (which is the top element of the `max` priority queue). If the new element is smaller, it is pushed into the `max` priority queue. This ensures that the lower half of the elements is stored in `max`, with the maximum element of the lower half at the top. + + 3. If the new element is larger than the current median, it is pushed into the `min` priority queue. This ensures that the higher half of the elements is stored in `min`, with the minimum element of the higher half at the top. + + 4. After each insertion, the code checks the size difference between the `min` and `max` priority queues. If the difference exceeds 1, it rebalances the queues by transferring the top element from the larger queue to the smaller one. This ensures that the size difference between the two halves remains at most 1, allowing for accurate median calculations. + + 5. To calculate and print the median, the code checks the total number of elements (c) in both priority queues. If c is even, the median is the average of the top elements of `min` and `max`. If c is odd, the median is the top element of the priority queue with more elements. + + By using two priority queues to maintain the lower and higher halves of the elements encountered so far, the code efficiently updates and calculates the median of the stream of integers in a scalable manner. The rebalancing step ensures that the size difference between the two halves remains at most 1, allowing for accurate median calculations even as new elements are added to the stream. + +*/ +#include +#include +using namespace std; + +void findMedian(int *arr, int n) +{ + priority_queue, greater> min; // Min-heap to store the higher half of the elements encountered + priority_queue max; // Max-heap to store the lower half of the elements encountered + + for (int i = 0; i < n; i++) + { + if (i == 0) + { + max.push(arr[i]); // For the first element, push it into the max heap + } + else + { + if (max.top() > arr[i]) + { + max.push(arr[i]); // If the new element is smaller than the current median, push it into the max heap + } + else + { + min.push(arr[i]); // If the new element is larger than the current median, push it into the min heap + } + } + + if (int(min.size() - max.size()) > 1) + { + int temp = min.top(); + min.pop(); + max.push(temp); // Rebalance the heaps by transferring the top element from min heap to max heap + } + else if (int(max.size() - min.size()) > 1) + { + int temp = max.top(); + max.pop(); + min.push(temp); // Rebalance the heaps by transferring the top element from max heap to min heap + } + + int c = max.size() + min.size(); // Total count of elements + + if (c % 2 == 0) + { + cout << (min.top() + max.top()) / 2 << " "; // If total count is even, median is the average of the top elements of both heaps + } + else + { + if (min.size() > max.size()) + { + cout << min.top() << " "; // If total count is odd and min heap has more elements, median is the top element of min heap + } + else + { + cout << max.top() << " "; // If total count is odd and max heap has more elements, median is the top element of max heap + } + } + } + + cout << endl; +} + +int main() +{ + int n; + cin >> n; // Number of elements + + int *arr = new int[n]; // Dynamically allocate memory for the array + + for (int i = 0; i < n; ++i) + { + cin >> arr[i]; // Read the elements of the array + } + + findMedian(arr, n); // Find and print the median of the stream + + delete[] arr; // Deallocate the memory + + return 0; +} diff --git a/Queue/queue.cpp b/Queue/queue.cpp new file mode 100644 index 00000000..83ab8424 --- /dev/null +++ b/Queue/queue.cpp @@ -0,0 +1,92 @@ +// Implement Queue data structure in C++ +/* + In this implementation, the Queue class has private member variables front, rear, and arr to keep track of the front and rear + indices of the queue and the elements in the queue, respectively. The class also has public member functions isFull(), isEmpty(), + enqueue(), dequeue(), peek(), and print() for checking if the queue is full or empty, adding elements to the rear of the queue, + removing elements from the front of the queue, peeking at the element at the front of the queue, and printing the elements in the + queue, respectively. The main() function demonstrates how to use the Queue class by creating a new queue object q, adding three + elements to the queue, removing one element from the front of the queue, peeking at the element at the front of the queue, and + printing the elements in the queue +*/ +#include +using namespace std; + +const int MAX_SIZE = 100; // maximum size of the queue + +class Queue { +private: + int front, rear; + int arr[MAX_SIZE]; +public: + Queue() { + front = -1; // initialize front and rear to -1 to indicate the queue is empty + rear = -1; + } + + bool isFull() { + return rear == MAX_SIZE - 1; // check if the rear index is at the maximum size + } + + bool isEmpty() { + return front == -1 && rear == -1; // check if both front and rear indices are at -1, indicating an empty queue + } + + void enqueue(int x) { + if (isFull()) { + cout << "Error: Queue is full" << endl; + return; + } + if (isEmpty()) { + front = rear = 0; // if the queue is empty, set both front and rear to 0 to add the first element + } + else { + rear++; // increment rear to add the new element + } + arr[rear] = x; // add the new element to the rear of the queue + } + + void dequeue() { + if (isEmpty()) { + cout << "Error: Queue is empty" << endl; + return; + } + if (front == rear) { + front = rear = -1; // if the queue has only one element, set both front and rear indices to -1 to indicate an empty queue + } + else { + front++; // increment front to remove the element + } + } + + int peek() { + if (isEmpty()) { + cout << "Error: Queue is empty" << endl; + return -1; + } + return arr[front]; // return the element at the front of the queue + } + + void print() { + if (isEmpty()) { + cout << "Queue is empty" << endl; + return; + } + cout << "Queue: "; + for (int i = front; i <= rear; i++) { + cout << arr[i] << " "; // print each element in the queue from front to rear + } + cout << endl; + } +}; + +int main() { + Queue q; + q.enqueue(1); + q.enqueue(2); + q.enqueue(3); + q.print(); // output: Queue: 1 2 3 + q.dequeue(); + q.print(); // output: Queue: 2 3 + cout << "Front element: " << q.peek() << endl; // output: Front element: 2 + return 0; +} diff --git a/Queue/queue.go b/Queue/queue.go new file mode 100644 index 00000000..efe9e04e --- /dev/null +++ b/Queue/queue.go @@ -0,0 +1,93 @@ +// Queue Data Structure +/* + In this implementation, we define a struct for the queue that has a slice to store the values and pointers to the + front and rear of the queue. We also define methods for checking if the queue is empty, adding an element to the + rear of the queue, and removing an element from the front of the queue. + + The NewQueue() function creates a new queue and initializes its front and rear pointers to nil. The IsEmpty() + method checks if the queue is empty by checking if the front pointer is nil. The Enqueue() method adds an + element to the rear of the queue by appending it to the slice and updating the rear pointer. The Dequeue() + method removes an element from the front of the queue by removing it from the slice and updating the front + pointer. + + In the main() function, we create a new queue, add some elements to it, and then dequeue them one + by one and print their values. +*/ +package main + +import "fmt" + +// Define a struct for queue that has a slice to store values and pointers to the front and rear of the queue +type Queue struct { + values []int + front *int + rear *int +} + +// Function to create a new queue and initialize its front and rear pointers +func NewQueue() *Queue { + q := Queue{values: make([]int, 0), front: nil, rear: nil} + return &q +} + +// Function to check if the queue is empty +func (q *Queue) IsEmpty() bool { + return q.front == nil +} + +// Function to add an element to the rear of the queue +func (q *Queue) Enqueue(val int) { + // If the queue is empty, set both front and rear pointers to the new element + if q.front == nil { + q.front = &val + q.rear = &val + } else { + // Otherwise, add the new element to the rear of the queue and update the rear pointer + q.values = append(q.values, val) + q.rear = &q.values[len(q.values)-1] + } +} + +// Function to remove an element from the front of the queue and return its value +func (q *Queue) Dequeue() (int, error) { + // If the queue is empty, return an error + if q.front == nil { + return 0, fmt.Errorf("queue is empty") + } + + // Get the value of the element at the front of the queue + val := *q.front + + // If there is only one element in the queue, set both front and rear pointers to nil + if len(q.values) == 1 { + q.front = nil + q.rear = nil + } else { + // Otherwise, remove the front element from the slice and update the front pointer + q.values = q.values[1:] + q.front = &q.values[0] + } + + // Return the value of the dequeued element + return val, nil +} + +func main() { + // Create a new queue + q := NewQueue() + + // Add some elements to the queue + q.Enqueue(10) + q.Enqueue(20) + q.Enqueue(30) + + // Dequeue elements from the queue and print their values + for !q.IsEmpty() { + val, err := q.Dequeue() + if err != nil { + fmt.Println(err) + } else { + fmt.Println(val) + } + } +} diff --git a/Queue/queue.java b/Queue/queue.java new file mode 100644 index 00000000..df09d22b --- /dev/null +++ b/Queue/queue.java @@ -0,0 +1,130 @@ +// 1. Queue Using Array: +// 2. Queue Using LinkedList: +/* + In this implementation, we use an array to store the elements of the queue, and we keep track of the front and rear indices, as well as the current size of the queue. + The enqueue operation adds an item to the rear of the queue, while the dequeue operation removes an item from the front of the queue. + The isEmpty and isFull methods check whether the queue is empty or full, respectively, while the size method returns the current size of the queue. + +*/ +// 1. Queue Using Array: +public class Queue { + private int maxSize; + private int[] queueArray; + private int front; + private int rear; + private int currentSize; + + public Queue(int size) { + this.maxSize = size; + this.queueArray = new int[size]; + this.front = 0; + this.rear = -1; + this.currentSize = 0; + } + + public void enqueue(int item) { + if (isFull()) { + System.out.println("Queue is full"); + return; + } + rear++; + if (rear == maxSize) { + rear = 0; + } + queueArray[rear] = item; + currentSize++; + } + + public int dequeue() { + if (isEmpty()) { + System.out.println("Queue is empty"); + return -1; + } + int temp = queueArray[front]; + front++; + if (front == maxSize) { + front = 0; + } + currentSize--; + return temp; + } + + public boolean isEmpty() { + return currentSize == 0; + } + + public boolean isFull() { + return currentSize == maxSize; + } + + public int size() { + return currentSize; + } + } + + + + +// Here's an example implementation of a Queue data structure in Java using a linked list +/* + In this implementation, we use a linked list to store the elements of the queue, and we keep track of the front and rear nodes, as well as the current size of the queue. + The enqueue operation adds a new node to the rear of the linked list, while the dequeue operation removes the head node from the linked list. + The isEmpty method checks whether the queue is empty, while the size method returns the current size of the queue. + Note that this implementation uses generics to allow the queue to store elements of any type. +*/ +// 2. Queue Using LinkedList: +public class Queue { + private Node front; + private Node rear; + private int size; + + public Queue() { + front = null; + rear = null; + size = 0; + } + + private static class Node { + T data; + Node next; + + public Node(T data) { + this.data = data; + this.next = null; + } + } + + public void enqueue(T item) { + Node newNode = new Node<>(item); + if (isEmpty()) { + front = newNode; + } else { + rear.next = newNode; + } + rear = newNode; + size++; + } + + public T dequeue() { + if (isEmpty()) { + System.out.println("Queue is empty"); + return null; + } + T item = front.data; + front = front.next; + if (front == null) { + rear = null; + } + size--; + return item; + } + + public boolean isEmpty() { + return size == 0; + } + + public int size() { + return size; + } + } + diff --git a/Queue/queue.js b/Queue/queue.js new file mode 100644 index 00000000..0df4b905 --- /dev/null +++ b/Queue/queue.js @@ -0,0 +1,110 @@ +// 1. Queue Using Array: +// 2. Queue Using LinkedList: +/* + In this implementation, we use an array to store the elements of the queue, and we keep track of the front and rear indices, as well as the current size of the queue. + The enqueue operation adds an item to the rear of the queue, while the dequeue operation removes an item from the front of the queue. + The isEmpty and isFull methods check whether the queue is empty or full, respectively, while the size method returns the current size of the queue. + +*/ +// 1. Queue Using Array: +class Queue { + constructor() { + this.items = []; + } + + enqueue(element) { + this.items.push(element); + } + + dequeue() { + if (this.isEmpty()) { + return "Underflow"; + } + return this.items.shift(); + } + + front() { + if (this.isEmpty()) { + return "No elements in Queue"; + } + return this.items[0]; + } + + isEmpty() { + return this.items.length === 0; + } + + printQueue() { + let str = ""; + for (let i = 0; i < this.items.length; i++) { + str += this.items[i] + " "; + } + return str; + } +} + +// Here's an example implementation of a Queue data structure in Java using a linked list +/* + In this implementation, we use a linked list to store the elements of the queue, and we keep track of the front and rear nodes, as well as the current size of the queue. + The enqueue operation adds a new node to the rear of the linked list, while the dequeue operation removes the head node from the linked list. + The isEmpty method checks whether the queue is empty, while the size method returns the current size of the queue. + Note that this implementation uses generics to allow the queue to store elements of any type. +*/ +// 2. Queue Using LinkedList: +class Node { + constructor(data) { + this.data = data; + this.next = null; + } +} + +class Queue { + constructor() { + this.front = null; + this.rear = null; + this.size = 0; + } + + enqueue(data) { + let newNode = new Node(data); + if (this.rear === null) { + this.front = newNode; + this.rear = newNode; + } else { + this.rear.next = newNode; + this.rear = newNode; + } + this.size++; + } + + dequeue() { + if (this.front === null) { + return "Underflow"; + } + let removedNode = this.front; + this.front = this.front.next; + if (this.front === null) { + this.rear = null; + } + this.size--; + return removedNode.data; + } + + isEmpty() { + return this.size === 0; + } + + getFront() { + return this.front ? this.front.data : "No elements in Queue"; + } + + printQueue() { + let str = ""; + let temp = this.front; + while (temp) { + str += temp.data + " "; + temp = temp.next; + } + return str; + } +} diff --git a/Queue/queue.py b/Queue/queue.py new file mode 100644 index 00000000..815ed6d6 --- /dev/null +++ b/Queue/queue.py @@ -0,0 +1,53 @@ +# Implement Queue Data Structure +''' + This implementation uses a Python list to store the items in the queue. The __init__() method initializes + an empty list. The is_empty() method checks whether the list is empty or not by checking the length of + the list. The enqueue() method adds an item to the back of the queue by appending it to the end of the + list. The dequeue() method removes and returns the item at the front of the queue by using the pop() + method to remove the first item in the list. If the list is empty, the method raises an IndexError. + The peek() method returns the item at the front of the queue without removing it by returning the + first item in the list. If the list is empty, the method raises an IndexError. The size() method + returns the number of items in the list by returning the length of the list. +''' +class Queue: + def __init__(self): + """ + Initializes an empty queue. + """ + self.items = [] + + def is_empty(self): + """ + Returns True if the queue is empty, False otherwise. + """ + return len(self.items) == 0 + + def enqueue(self, item): + """ + Adds the given item to the back of the queue. + """ + self.items.append(item) + + def dequeue(self): + """ + Removes and returns the item at the front of the queue. + If the queue is empty, raises an IndexError. + """ + if self.is_empty(): + raise IndexError("Queue is empty") + return self.items.pop(0) + + def peek(self): + """ + Returns the item at the front of the queue without removing it. + If the queue is empty, raises an IndexError. + """ + if self.is_empty(): + raise IndexError("Queue is empty") + return self.items[0] + + def size(self): + """ + Returns the number of items in the queue. + """ + return len(self.items) diff --git a/Queue/queue_using_stack.js b/Queue/queue_using_stack.js new file mode 100644 index 00000000..aed98f31 --- /dev/null +++ b/Queue/queue_using_stack.js @@ -0,0 +1,90 @@ +/* +Implement a first in first out (FIFO) queue using only two stacks. +The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty). + +Implement the MyQueue class: + +void push(int x) Pushes element x to the back of the queue. +- method adds an element to the back of the queue by pushing it onto stack1. + + +int pop() Removes the element from the front of the queue and returns it. +- method removes and returns the front element of the queue by first checking if stack2 is empty. +- if it is, reversing the elements from stack1 onto stack2 so that the front element + of the queue is now at the top of stack2. +- Then it pops the top element from stack2. + + +int peek() Returns the element at the front of the queue. +- returns the front element of the queue without removing it. +- First checking if stack2 is empty +- if it is, reversing the elements from stack1 onto stack2 so that the front element of the queue is now at the top of stack2. +- Then it returns the top element of stack2. + + +boolean empty() Returns true if the queue is empty, false otherwise. +- returns a boolean indicating whether the queue is empty or not. +- It returns true if both stack1 and stack2 are empty, and false otherwise. +*/ + +var MyQueue = function() { + this.stack1 = []; + this.stack2 = []; +}; + + +/** + * @param {number} x + * @return {void} + */ +MyQueue.prototype.push = function(x) { + this.stack1.push(x); +}; + + +/** + * @return {number} + */ +MyQueue.prototype.pop = function() { + if (this.stack2.length === 0) { + if (this.stack1.length === 0) { + return "Underflow"; + } + while (this.stack1.length > 0) { + this.stack2.push(this.stack1.pop()); + } + } + return this.stack2.pop(); +}; + +/** + * @return {number} + */ +MyQueue.prototype.peek = function() { + if (this.stack2.length === 0) { + if (this.stack1.length === 0) { + return "Queue is empty"; + } + while (this.stack1.length > 0) { + this.stack2.push(this.stack1.pop()); + } + } + return this.stack2[this.stack2.length - 1]; +}; + +/** + * @return {boolean} + */ +MyQueue.prototype.empty = function() { + return this.stack1.length === 0 && this.stack2.length === 0; +}; + + +// MyQueue object will be instantiated and called as such: +let myQueue = new MyQueue(); +var param_0 = myQueue.push(1); // queue is: [1] +var param_1 = myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue) +var param_2 = myQueue.peek(); // return 1 +var param_3 = myQueue.pop(); // return 1, queue is [2] +var param_4 = myQueue.empty(); // return false + diff --git a/Queue/queue_using_stacks.go b/Queue/queue_using_stacks.go new file mode 100644 index 00000000..874b2e03 --- /dev/null +++ b/Queue/queue_using_stacks.go @@ -0,0 +1,74 @@ +type MyQueue struct { + stack1 []int + stack2 []int +} + +/** Initialize your data structure here. */ +func Constructor() MyQueue { + return MyQueue{} +} + +/** Push element x to the back of queue. */ +func (this *MyQueue) Push(x int) { + this.stack1 = append(this.stack1, x) +} + +/** Removes the element from in front of queue and returns that element. */ +func (this *MyQueue) Pop() int { + if len(this.stack2) == 0 { + this.moveElements() + } + element := this.stack2[len(this.stack2)-1] + this.stack2 = this.stack2[:len(this.stack2)-1] + return element +} + +/** Get the front element. */ +func (this *MyQueue) Peek() int { + if len(this.stack2) == 0 { + this.moveElements() + } + return this.stack2[len(this.stack2)-1] +} + +/** Returns whether the queue is empty. */ +func (this *MyQueue) Empty() bool { + return len(this.stack1) == 0 && len(this.stack2) == 0 +} + +/** Move elements from stack1 to stack2. */ +func (this *MyQueue) moveElements() { + for len(this.stack1) > 0 { + element := this.stack1[len(this.stack1)-1] + this.stack1 = this.stack1[:len(this.stack1)-1] + this.stack2 = append(this.stack2, element) + } +} + +/* + +The MyQueue struct is defined with two slices: stack1 and stack2. stack1 represents the main stack where elements are pushed initially, and stack2 is used to reverse the order of elements for queue operations. + +The Constructor function is a constructor for the MyQueue struct. It initializes an empty MyQueue instance and returns it. + +The Push method takes an integer x and appends it to the stack1 slice, which represents the back of the queue. + +The Pop method removes and returns the element from the front of the queue. It checks if stack2 is empty, and if so, it calls the moveElements method to transfer elements from stack1 to stack2. This ensures that the elements in stack2 are in the correct order for queue operations. The last element of stack2 is then removed and returned as the result. + +The Peek method returns the element at the front of the queue without removing it. It performs the same check as the Pop method to ensure that the elements in stack2 are up to date, and then returns the last element in stack2. + +The Empty method checks if both stack1 and stack2 are empty, indicating whether the queue is empty or not. + +The moveElements method is used to transfer elements from stack1 to stack2 in the correct order. It pops elements from stack1 and appends them to stack2 until stack1 is empty. This ensures that the elements in stack2 are in the reversed order, mimicking the behavior of a queue. +Time Complexity: + +The Push operation has a time complexity of O(1) because it simply appends an element to the stack1 slice. +The Pop operation has an amortized time complexity of O(1). When stack2 is not empty, popping an element is a constant-time operation. If stack2 is empty, the moveElements method is called, which transfers all elements from stack1 to stack2. This transfer operation takes O(n) time, where n is the number of elements in stack1. However, each element is transferred at most twice in total (once from stack1 to stack2 and once from stack2 back to stack1 in future push operations). Therefore, the average time complexity per pop operation is O(1). +The Peek operation has the same amortized time complexity as Pop, which is O(1). +The Empty operation has a time complexity of O(1) because it checks the lengths of both stack1 and stack2 to determine if the queue is empty. +Space Complexity: + +The space complexity of the MyQueue struct is O(n), where n is the number of elements stored in the queue. This includes the space required for the stack1 and stack2 slices. +In the worst case, when all elements are stored in stack1, the space complexity is O(n). However, when elements are transferred from stack1 to stack2, the space complexity remains O(n) because each element is moved and stored only once + +*/ diff --git a/Queue/queues_using_stacks.py b/Queue/queues_using_stacks.py new file mode 100644 index 00000000..87b8dccc --- /dev/null +++ b/Queue/queues_using_stacks.py @@ -0,0 +1,77 @@ +''' Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Implement Queue using Stacks in Python +Issue Number : #256 +Problem statement : + +Explanation of the below python code : + +In the above code, the Queue class represents a queue implemented using two stacks. The enqueue method simply adds the element to the first stack (stack1). The dequeue method pops an element from the second stack (stack2) if it's not empty. If stack2 is empty, it transfers all elements from stack1 to stack2 in reverse order and then pops an element from stack2. + +The program provides the user with a menu to select the operation they want to perform - enqueue, dequeue, or quit. The user can input the value to enqueue, and the program prints the enqueued or dequeued value accordingly. + +Summary about the code and time complexity: + +This code implements a queue using two stacks. +The enqueue operation is simply implemented by appending an element to one of the stacks, while the dequeue operation involves reversing the order of the elements by popping from one stack and pushing onto the other, and then popping the top element from the second stack. +The time complexity of the enqueue operation is O(1), while the time complexity of the dequeue operation is O(n) in the worst case, where n is the number of elements in the queue. This is because in the worst case, all the elements will need to be moved from one stack to the other during the dequeue operation. + + + +----------------------------------------------------------------------------------------------------------//Python code begins here----------------------------------------------------------------------------------------------------------------------- +''' + +class Queue: + def __init__(self): + # Initialize two empty stacks + self.stack1 = [] + self.stack2 = [] + + def enqueue(self, val): + # Add an element to the end of the queue by appending it to stack1 + self.stack1.append(val) + + def dequeue(self): + # If stack2 is empty, reverse the order of the elements by popping from stack1 and pushing onto stack2 + if not self.stack2: + while self.stack1: + self.stack2.append(self.stack1.pop()) + # If stack2 is still empty, the queue is empty, so return None + if not self.stack2: + return None + # Otherwise, pop the top element from stack2 and return it + return self.stack2.pop() + +# Create an instance of the Queue class +queue = Queue() + +# Enter into an infinite loop to prompt the user for operations +while True: + print("Select operation -\n" + "1. Enqueue\n" + "2. Dequeue\n" + "3. Quit") + + # Prompt the user for their choice of operation + choice = int(input("Enter choice: ")) + + if choice == 1: + # If the user selects option 1, prompt them for a value to enqueue and enqueue it + val = int(input("Enter value to enqueue: ")) + queue.enqueue(val) + print("Enqueued value:", val) + elif choice == 2: + # If the user selects option 2, dequeue a value and print it, or indicate that the queue is empty + val = queue.dequeue() + if val: + print("Dequeued value:", val) + else: + print("Queue is empty.") + elif choice == 3: + # If the user selects option 3, quit the loop + break + else: + # If the user selects an invalid option, prompt them to try again + print("Invalid choice. Please try again.") + diff --git a/Queues/stack_using_queue.cpp b/Queue/stack_using_queue.cpp similarity index 100% rename from Queues/stack_using_queue.cpp rename to Queue/stack_using_queue.cpp diff --git a/Queues/queue.go b/Queues/queue.go deleted file mode 100644 index 2b11d1fa..00000000 --- a/Queues/queue.go +++ /dev/null @@ -1,57 +0,0 @@ -// Implement Queue Data Structure -// TODO: Make Queue Generic - -package main - -import "fmt" - -type Queue []string - -// IsEmpty func checks if the queue is empty -func (q *Queue) IsEmpty() bool{ - return len(*q) == 0 -} - -// EnQueue func enqueues a new value in the Queue -func (q *Queue) EnQueue(str string) { - fmt.Printf("%s entered queue\n", str) - *q = append(*q, str) -} -// DeQueue func dequeues "first in" as in FIFO, value in the Queue -func (q *Queue) DeQueue() (string, bool) { - if q.IsEmpty() { - return "", false - } else { - // pick first element from queue - element := (*q)[0] - // slice off the picked element - *q = (*q)[1:] - return element, true - } -} - -func main() { - var queue Queue - - // DeQueue empty queue - ele, msg := queue.DeQueue() - if msg == true { - fmt.Printf("%s", ele) - } else { - fmt.Printf("Nothing to delete!\n") - } - - // EnQueue 3 values in queue - queue.EnQueue("Hello0") - queue.EnQueue("Hello1") - queue.EnQueue("Hello2") - - // DeQueue all values - for len(queue) > 0 { - ele, msg := queue.DeQueue() - if msg == true { - fmt.Printf("%s deleted from queue\n", ele) - } - } - -} diff --git a/README.md b/README.md index eaae885b..9d1fec1d 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,716 @@ + +

+ + + Data Structures & Algorithms + +

+
+ # Implementation of well known Data Structures and Algorithms -## Contributing +Contributions are welcome! +You can join the fun by following our [contributing guide](https://github.com/akgmage/data-structures-and-algorithms/blob/main/CONTRIBUTING.md). + +# Table of contents + +- [Intro](https://github.com/akgmage/data-structures-and-algorithms#intro) + - [Time complexity](https://github.com/akgmage/data-structures-and-algorithms#time-complexity) + - [Calculation Rules](https://github.com/akgmage/data-structures-and-algorithms#calculation-rules) + - [Loops](https://github.com/akgmage/data-structures-and-algorithms#loops) + - [Order of Magnitude](https://github.com/akgmage/data-structures-and-algorithms#order-of-magnitude) + - [Phases](https://github.com/akgmage/data-structures-and-algorithms#phases) + - [Several Variables](https://github.com/akgmage/data-structures-and-algorithms#several-variables) + - [Recursion](https://github.com/akgmage/data-structures-and-algorithms#recursion) + - [Complexity classes](https://github.com/akgmage/data-structures-and-algorithms#complexity-classes) + - [Estimating efficiency](https://github.com/akgmage/data-structures-and-algorithms#estimating-efficiency) +- [Data structures](https://github.com/akgmage/data-structures-and-algorithms#data-structures) +- [Sorting](https://github.com/akgmage/data-structures-and-algorithms#sorting) + - [Well Known Sorting Algorithms](https://github.com/akgmage/data-structures-and-algorithms#well-known-sorting-algorithms) +- [Binary Search](https://github.com/akgmage/data-structures-and-algorithms#binary-search) + - [Method 1](https://github.com/akgmage/data-structures-and-algorithms#method-1) + - [Example](https://github.com/akgmage/data-structures-and-algorithms#example) + - [Method 2](https://github.com/akgmage/data-structures-and-algorithms#method-2) +- [Greedy algorithms](https://github.com/akgmage/data-structures-and-algorithms#greedy-algorithms) + - [Coin problem](https://github.com/akgmage/data-structures-and-algorithms#coin-problem) + - [General case](https://github.com/akgmage/data-structures-and-algorithms#general-case) +- [Dynamic programming](https://github.com/akgmage/data-structures-and-algorithms#dynamic-programming) + - [Coin problem](https://github.com/akgmage/data-structures-and-algorithms#coin-problem-1) + - [Using memoization](https://github.com/akgmage/data-structures-and-algorithms#using-memoization) +- [Amortized analysis](https://github.com/akgmage/data-structures-and-algorithms#amortized-analysis) +- [Pattern 1: Two Pointers](https://github.com/akgmage/data-structures-and-algorithms#pattern-1-two-pointers) + - [Practice problems for two pointers](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-two-pointers) +- [Pattern 2: Fast and Slow Pointers](https://github.com/akgmage/data-structures-and-algorithms#pattern-2-fast-and-slow-pointers) + - [Practice problems for fast and slow pointers](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-fast-and-slow-pointers) +- [Pattern 3: Sliding Window](https://github.com/akgmage/data-structures-and-algorithms#pattern-3-sliding-window) + - [Practice problems for sliding window](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-sliding-window) +- [Pattern 4: Merge Interval](https://github.com/akgmage/data-structures-and-algorithms#pattern-4-merge-interval) + - [Practice problems for merge intervals](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-merge-intervals) +- [Pattern 5: In-place Reversal of a Linked List](https://github.com/akgmage/data-structures-and-algorithms#pattern-5-in-place-reversal-of-a-linked-list) + - [Practice problems for in-place reversal of a linked list](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-in-place-reversal-of-a-linked-list) +- [Pattern 6: Two Heaps](https://github.com/akgmage/data-structures-and-algorithms#pattern-6-two-heaps) + - [Practice problems for two heaps](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-two-heaps) +- [Pattern 7: K-way Merge](https://github.com/akgmage/data-structures-and-algorithms#pattern-7-k-way-merge) + - [Practice problems for k-way Merge](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-k-way-merge) +- [Pattern 8: Top K Elements](https://github.com/akgmage/data-structures-and-algorithms#pattern-8-top-k-elements) + - [Practice problems for Top K Elements](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-top-k-elements) + +# Intro + +The design of algorithms consists of problem solving and mathematical +thinking. Skills for analyzing problems and solving them creatively are needed. +An algorithm for solving a problem has to be both correct and efficient, and the +core of the problem is often about inventing an efficient algorithm. + +# Time complexity + +The efficiency of algorithms is important. Usually, it is easy to design an algorithm that solves the problem slowly, but the real challenge is to invent a fast algorithm. The time complexity of an algorithm estimates how much time the algorithm will use for some input. The idea is to represent the efficiency as a function whose parameter is the size of the input. By calculating the time complexity, we can find out whether the algorithm is fast enough without implementing it. + +## Calculation rules + +The time complexity of an algorithm is denoted `O(...)` where the three dots represent some function. Usually, the variable n denotes the input size. For example, if the input is an array of numbers, n will be the size of the array, and if the input is a string, n will be the length of the string. + +## Loops + +A common reason why an algorithm is slow is that it contains many loops that go through the input. The more nested loops the algorithm contains, the slower it is. If there are k nested loops, the time complexity is O(n^k). + +For example, the time complexity of the following code is O(n): + +```cpp +for (int i = 1; i <= n; i++) { + // code +} +``` + +And the time complexity of the following code is O(n^2): + +```cpp +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + // code + } +} +``` + +## Order of magnitude + +A time complexity does not tell us the exact number of times the code inside a loop is executed, but it only shows the order of magnitude. In the following examples, the code inside the loop is executed 3n, n + 5 and [n / 2] times, but the time complexity of each code is O(n). + +```cpp +for (int i = 1; i <= 3*n; i++) { + // code +} +``` + +```cpp +for (int i = 1; i <= n+5; i++) { + // code +} +``` + +```cpp +for (int i = 1; i <= n; i += 2) { +// code +} +``` + +As another example, the time complexity of the following code is O(n^2): + +```cpp +for (int i = 1; i <= n; i++) { + for (int j = i+1; j <= n; j++) { + // code + } +} +``` + +## Phases + +If the algorithm consists of consecutive phases, the total time complexity is the largest time complexity of a single phase. The reason for this is that the slowest phase is usually the bottleneck of the code. For example, the following code consists of three phases with time complexities O(n), O(n^2) and O(n). Thus, the total time complexity is O(n^2). + +```cpp +for (int i = 1; i <= n; i++) { + // code +} +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + // code + } +} +for (int i = 1; i <= n; i++) { +// code +} +``` + +## Several variables + +Sometimes the time complexity depends on several factors. In this case, the time complexity formula contains several variables. For example, the time complexity of the following code is O(nm): + +```cpp +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + // code + } +} +``` + +## Recursion + +The time complexity of a recursive function depends on the number of times the function is called and the time complexity of a single call. The total time complexity is the product of these values. +For example, consider the following function: + +```cpp +void f(int n) { + if (n == 1) return; + f(n-1); +} +``` + +The call f(n) causes n function calls, and the time complexity of each call is O(1). +Thus, the total time complexity is O(n). + +As another example, consider the following function: + +```cpp +void g(int n) { + if (n == 1) return; + g(n-1); + g(n-1); +} +``` + +In this case each function call generates two other calls, except for n = 1. Let us see what happens when g is called with parameter n. The following table shows the function calls produced by this single call: + +function call number of calls +g(n)        1 +g(n - 1)        2 +g(n - 2)        4 +....        +g(1)        2^(n - 1) + +Based on this, the time complexity is +1 + 2 + 4 + ... + 2^(n - 1) = 2^(n - 1) = O(2^n). + +# Complexity classes + +The following list contains common time complexities of algorithms: + +O(1) : The running time of a constant-time algorithm does not depend on the input size. A typical constant-time algorithm is a direct formula that calculates the answer. + +O(logn) : A logarithmic algorithm often halves the input size at each step. The running time of such an algorithm is logarithmic, because log base 2 n equals the number of times n must be divided by 2 to get 1. + +O(√n) : A square root algorithm is slower than O(logn) but faster than O(n). A special property of square roots is that √n = n / √n , so the square root √n lies, in some sense, in the middle of the input. + +O(n) : A linear algorithm goes through the input a constant number of times. This is often the best possible time complexity, because it is usually necessary to access each input element at least once before reporting the answer. + +O(nlogn) : This time complexity often indicates that the algorithm sorts the input, because the time complexity of efficient sorting algorithms is O(nlogn). Another possibility is that the algorithm uses a data structure where each operation takes O(logn) time. + +O(n^2) : A quadratic algorithm often contains two nested loops. It is possible to go through all pairs of the input elements in O(n^2) time. + +O(n^3) : A cubic algorithm often contains three nested loops. It is possible to go through all triplets of the input elements in O(n^3) time. + +O(2^n) : This time complexity often indicates that the algorithm iterates through all subsets of the input elements. For example, the subsets of {1,2,3} are Φ, {1}, {2}, {3}, {1,2}, {1,3}, {2,3} and {1,2,3}. + +O(n!) : This time complexity often indicates that the algorithm iterates through all permutations of the input elements. For example, the permutations of {1,2,3} are (1,2,3), (1,3,2), (2,1,3 , (2,3,1), (3,1,2) and (3,2,1). + +An algorithm is polynomial if its time complexity is at most O(n^k) where k is a constant. All the above time complexities except O(2^n) and O(n!) are polynomial. +In the practice, the constant k is usually small, and therefore a polynomial time complexity roughly means that the algorithm is efficient. Still, there are many important problems for which no polynomial algorithm is known, i.e., nobody knows how to solve them efficiently. NP-hard problems are an important set of problems, for which no polynomial algorithm is known. + +# Estimating efficiency + +By calculating the time complexity of an algorithm, it is possible to check, before implementing the algorithm, that it is efficient enough for the problem. The starting point for estimations is the fact that a modern computer can perform some hundreds of millions of operations in a second. For example, assume that the time limit for a problem is one second and the input size is n = 10 ^ 5. If the time complexity is O(n^2), the algorithm will perform about (10 ^ 5) ^ 2 = 10 ^ 10 operations. This should take at least some tens of seconds, so the algorithm seems to be too slow for solving the problem. + +On the other hand, given the input size, we can try to guess the required time complexity of the algorithm that solves the problem. The following table contains some useful estimates assuming a time limit of one second. +input size required time complexity +n <= 10        O(n!) +n <= 20        O(2 ^ n) +n <= 500         O(n ^ 3) +n <= 5000       O(n ^ 2) +n <= 10 ^ 6      O(nlogn) or O(n) +n is large      O(1) or O(logn) + +For example, if the input size is n = 10 ^ 5, it is probably expected that the time complexity of the algorithm is O(n) or O(nlogn). This information makes it easier to design the algorithm, because it rules out approaches that would yield an algorithm with a worse time complexity. Still, it is important to remember that a time complexity is only an estimate of efficiency, because it hides the constant factors. For example, an algorithm that runs in O(n) time may perform n/2 or 5n operations. This has an important effect on the actual running time of the algorithm. + +# Data structures + +A data structure is a way to store data in the memory of a computer. It is +important to choose an appropriate data structure for a problem, because each +data structure has its own advantages and disadvantages. The crucial question +is: which operations are efficient in the chosen data structure? + +- `Dynamic arrays` : A dynamic array is an array whose size can be changed during the execution of the program. +- `Set structures` : A set is a data structure that maintains a collection of elements. The basic operations of sets are element insertion, search and removal. +- `Map structures` : A map is a generalized array that consists of key-value-pairs. While the keys in an ordinary array are always the consecutive integers 0,1,...,n-1, where n is the size of the array, the keys in a map can be of any data type and they do not have to be consecutive values. +- `Deque` : A deque is a dynamic array whose size can be efficiently changed at both ends of the array. Like a vector, a deque provides the functions push_back and pop_back, but it also includes the functions push_front and pop_front which are not available in a vector. +- `Stack` : A stack is a data structure that provides two O(1) time operations: adding an element to the top, and removing an element from the top. It is only possible to access the top element of a stack. +- `Queue` : A queue also provides two O(1) time operations: adding an element to the end of the queue, and removing the first element in the queue. It is only possible to access the first and last element of a queue. +- `Priority queue` : A priority queue maintains a set of elements. The supported operations are insertion and, depending on the type of the queue, retrieval and removal of either the minimum or maximum element. Insertion and removal take O(logn) time, and retrieval takes O(1) time. While an ordered set efficiently supports all the operations of a priority queue, the benefit of using a priority queue is that it has smaller constant factors. A priority queue is usually implemented using a heap structure that is much simpler than a balanced binary tree used in an ordered set. + +# Sorting + +Sorting is a fundamental algorithm design problem. Many efficient algorithms +use sorting as a subroutine, because it is often easier to process data if the +elements are in a sorted order. +For example, the problem ”does an array contain two equal elements?” is easy +to solve using sorting. If the array contains two equal elements, they will be next +to each other after sorting, so it is easy to find them. Also, the problem ”what is +the most frequent element in an array?” can be solved similarly. +There are many algorithms for sorting, and they are also good examples of +how to apply different algorithm design techniques. The efficient general sorting +algorithms work in O(nlogn) time, and many algorithms that use sorting as a +subroutine also have this time complexity. + +```js +heights = [6, 5, 4, 5, 2, 3]; +heights.sort(); +Output: [2, 3, 4, 5, 5, 6]; +``` + +Even though languages have built-in sorting method, sorting is a great example of how there may be many ways to think about the same problem, some perhaps better than others. Understanding sorting is a traditional first step towards mastery of algorithms and computer science. + +### Well known sorting algorithms + +- Bubble Sort [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.py) +- Insertion Sort [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.py) +- Selection Sort [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.py) +- Merge Sort [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.py) +- Quick Sort [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.cpp) [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.go) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.py) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.js) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.java) + +# Binary Search + +A general method for searching for an element in an array is to use a for loop +that iterates through the elements of the array. For example, the following code +searches for an element x in an array: + +```cpp +for (int i = 0; i < n; i++) { + if (array[i] == x) { + // x found at index i + } +} +``` + +The time complexity of this approach is O(n), because in the worst case, it +is necessary to check all elements of the array. If the order of the elements is +arbitrary, this is also the best possible approach, because there is no additional +information available where in the array we should search for the element x. +However, if the array is sorted, the situation is different. In this case it is +possible to perform the search much faster, because the order of the elements in +the array guides the search. The following binary search algorithm efficiently +searches for an element in a sorted array in O(logn) time. + +## Method 1 + +The usual way to implement binary search resembles looking for a word in a +dictionary. The search maintains an active region in the array, which initially +contains all array elements. Then, a number of steps is performed, each of which +halves the size of the region. +At each step, the search checks the middle element of the active region. If +the middle element is the target element, the search terminates. Otherwise, the +search recursively continues to the left or right half of the region, depending on +the value of the middle element. +The above idea can be implemented as follows: + +```cpp +int a = 0, b = n-1; +while (a <= b) { + int k = (a+b)/2; + if (array[k] == x) { + // x found at index k + } + if (array[k] > x) b = k-1; + else a = k+1; +} +``` + +## Example + +[Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Binary%20Search/binary_search_iterative.go) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Binary%20Search/binary_search.py) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Binary%20Search/binary_search.cpp) [JS](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Binary%20Search/binary_search.js) + +## Method 2 + +An alternative method to implement binary search is based on an efficient way to +iterate through the elements of the array. The idea is to make jumps and slow +the speed when we get closer to the target element. +The search goes through the array from left to right, and the initial jump +length is n/2. At each step, the jump length will be halved: first n/4, then n/8, +n/16, etc., until finally the length is 1. After the jumps, either the target element +has been found or we know that it does not appear in the array. +The following code implements the above idea: + +```cpp +int k = 0; +for (int b = n/2; b >= 1; b /= 2) { + while (k+b < n && array[k+b] <= x) k += b; +} +if (array[k] == x) { + // x found at index k +} +``` + +During the search, the variable b contains the current jump length. The +time complexity of the algorithm is O(logn), because the code in the while loop is +performed at most twice for each jump length. + +# Greedy algorithms + +A greedy algorithm constructs a solution to the problem by always making a +choice that looks the best at the moment. A greedy algorithm never takes back +its choices, but directly constructs the final solution. For this reason, greedy +algorithms are usually very efficient. +The difficulty in designing greedy algorithms is to find a greedy strategy that +always produces an optimal solution to the problem. The locally optimal choices +in a greedy algorithm should also be globally optimal. It is often difficult to argue +that a greedy algorithm works. + +## Coin problem + +As a first example, we consider a problem where we are given a set of coins and +our task is to form a sum of money n using the coins. The values of the coins are +coins = `{c1, c2,..., ck}`, and each coin can be used as many times we want. What +is the minimum number of coins needed? +For example, if the coins are the euro coins (in cents) +`{1,2,5,10,20,50,100,200}` +and `n = 520`, we need at least four coins. The optimal solution is to select coins +`200 + 200 + 100 + 20` whose sum is `520`. + +A simple greedy algorithm to the problem always selects the largest possible coin, +until the required sum of money has been constructed. This algorithm works in +the example case, because we first select two `200` cent coins, then one `100` cent +coin and finally one `20` cent coin. But does this algorithm always work? +It turns out that if the coins are the euro coins, the greedy algorithm always +works, i.e., it always produces a solution with the fewest possible number of coins. +The correctness of the algorithm can be shown as follows: +First, each coin `1, 5, 10, 50 and 100` appears at most once in an optimal +solution, because if the solution would contain two such coins, we could replace them by one coin and obtain a better solution. For example, if the solution would +contain coins `5 + 5`, we could replace them by coin 10. +In the same way, coins 2 and 20 appear at most twice in an optimal solution, +because we could replace coins `2 + 2 + 2` by coins `5 + 1` and coins `20 + 20 + 20` by +coins 50 + 10. Moreover, an optimal solution cannot contain coins `2 + 2 + 1` or +`20 + 20 + 10`, because we could replace them by coins 5 and 50. +Using these observations, we can show for each coin x that it is not possible +to optimally construct a sum x or any larger sum by only using coins that are +smaller than x. For example, `if x = 100`, the largest optimal sum using the smaller +coins is `50 + 20 + 20 + 5 + 2 + 2 = 99`. Thus, the greedy algorithm that always selects +the largest coin produces the optimal solution. +This example shows that it can be difficult to argue that a greedy algorithm +works, even if the algorithm itself is simple. + +## General case + +In the general case, the coin set can contain any coins and the greedy algorithm +does not necessarily produce an optimal solution. +We can prove that a greedy algorithm does not work by showing a counterexample +where the algorithm gives a wrong answer. In this problem we can easily +find a counterexample: if the coins are `{1,3,4}` and the target sum is `6`, the greedy +algorithm produces the solution 4 + 1 + 1 while the optimal solution is `3 + 3`. +It is not known if the general coin problem can be solved using any greedy +algorithm. + +# Dynamic programming + +Dynamic programming is a technique that combines the correctness of complete +search and the efficiency of greedy algorithms. Dynamic programming can +be applied if the problem can be divided into overlapping subproblems that can +be solved independently. + +There are two uses for dynamic programming: + +- Finding an optimal solution: We want to find a solution that is as large as possible or as small as possible. +- Counting the number of solutions: We want to calculate the total number of possible solutions. + +## Coin problem + +We first focus on a problem that we have already seen in Greedy Algorithm Given a set +of coin values coins = `{c1, c2,..., ck}` and a target sum of money n, our task is to +form the sum n using as few coins as possible. +We solved the problem using a greedy algorithm that always +chooses the largest possible coin. The greedy algorithm works, for example, when +the coins are the euro coins, but in the general case the greedy algorithm does +not necessarily produce an optimal solution. +Now is time to solve the problem efficiently using dynamic programming, so +that the algorithm works for any coin set. The dynamic programming algorithm +is based on a recursive function that goes through all possibilities how to form +the sum, like a brute force algorithm. However, the dynamic programming +algorithm is efficient because it uses memoization and calculates the answer to +each subproblem only once. + +The idea in dynamic programming is to formulate the problem recursively so +that the solution to the problem can be calculated from solutions to smaller +subproblems. In the coin problem, a natural recursive problem is as follows: what +is the smallest number of coins required to form a sum x? +Let solve(x) denote the minimum number of coins required for a sum x. +The values of the function depend on the values of the coins. For example, if +`coins = {1,3,4}`, the first values of the function are as follows: + +```solve(0) = 0 +solve(1) = 1 +solve(2) = 2 +solve(3) = 1 +solve(4) = 1 +solve(5) = 2 +solve(6) = 2 +solve(7) = 2 +solve(8) = 2 +solve(9) = 3 +solve(10) = 3 +``` + +For example, `solve(10) = 3`, because at least 3 coins are needed to form the +sum `10`. The optimal solution is `3 + 3 + 4 = 10`. +The essential property of solve is that its values can be recursively calculated +from its smaller values. The idea is to focus on the first coin that we choose for +the sum. For example, in the above scenario, the first coin can be either `1, 3 +or 4`. If we first choose coin `1`, the remaining task is to form the sum 9 using +the minimum number of coins, which is a subproblem of the original problem. +Of course, the same applies to coins` 3 and 4`. + +Thus, we can use the following recursive formula to calculate the minimum number of coins: +solve(x) = min(solve(x - 1) + 1, +solve(x - 3) + 1, +solve(x - 4) + 1). + +The base case of the recursion is solve(0) Æ 0, because no coins are needed to form an empty sum. For example, +solve(10) = solve(7) + 1 = solve(4) + 2 = solve(0) + 3 = 3. + +Now we are ready to give a general recursive function that calculates the minimum number of coins needed to form a sum x: +solve(x) = Infinity if x < 0 +solve(x) = 0 if x == 0 +solve(x) = min(c --> coins) ==> solve(x - c) + 1, if x > 0 + +Once a recursive function that solves the problem has been found, we can +directly implement a solution, (the constant INF denotes infinity): + +```cpp +int solve(int x) { + if (x < 0) return INF; + if (x == 0) return 0; + int best = INF; + for (auto c : coins) { + best = min(best, solve(x-c)+1); + } + return best; +} +``` + +Still, this function is not efficient, because there may be an exponential number of ways to construct the sum. However, next we will see how to make the function efficient using a technique called memoization. + +## Using memoization + +The idea of dynamic programming is to use memoization to efficiently calculate values of a recursive function. This means that the values of the function are stored in an array after calculating them. For each parameter, the value of the function is calculated recursively only once, and after this, the value can be +directly retrieved from the array. + +In this problem, we use arrays + +```cpp +bool ready[N]; +int value[N]; +``` + +where `ready[x]` indicates whether the value of `solve(x)` has been calculated, +and if it is, `value[x]` contains this value. The constant `N` has been chosen so that all required values fit in the arrays. +Now the function can be efficiently implemented as follows: + +```cpp +int solve(int x) { + if (x < 0) return INF; + if (x == 0) return 0; + if (ready[x]) return value[x]; + int best = INF; + for (auto c : coins) { + best = min(best, solve(x-c)+1); + } + value[x] = best; + ready[x] = true; + return best; +} +``` + +# Amortized analysis + +The time complexity of an algorithm is often easy to analyze just by examining the structure of the algorithm: what loops does the algorithm contain and how many times the loops are performed. However, sometimes a straightforward analysis does not give a true picture of the efficiency of the algorithm. +Amortized analysis can be used to analyze algorithms that contain operations whose time complexity varies. The idea is to estimate the total time used to all such operations during the execution of the algorithm, instead of focusing on individual operations. + +# Pattern 1: Two Pointers + +As the name suggests, the two pointers pattern uses two pointers to iterate over an array or list until the conditions of the problem are satisfied. This is useful because it allows us to keep track of the values of two different indexes in a single iteration. Whenever there’s a requirement to find two data elements in an array that satisfy a certain condition, the two pointers pattern should be the first strategy to come to mind. + +The pointers can be used to iterate the data structure in one or both directions, depending on the problem statement. For example, to identify whether a string is a palindrome, we can use one pointer to iterate the string from the beginning and the other to iterate it from the end. At each step, we can compare the values of the two pointers and see if they meet the palindrome properties. + +## Practice problems for two pointers + +- Two sum [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.java) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.py) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.js) + +- Three Number Sum [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.go) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.java) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.py) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.js) + +- Valid Pallindrome [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.go) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.js) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.py) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.java) + +- Reverse Word in a String [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/reverse_words_in_a_string.go) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/reverse_words_in_a_string.jd) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/reverse_words_in_a_string.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/reverse_words_in_a_string.java) + +- Valid Pallindrome II [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.py) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.js) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.java) + +# Pattern 2: Fast and Slow Pointers + +The fast and slow pointer technique (also known as the tortoise and hare algorithm) uses two pointers to determine traits about directional data structures. This can be an array, singly-linked list, or a graph. + +Similar to the two pointers pattern, the fast and slow pointers pattern uses two pointers to traverse an iterable data structure at different speeds. It’s usually used to identify distinguishable features of directional data structures, such as a linked list or an array. + +The pointers can be used to traverse the array or list in either direction, however, one moves faster than the other. Generally, the slow pointer moves forward by a factor of one, and the fast pointer moves by a factor of two in each step. However, the speed can be adjusted according to the problem statement. + +Unlike the two pointers approach, which is concerned with data values, the fast and slow pointers approach is used to determine data structure traits using indices in arrays or node pointers in linked lists. The approach is commonly used to detect cycles in the given data structure, so it’s also known as Floyd’s cycle detection algorithm. + +The key idea is that the pointers start at the same location, but they move forward at different speeds. If there is a cycle, the two are bound to meet at some point in the traversal. To understand the concept, think of two runners on a track. While they start from the same point, they have different running speeds. If the race track is a circle, the faster runner will overtake the slower one after completing a lap. On the other hand, if the track is straight, the faster runner will end the race before the slower one, hence never meeting on the track again. The fast and slow pointers pattern uses the same intuition. + +## Practice problems for fast and slow pointers + +- Linked List cycle detection [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Fast%20and%20Slow%20Pointers/linked_floyds_cycle_detection.cpp) [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/floyds_cycle_detection.go) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/floyds_cycle_detection.java) +- Find middle of Linked List [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Fast%20and%20Slow%20Pointers/linked_list_compute_midpoint.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Fast%20and%20Slow%20Pointers/linked_list_find_middle.py) +- Happy Number [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Fast%20and%20Slow%20Pointers/happy_number.go) +- Pallindrome Linked List [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_pallindrome.cpp) +- Remove Kth node from end [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_kth_from_end.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_remove_nth_node_from_end.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/remove_kth_node_from_end.py) +- Linked List Sort List [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/liniked_list_sort_list.cpp) + +# Pattern 3: Sliding Window + +The Sliding window is a problem-solving technique of data structure and algorithm for problems that apply arrays or lists. These problems are painless to solve using a brute force approach in O(n²) or O(n³). However, the Sliding window technique can reduce the time complexity to O(n). + +The sliding window pattern is a computational method aimed at reducing the use of nested loops in an algorithm. It’s a variation of the two pointers pattern, where the pointers can be used to set window bounds. + +A window is a sublist formed over a part of an iterable data structure. It can be used to slide over the data in chunks corresponding to the window size. The sliding window pattern allows us to process the data in segments instead of the entire list. The segment or window size can be set according to the problem’s requirements. For example, if we have to find three consecutive integers with the largest sum in an array, we can set the window size to 3. This will allow us to process the data three elements at a time. + +Why is this method more efficient? It isn’t if, for each window, we iterate over all the elements of the window because that gives us the same O(kn) time complexity. + +Instead, what if we focused on the element entering the window and the one leaving it? For example, after calculating the sum of the first three elements, we move the window one step forward, subtract the element that is no longer in the window from the sum, and add the new element that has entered it. Next we check if the new sum is greater than the first. If it is, we update the max sum found so far. Now, each time we move the window forward, we perform at most four operations, reducing the time complexity to O(4n), that is, O(n). + +## Practice problems for sliding window + +- Find Maximum in Sliding Window [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Sliding%20Window/sliding_window_max.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Sliding%20Window/sliding_window_max.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Sliding%20Window/sliding_window_max.py) +- Minimum Window Subsequence +- Repeated DNA Sequences +- Minimum Window Substring +- Longest Substring without Repeating Characters [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Sliding%20Window/longest_substring_without_repeating_characters.go) + +# Pattern 4: Merge Interval + +The merge intervals pattern deals with problems involving overlapping intervals. Each interval is represented by a start and an end time. For example, an interval of [10,20] seconds means that the interval starts at 10 seconds and ends at 20seconds, such that both 10 and time 20 are included in the interval. + +The most common problems solved using this pattern are scheduling problems. + +The key to understanding this pattern and exploiting its power lies in understanding how any two intervals may overlap. + +Many problems in the real world use the merge intervals pattern. Let’s look at some examples. + +- Display busy schedule: Display the busy hours of a user to other users without revealing the individual meeting slots in a calendar. + +- Schedule a new meeting: Add a new meeting to the tentative meeting schedule of a user in such a way that no two meetings overlap each other. + +- Task scheduling in operating systems (OS): Schedule tasks for the OS based on task priority and the free slots in the machine’s processing schedule. + +## Practice problems for merge intervals + +- Merge Intervals [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.java) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.py) [Javacript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.js) +- Insert Interval [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.py) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.js) +- Interval List Intersections +- Employee Free Time +- Meeting Rooms + +# Pattern 5: In-place Reversal of a Linked List + +The in-place reversal of a linked list pattern allows us to reverse a linked list without any additional memory, using only the given nodes. + +Many problems require a reversal of a set of nodes in a linked list without using additional memory. In such cases, using the in-place reversal pattern is the simplest solution. Instead of making a new linked list with reversed links, we can do it in place, without using additional memory. + +How can we achieve an in-place reversal of nodes? We iterate in a linked list and keep track of the current node, the next node, and the previous node simultaneously. Keeping track of the nodes allows us to easily change the links between them and make them point to a different node than before. + +When solving such problems, the naive approach of iterating the linked list using nested loops takes O(n²) time. However, using the in-place reversal pattern, the time complexity is O(n) +time, since we use a single loop to iterate the linked list. + +Similarly, for space complexity: the naive approach requires the use of additional memory—if a linked list contains thousands of nodes, we’d need to allocate a lot of additional memory resources to solve the problem. However, the in-place reversal of a linked pattern will use only O(1) space. + +## Practice problems for in-place reversal of a linked list + +- Reverse Linked List [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_reverse.cpp) +- Reverse Nodes in k-group +- Reorder List +- Swapping Nodes in a Linked List +- Swapping Nodes in Pairs [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_swap_nodes_in_pair.cpp) +- Reverse Nodes in Even Length Groups + +# Pattern 6: Two Heaps + +As the name suggests, the two heaps pattern uses either two min-heaps, two max-heaps, or a min-heap and a max-heap simultaneously to solve the problem. + +Given that there are n elements in a heap, it takes O(log n) time to insert an element in it, O(log n) time to remove an element from it, and O(1) time to access the element at the root of the heap. The root stores the smallest element in the case of a min-heap and the largest element in a max-heap. + +In some problems, we’re given a set of data such that it can be divided into two parts. We can either use the first part to find the smallest element using the min-heap and the second part to find the largest element using the max-heap, or we can do the reverse and use the first part to find the largest element using the max-heap and the second part to find the smallest element using the min-heap. + +There might be cases where we need to find the two largest numbers from two different data sets. We’ll use two max-heaps to store two different data sets in that case. In other cases, we might need to find the two smallest numbers from two different data sets, and then we would use two min-heaps. + +Many problems in the real world use the two heaps pattern. Let’s look at some examples. + +- Video streaming: During a user session, there is often a possibility that packet drops and buffering might occur. We want to record the median number of buffering events that might occur in a particular session, which could then be used to improve the user experience. + +- Netflix: As part of a demographic study, we’re interested in the median age of our viewers. We want to implement a functionality whereby the median age can be updated efficiently whenever a new user signs up for video streaming. + +## Practice problems for two heaps + +- Maximize Capital +- Find Median from a data stream +- Schedule Tasks on minimum machines + +# Pattern 7: K-way Merge + +The k-way merge pattern helps to solve problems involving a list of sorted arrays. + +Here is what the pattern looks like: + +1. Insert the first element of each array in a min-heap. +2. Next, remove the smallest element from the heap and add it to the merged array. +3. Keep track of which array each element comes from. +4. Then, insert the next element of the same array into the heap. +5. Repeat steps 2 to 4 to fill the merged array in sorted order. + +Many problems in the real world use the k-way merge pattern. Let’s look at some examples. + +- Merge tweets in twitter feed: Sometimes we need to implement a module that adds a user’s Tweets into an already populated Twitter feed in chronological order. + +- Used in external sorting procedures: When an algorithm is processing huge amounts of data, it needs to repeatedly fetch it from external storage because RAM capacity is fixed. To overcome the speed limitation of external storage, k-way merges are used in external sorting. Let’s consider a case where we need to perform six merges. A binary merge requires three merge passes while a 6-way merge only requires one pass. K-way merge reduces the number of accesses to external storage, which in turn greatly improves performance when dealing with large amounts of data. + +## Practice problems for k-way Merge + +- Merge Sorted Array +- Kth smallest number in M sorted list +- Find K pairs with smallest sums +- Merge K sorted lists +- Kth Smallest element in a sorted matrix +- Median of two sorted arrays + +# Pattern 8: Top K Elements + +The top K elements pattern helps find some specific k number of elements from the given data with optimum time complexity. Many problems ask us to find the top, the smallest, or the most/least frequent k elements in an unsorted list of elements. To solve such problems, sorting the list takes O(nlog(n)) time, then finding the k elements takes O(k) time. However, the top k elements pattern can allow us to solve the problem using O(n logk) time without sorting the list first. + +Which data structure can we use to solve such problems? The best data structure to keep track of the smallest or largest k elements is heap. With this pattern, we either use a max-heap or a min-heap to find the smallest or largest k elements, respectively. + +For example, let’s look at how this pattern takes steps to solve the problem of finding the top k largest elements (using min-heap) or top k smallest elements (using max-heap): +Insert the first k elements from the given set of elements to the min-heap or max-heap. + +Iterate through the rest of the elements. + +For min-heap, if you find the larger element, remove the top (smallest number) of the min-heap and insert the new larger element. +For max-heap, if you find the smaller element, remove the top (largest number) of the max-heap and insert the new smaller element. +Iterating the complete list takes O(n) time, and the heap takes O(logk) time for insertion. However, we get the O(1) access to the k elements using the heap. -Contributions are always welcome! +Many problems in the real world use the top K elements pattern. Let’s look at some examples. -Check out [contributing.md](https://github.com/akgmage/data-structures-and-algorithms/blob/main/CONTRIBUTING.md) +- Uber: Select at least the n nearest drivers within the user’s vicinity, avoiding the drivers that are too far away. -## Fork this repository +- Stocks: Given the set of IDs of brokers, determine the top K broker’s performance with the frequently repeated IDs in the given data set. -Fork this repository by clicking on the fork button on the top of this page. This will create a copy of this repository in your account. +## Practice problems for Top K Elements -- [How to Fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo) +- Kth largest element in a stream +- Reorganize string +- K closest point to origin +- Top K frequent element +- Kth largest element in an array +- Kth smallest element in an BST diff --git a/Random_Problems/matrix_rotate_90_anti_clockwise.cpp b/Random_Problems/matrix_rotate_90_anti_clockwise.cpp deleted file mode 100644 index 162570f0..00000000 --- a/Random_Problems/matrix_rotate_90_anti_clockwise.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -using namespace std; -int main(){ - int Mat[10][10], R, C, value = 1; - cin >> R >> C; - // creating matrix - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - Mat[i][j] = value++; - } - } - // Transposing matrix - for(int i = 0; i < R; i++){ - for(int j = i + 1; j < C; j++){ - swap(Mat[i][j], Mat[j][i]); - } - } - //Rotate matrix 90 degrees anti clockwise - for(int i = 0; i < R; i++){ - int start = 0; - int end = C-1; - while(start < end){ - swap(Mat[start][i], Mat[end][i]); - start++; - end--; - } - } - // Display matrix - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cout << Mat[i][j] << " "; - } - cout << endl; - } - return 0; -} \ No newline at end of file diff --git a/Random_Problems/matrix_rotate_90_clockwise.cpp b/Random_Problems/matrix_rotate_90_clockwise.cpp deleted file mode 100644 index 47709a32..00000000 --- a/Random_Problems/matrix_rotate_90_clockwise.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -using namespace std; -int main(){ - int Mat[10][10], R, C, value = 1; - cin >> R >> C; - // creating matrix - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - Mat[i][j] = value++; - } - } - // Transposing matrix - for(int i = 0; i < R; i++){ - for(int j = i + 1; j < C; j++){ - swap(Mat[i][j], Mat[j][i]); - } - } - //Rotate matrix 90 degrees clockwise - for(int i = 0; i < R; i++){ - int start = 0; - int end = C-1; - while(start < end){ - swap(Mat[i][start], Mat[i][end]); - start++; - end--; - } - } - // Display matrix - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cout << Mat[i][j] << " "; - } - cout << endl; - } - return 0; -} \ No newline at end of file diff --git a/Random_Problems/matrix_search_in_sorted_mat.cpp b/Random_Problems/matrix_search_in_sorted_mat.cpp deleted file mode 100644 index cf034b43..00000000 --- a/Random_Problems/matrix_search_in_sorted_mat.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Search in a rowwise/colwise sorted matrix O(n + m) -#include -using namespace std; -bool search_in_sorted_mat(int Mat[][10], int R, int C, int key){ - int i = 0, flag = 0, j = C-1; - while(j >= 0 && i < R){ - if(Mat[i][j] == key){ - cout << "Found at position " << i << " " << j << endl; - flag = 1; - break; - } - else if(Mat[i][j] < key) - i++; - else if(Mat[i][j] > key){ - j--; - } - } - return flag == 1 ? true : false; -} -int main(){ - int Mat[10][10], R, C; - cin >> R >> C; - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cin >> Mat[i][j]; - } - } - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cout << Mat[i][j] << " "; - } - cout << endl; - } - int key; - cin >> key; - cout << search_in_sorted_mat(Mat, R, C, key); -} \ No newline at end of file diff --git a/Random_Problems/matrix_spiral_print.cpp b/Random_Problems/matrix_spiral_print.cpp deleted file mode 100644 index 185d237d..00000000 --- a/Random_Problems/matrix_spiral_print.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Printing Matrix in spiral order -#include -using namespace std; -void print_spiral(int Mat[][10], int R, int C){ - int startRow = 0, endRow = R-1, startCol = 0, endCol = C-1; - while(startRow <= endRow && startCol <= endCol){ - // Print first row - for(int i = startCol; i <= endCol; i++){ - cout << Mat[startRow][i]; - } - startRow++; - // print end col - for(int i = startRow; i <= endRow; i++){ - cout << Mat[i][endCol]; - } - endCol--; - // Print end row - if(endRow > startRow){ - for(int i = endCol; i >= startCol; i--){ - cout << Mat[endRow][i]; - } - endRow--; - } - // print start row - if(startCol < endCol){ - for(int i = endRow; i >= startRow; i--){ - cout << Mat[i][startCol]; - } - startCol++; - } - } -} -int main(){ - int Mat[10][10], R, C; - cin >> R >> C; - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cin >> Mat[i][j]; - } - } - for(int i = 0; i < R; i++){ - for(int j = 0; j < C; j++){ - cout << Mat[i][j] << " "; - } - cout << endl; - } - print_spiral(Mat, R, C); - return 0; -} \ No newline at end of file diff --git a/Random_Problems/maximum_subarray.cpp b/Random_Problems/maximum_subarray.cpp deleted file mode 100644 index 44e3670f..00000000 --- a/Random_Problems/maximum_subarray.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Subarray with maximum sum O(n^3) -#include -using namespace std; -int main(){ - int n; - cin >> n; - vector V(n); - for(int i = 0; i < n; i++){ - cin >> V[i]; - } - int current_sum = 0, left = 0, right = 0; - int maximum_sum = INT_MIN; - for(int i = 0; i < n; i++){ - for(int j = i; j < n; j++){ - current_sum = 0; - for(int k = i; k <= j; k++){ - current_sum += V[k]; - } - if(current_sum > maximum_sum){ - maximum_sum = current_sum; - left = i; - right = j; - } - //maximum_sum = max(maximum_sum, current_sum); - } - } - cout << maximum_sum << endl; - for(int i = left; i <= right; i++) - cout << V[i]; - return 0; -} \ No newline at end of file diff --git a/Random_Problems/maximum_subarray_cumulative.cpp b/Random_Problems/maximum_subarray_cumulative.cpp deleted file mode 100644 index 607d0e37..00000000 --- a/Random_Problems/maximum_subarray_cumulative.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Subarray with maximum sum using cumulative sum O(n^2) -#include -using namespace std; -int main(){ - int n; - cin >> n; - vector V(n); - vector W(n); - for(int i = 0; i < n; i++) W[i] = 0; - cin >> V[0]; - W[0] = V[0]; - for(int i = 1; i < n; i++){ - cin >> V[i]; - W[i] = W[i-1] + V[i]; - } - for(int i = 0; i < n; i++) cout << W[i] << " "; - cout << endl; - int current_sum = 0, left = 0, right = 0; - int maximum_sum = INT_MIN; - for(int i = 0; i < n; i++){ - for(int j = i; j < n; j++){ - current_sum = 0; - current_sum = W[j] - W[i - 1]; - if(current_sum > maximum_sum){ - maximum_sum = current_sum; - left = i; - right = j; - } - //maximum_sum = max(maximum_sum, current_sum); - } - } - cout << maximum_sum << endl; - for(int i = left; i <= right; i++) - cout << V[i]; - return 0; -} \ No newline at end of file diff --git a/Random_Problems/maximum_subarray_kadanes.cpp b/Random_Problems/maximum_subarray_kadanes.cpp deleted file mode 100644 index d50b768a..00000000 --- a/Random_Problems/maximum_subarray_kadanes.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// In computer science, the maximum sum subarray problem, also known as the maximum segment sum problem, -// is the task of finding a contiguous subarray with the largest sum, within a given one-dimensional array A[1...n] of numbers -// Subarray with maximum sum using kadane's algo O(n) -#include -using namespace std; -int main(){ - int n; - cin >> n; - vector V(n); - for(int i = 0; i < n; i++){ - cin >> V[i]; - } - int curr_sum = 0; - int max_sum_so_far = INT_MIN; - for(int x : V){ - curr_sum += x; - max_sum_so_far = max(curr_sum, max_sum_so_far); - curr_sum = max(curr_sum, 0); - } - cout << max_sum_so_far; - return 0; -} \ No newline at end of file diff --git a/Random_Problems/move_zeros.cpp b/Random_Problems/move_zeros.cpp deleted file mode 100644 index 7d59fc85..00000000 --- a/Random_Problems/move_zeros.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements. -// Input : [1, 0, 0, 4, 3] -// Output : [1, 4, 3, 0, 0] -#include -using namespace std; - -int main(){ - int n; - cin >> n; - vector V(n); - for(int i = 0; i < n; i++){ - cin >> V[i]; - } - int x = 0; - for(int i = 0; i < n; i++){ - if(V[i] != 0){ - V[x] = V[i]; - x++; - } - } - for(int i = x; i < n; i++){ - V[i] = 0; - } - for(int i = 0; i < n; i++){ - cout << V[i] << " "; - } - return 0; -} \ No newline at end of file diff --git a/Random_Problems/printing_all_subarrays.cpp b/Random_Problems/printing_all_subarrays.cpp deleted file mode 100644 index 5556dc2d..00000000 --- a/Random_Problems/printing_all_subarrays.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Printing all subarrays of an array -#include -using namespace std; -int main(){ - int n; - cin >> n; - vector V(n); - for(int i = 0; i < n; i++){ - cin >> V[i]; - } - for(int i = 0; i < n; i++){ - for(int j = i; j < n; j++){ - for(int k = i; k <= j; k++){ - cout << V[k] << ","; - } - cout << endl; - } - } -} \ No newline at end of file diff --git a/Random_Problems/rotate_string.cpp b/Random_Problems/rotate_string.cpp deleted file mode 100644 index 0a6fc619..00000000 --- a/Random_Problems/rotate_string.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Rotating a string -#include -using namespace std; - -void rotate(char *a, int k){ - int len = strlen(a); - while(len >= 0){ - a[len + k] = a[len]; - len--; - } - len = strlen(a); - int j = len - k; - int s = 0; - while(j < len){ - a[s] = a[j]; - s++; - j++; - } - a[len - k] = '\0'; -} -int main(){ - char a[100] = "abhisekkumar"; - int k = 5; - rotate(a, k); - cout << a << endl; - return 0; -} \ No newline at end of file diff --git a/Random_Problems/set_bits.cpp b/Random_Problems/set_bits.cpp deleted file mode 100644 index 40e2b75c..00000000 --- a/Random_Problems/set_bits.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Count the number of bits that are set in an integer -#include -using namespace std; -short check_set_bits(unsigned int x){ - short num_bits = 0; - while(x){ - num_bits += x & 1; - x >>= 1; - } - return num_bits; -} -int main(){ - cout << check_set_bits(1) << endl; - cout << check_set_bits(7) << endl; - cout << check_set_bits(15) << endl; - return 0; -} \ No newline at end of file diff --git a/Random_Problems/spiral_print.cpp b/Random_Problems/spiral_print.cpp deleted file mode 100644 index aa6a26ab..00000000 --- a/Random_Problems/spiral_print.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -using namespace std; -int main(){ - int mat[100][100]; - int row, col; - cin >> row >> col; - for(int i = 0; i < row; i++){ - for(int j = 0; j < col; j++){ - cin >> mat[i][j]; - } - } - // spiral print - int start_row = 0; - int start_col = 0; - int end_row = row - 1; - int end_col = col - 1; - while(start_row <= end_row && start_col <= end_col){ - // print first row - for(int i = start_col; i <= end_col; i++){ - cout << mat[start_row][i] << " "; - } - start_row++; - // print last col - for(int i = start_row; i <= end_row; i++){ - cout << mat[i][end_col] << " "; - } - end_col--; - // last row - if(end_row > start_row){ - for(int i = end_col; i >= start_col; i--){ - cout << mat[end_row][i] << " "; - } - end_row--; - } - // start column - if(end_col > start_col){ - for(int i = end_row; i >= start_row; i--){ - cout << mat[i][start_col] << " "; - } - start_col++; - } - } - return 0; -} - \ No newline at end of file diff --git a/Random_Problems/tempCodeRunnerFile.cpp b/Random_Problems/tempCodeRunnerFile.cpp deleted file mode 100644 index 7b90afb8..00000000 --- a/Random_Problems/tempCodeRunnerFile.cpp +++ /dev/null @@ -1 +0,0 @@ -ans \ No newline at end of file diff --git a/Random_Problems/unique_occurences.cpp b/Random_Problems/unique_occurences.cpp deleted file mode 100644 index 90372302..00000000 --- a/Random_Problems/unique_occurences.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - Given an array of integers arr, write a function that returns true - if and only if the number of occurrences of each value in the array is unique. - Input: arr = [1,2,2,1,1,3] - Output: true - Explanation: The value 1 has 3 occurrences, 2 has 2 and 3 has 1. No two values have the same number of occurrences. -*/ -#include -using namespace std; -bool check_unique(vector& V, int n){ - map M; - for(int x : V){ - M[x]++; - } - map second_map; - for(auto x : M){ - if(second_map[x.second] > 0) // if same map value encountered then return false - return false; - else - second_map[x.second]++; - } - return true; -} -int main(){ - int n; - cin >> n; - vector V(n); - for(int i = 0; i < n; i++){ - cin >> V[i]; - } - if(check_unique(V, n)) - cout << "True"; - else - cout << "False"; - return 0; -} diff --git a/Recursion/calculatextopowern.cpp b/Recursion/calculatextopowern.cpp index 79f2bb1a..891e6686 100644 --- a/Recursion/calculatextopowern.cpp +++ b/Recursion/calculatextopowern.cpp @@ -1,15 +1,16 @@ -#include -using namespace std; // Canculate power of x^n // Sample Input : 3 2 // Output : 9 -/* Method 1 Time complexity O(n) -int power(int x, int n){ + +#include +using namespace std; + +// Method 1 Time complexity O(n) +int power1(int x, int n){ if(n == 0) return 1; - return x * power(x, n-1); + return x * power1(x, n-1); } -*/ // Method 2 : Time complexity O(log n) int power(int x, int n){ diff --git a/Recursion/count_zeroes.go b/Recursion/count_zeros.go similarity index 100% rename from Recursion/count_zeroes.go rename to Recursion/count_zeros.go diff --git a/Recursion/factorial.js b/Recursion/factorial.js new file mode 100644 index 00000000..8a87ee0d --- /dev/null +++ b/Recursion/factorial.js @@ -0,0 +1,18 @@ +// Factorial of an integer +// Sample Input: 5 +// Output: 120 +function recursiveFactorial(number) { + //Base case + if (number == 1) { + return 1; + } + + //recursively calling function to get factorial result + return number * recursiveFactorial(number - 1); +} + +// Driver code +console.log(recursiveFactorial(2)); +console.log(recursiveFactorial(3)); +console.log(recursiveFactorial(4)); +console.log(recursiveFactorial(5)); diff --git a/Recursion/is_sorted.cpp b/Recursion/is_array_sorted.cpp similarity index 86% rename from Recursion/is_sorted.cpp rename to Recursion/is_array_sorted.cpp index aad9679c..8d10ef0e 100644 --- a/Recursion/is_sorted.cpp +++ b/Recursion/is_array_sorted.cpp @@ -1,20 +1,20 @@ -// Program to check if an array is alerady sorted -// Sample Input: [1, 2, 3, 4, 4, 5, 3] -// Output: 0 meaning false -// Sample Output: [1, 2, 3, 4, 4, 5, 6] -// Output: 1 meaning true -#include -using namespace std; -bool is_sorted(int A[], int m){ - if(m == 0 || m == 1) - return true; - if(A[0] > A[1]) - return false; - bool small_array = is_sorted(A + 1, m - 1); - return small_array; -} -int main(){ - int A[7] = {1, 2, 3, 4, 4, 5, 3}; - cout << is_sorted(A, 7); - return 0; +// Given an array, check whether the array is in sorted order with recursion. +// Sample Input: [1, 2, 3, 4, 4, 5, 3] +// Output: 0 meaning false +// Sample Output: [1, 2, 3, 4, 4, 5, 6] +// Output: 1 meaning true +#include +using namespace std; +bool is_sorted(int A[], int m){ + if(m == 0 || m == 1) + return true; + if(A[0] > A[1]) + return false; + bool small_array = is_sorted(A + 1, m - 1); + return small_array; +} +int main(){ + int A[7] = {1, 2, 3, 4, 4, 5, 3}; + cout << is_sorted(A, 7); + return 0; } \ No newline at end of file diff --git a/Recursion/is_array_sorted.go b/Recursion/is_array_sorted.go index fc3353f8..7a98256e 100644 --- a/Recursion/is_array_sorted.go +++ b/Recursion/is_array_sorted.go @@ -1,3 +1,5 @@ +// Given an array, check whether the array is in sorted order with recursion. + package main import "fmt" @@ -5,12 +7,15 @@ import "fmt" // Time Complexity: O(n). Space Complexity: O(n) for recursive stack space. func isSorted(A []int) bool { n := len(A) + // Base case 1 if n == 1 { return true } + // Base case 2 if A[n - 1] < A[n - 2] { return false } + // recursive case return isSorted(A[:n-1]) } diff --git a/Recursion/powerset.cpp b/Recursion/powerset.cpp new file mode 100644 index 00000000..08de5b64 --- /dev/null +++ b/Recursion/powerset.cpp @@ -0,0 +1,104 @@ +/* + + Write a function that takes in an array of unique integers and returns its powerset. + + Sample Input : [1, 2, 3] + Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] + + Explanation: + + The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset + of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. + + The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: + + 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index + of the last element in the array (`len(array) - 1`). + + 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current + index as input. + + 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been + processed, and it returns a 2D slice containing the empty set as the only subset. + + 4. If the index is not less than 0, the function retrieves the element at the current index from the array. + + 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without + the current element. + + 6. It then calculates the length of the subsets generated so far. + + 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element + to each subset. The new subsets are added to the existing subset slice. + + 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. + + By recursively generating subsets while building upon the subsets generated at each step, the function constructs the + powerset of the given array. + + O(n*2^n) time | O(n*2^n) space - where n is the length of the input array +*/ +#include + +using namespace std; + +vector> powerset(vector& array, int index) { + // Base case: when the index reaches -1, return a vector with an empty subset + if (index < 0) { + return {{}}; + } + + int element = array[index]; + // Recursive call to generate the powerset for the elements up to index - 1 + vector> subset = powerset(array, index - 1); + + // Length of the current subset vector + int length = subset.size(); + + // Iterate through each subset and create new subsets by adding the current element + for (int i = 0; i < length; i++) { + vector currentSubset = subset[i]; + // Create a new subset by making a copy of the current subset + vector newSubset = currentSubset; + // Add the current element to the new subset + newSubset.push_back(element); + // Add the new subset to the existing subset vector + subset.push_back(newSubset); + } + + // Return the resulting subset vector + return subset; +} + +vector> Powerset(vector& array) { + // Call the helper function to generate the powerset starting from the last index + return powerset(array, array.size() - 1); +} + +// Iterative approach +vector> powerset(vector& array) { + vector> subset; + subset.push_back({}); // Initialize the powerset with the empty subset + + // Iterate over each element in the input array + for (int ele : array) { + int length = subset.size(); // Get the current length of the subset + + // Iterate over each existing subset + for (int i = 0; i < length; i++) { + vector currentSubset = subset[i]; // Get the current subset + + // Create a new subset by making a copy of the current subset + vector newSubset = currentSubset; + + // Add the current element to the new subset + newSubset.push_back(ele); + + // Append the new subset to the powerset + subset.push_back(newSubset); + } + } + + // Return the powerset + return subset; +} \ No newline at end of file diff --git a/Recursion/powerset.go b/Recursion/powerset.go new file mode 100644 index 00000000..a9a5a4f8 --- /dev/null +++ b/Recursion/powerset.go @@ -0,0 +1,105 @@ +/* + + Write a function that takes in an array of unique integers and returns its powerset. + + Sample Input : [1, 2, 3] + Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] + + Explanation: + + The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset + of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. + + The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: + + 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index + of the last element in the array (`len(array) - 1`). + + 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current + index as input. + + 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been + processed, and it returns a 2D slice containing the empty set as the only subset. + + 4. If the index is not less than 0, the function retrieves the element at the current index from the array. + + 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without + the current element. + + 6. It then calculates the length of the subsets generated so far. + + 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element + to each subset. The new subsets are added to the existing subset slice. + + 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. + + By recursively generating subsets while building upon the subsets generated at each step, the function constructs the + powerset of the given array. + + O(n*2^n) time | O(n*2^n) space - where n is the length of the input array +*/ + +package main + +func Powerset(array []int) [][]int { + // Call the powerset helper function to compute the powerset of the array + return powerset(array, len(array)-1) +} + +func powerset(array []int, index int) [][]int { + // Base case: If the index is less than 0, all elements have been processed, return the empty set + if index < 0 { + return [][]int{{}} + } + + // Retrieve the element at the current index + element := array[index] + + // Recursively call the powerset function with the array and the index decremented by 1 + subset := powerset(array, index-1) + + // Calculate the length of the current subset + length := len(subset) + + // Iterate over the existing subsets and create new subsets by appending the current element + for i := 0; i < length; i++ { + currentSubset := subset[i] + newSubset := append([]int{}, currentSubset...) // Create a new subset by making a copy of the current subset + newSubset = append(newSubset, element) // Append the current element to the new subset + subset = append(subset, newSubset) // Add the new subset to the existing subset slice + } + + // Return the updated subset slice containing all the subsets of the original array + return subset +} + +// Iterative approach + +func PowersetIterative(array []int) [][]int { + // Initialize the powerset with the empty subset + subset := [][]int{{}} + + // Iterate over each element in the input array + for _, ele := range array { + // Get the current length of the subset + length := len(subset) + + // Iterate over each existing subset + for i := 0; i < length; i++ { + // Get the current subset + currentSubset := subset[i] + + // Create a new subset by making a copy of the current subset + newSubset := append([]int{}, currentSubset...) + + // Add the current element to the new subset + newSubset = append(newSubset, ele) + + // Append the new subset to the powerset + subset = append(subset, newSubset) + } + } + + // Return the powerset + return subset +} diff --git a/Recursion/powerset.java b/Recursion/powerset.java new file mode 100644 index 00000000..1c93b214 --- /dev/null +++ b/Recursion/powerset.java @@ -0,0 +1,127 @@ +/* + + Write a function that takes in an array of unique integers and returns its powerset. + + Sample Input : [1, 2, 3] + Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] + + Explanation: + + The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset + of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. + + The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: + + 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index + of the last element in the array (`len(array) - 1`). + + 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current + index as input. + + 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been + processed, and it returns a 2D slice containing the empty set as the only subset. + + 4. If the index is not less than 0, the function retrieves the element at the current index from the array. + + 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without + the current element. + + 6. It then calculates the length of the subsets generated so far. + + 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element + to each subset. The new subsets are added to the existing subset slice. + + 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. + + By recursively generating subsets while building upon the subsets generated at each step, the function constructs the + powerset of the given array. + + O(n*2^n) time | O(n*2^n) space - where n is the length of the input array +*/ +import java.util.ArrayList; +import java.util.List; + +class Main { + + public static List> powerset(int[] array, int index) { + // Base case: when the index reaches -1, return a list with an empty subset + if (index < 0) { + List> subsets = new ArrayList<>(); + subsets.add(new ArrayList<>()); + return subsets; + } + + int element = array[index]; + // Recursive call to generate the powerset for the elements up to index - 1 + List> subsets = powerset(array, index - 1); + + // Length of the current subsets list + int length = subsets.size(); + + // Iterate through each subset and create new subsets by adding the current element + for (int i = 0; i < length; i++) { + List currentSubset = subsets.get(i); + // Create a new subset by making a copy of the current subset + List newSubset = new ArrayList<>(currentSubset); + // Add the current element to the new subset + newSubset.add(element); + // Add the new subset to the existing subsets list + subsets.add(newSubset); + } + + // Return the resulting subsets list + return subsets; + } + + public static List> powerset(int[] array) { + // Call the helper function to generate the powerset starting from the last index + return powerset(array, array.length - 1); + } + + public static void main(String[] args) { + int[] array = {1, 2, 3}; + List> subsets = powerset(array); + + // Print the subsets + for (List subset : subsets) { + System.out.println(subset); + } + } +} + + +// Iterative approach +public class Powerset { + public static List> powerset(int[] array) { + List> subset = new ArrayList<>(); + subset.add(new ArrayList<>()); // Initialize the powerset with the empty subset + + // Iterate over each element in the input array + for (int ele : array) { + int length = subset.size(); // Get the current length of the subset + + // Iterate over each existing subset + for (int i = 0; i < length; i++) { + List currentSubset = new ArrayList<>(subset.get(i)); // Get the current subset + + // Create a new subset by making a copy of the current subset + List newSubset = new ArrayList<>(currentSubset); + + // Add the current element to the new subset + newSubset.add(ele); + + // Append the new subset to the powerset + subset.add(newSubset); + } + } + + // Return the powerset + return subset; + } + + public static void main(String[] args) { + int[] array = {1, 2, 3}; + List> result = powerset(array); + System.out.println(result); + } +} \ No newline at end of file diff --git a/Recursion/powerset.js b/Recursion/powerset.js new file mode 100644 index 00000000..7bfff2a8 --- /dev/null +++ b/Recursion/powerset.js @@ -0,0 +1,109 @@ +/* + + Write a function that takes in an array of unique integers and returns its powerset. + + Sample Input : [1, 2, 3] + Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] + + Explanation: + + The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset + of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. + + The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: + + 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index + of the last element in the array (`len(array) - 1`). + + 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current + index as input. + + 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been + processed, and it returns a 2D slice containing the empty set as the only subset. + + 4. If the index is not less than 0, the function retrieves the element at the current index from the array. + + 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without + the current element. + + 6. It then calculates the length of the subsets generated so far. + + 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element + to each subset. The new subsets are added to the existing subset slice. + + 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. + + By recursively generating subsets while building upon the subsets generated at each step, the function constructs the + powerset of the given array. + + O(n*2^n) time | O(n*2^n) space - where n is the length of the input array +*/ +function powerset(array, index) { + // Base case: when the index reaches -1, return a list with an empty subset + if (index < 0) { + return [[]]; + } + + const element = array[index]; + // Recursive call to generate the powerset for the elements up to index - 1 + const subsets = powerset(array, index - 1); + + // Create new subsets by adding the current element to existing subsets + const length = subsets.length; + for (let i = 0; i < length; i++) { + const currentSubset = subsets[i]; + // Create a new subset by making a copy of the current subset + const newSubset = [...currentSubset]; + // Add the current element to the new subset + newSubset.push(element); + // Add the new subset to the existing subsets array + subsets.push(newSubset); + } + + // Return the resulting subsets array + return subsets; +} + +function getPowerSet(array) { + // Call the helper function to generate the powerset starting from the last index + return powerset(array, array.length - 1); +} + +// Example usage +const array = [1, 2, 3]; +const subsets = getPowerSet(array); + +// Print the subsets +subsets.forEach((subset) => { + console.log(subset); +}); + +// Iterative approach +function powersetIterative(array) { + // Initialize the powerset with the empty subset + let subset = [[]]; + + // Iterate over each element in the input array + for (let ele of array) { + // Get the current length of the subset + let length = subset.length; + + // Iterate over each existing subset + for (let i = 0; i < length; i++) { + // Get the current subset + let currentSubset = subset[i]; + + // Create a new subset by making a copy of the current subset + let newSubset = [...currentSubset]; + + // Add the current element to the new subset + newSubset.push(ele); + + // Append the new subset to the powerset + subset.push(newSubset); + } + } + + // Return the powerset + return subset; +} diff --git a/Recursion/powerset.py b/Recursion/powerset.py new file mode 100644 index 00000000..7a3a8939 --- /dev/null +++ b/Recursion/powerset.py @@ -0,0 +1,96 @@ +''' + + Write a function that takes in an array of unique integers and returns its powerset. + + Sample Input : [1, 2, 3] + Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] + + Explanation: + + The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset + of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. + + The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: + + 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index + of the last element in the array (`len(array) - 1`). + + 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current + index as input. + + 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been + processed, and it returns a 2D slice containing the empty set as the only subset. + + 4. If the index is not less than 0, the function retrieves the element at the current index from the array. + + 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without + the current element. + + 6. It then calculates the length of the subsets generated so far. + + 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element + to each subset. The new subsets are added to the existing subset slice. + + 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. + + By recursively generating subsets while building upon the subsets generated at each step, the function constructs the + powerset of the given array. + + O(n*2^n) time | O(n*2^n) space - where n is the length of the input array +''' +def powerset(array): + # Base case: when the index reaches -1, return a list with an empty subset + if index < 0: + return [[]] + + element = array[index] + # Recursive call to generate the powerset for the elements up to index - 1 + subset = powerset(array, index - 1) + + # Length of the current subset list + length = len(subset) + + # Iterate through each subset and create new subsets by adding the current element + for i in range(length): + currentSubset = subset[i] + # Create a new subset by making a copy of the current subset + newSubset = currentSubset[:] + # Add the current element to the new subset + newSubset.append(element) + # Add the new subset to the existing subset list + subset.append(newSubset) + + # Return the resulting subset list + return subset + +def Powerset(array): + # Call the helper function to generate the powerset starting from the last index + return powerset(array, len(array) - 1) + + +# Iterative approach +def powersetIterative(array): + # Initialize the powerset with the empty subset + subset = [[]] + + # Iterate over each element in the input array + for ele in array: + # Get the current length of the subset + length = len(subset) + + # Iterate over each existing subset + for i in range(length): + # Get the current subset + currentSubset = subset[i] + + # Create a new subset by making a copy of the current subset + newSubset = list(currentSubset) + + # Add the current element to the new subset + newSubset.append(ele) + + # Append the new subset to the powerset + subset.append(newSubset) + + # Return the powerset + return subset diff --git a/Random_Problems/bubble_sort_recursive.cpp b/Recursion/recursive_bubble_sort.cpp similarity index 97% rename from Random_Problems/bubble_sort_recursive.cpp rename to Recursion/recursive_bubble_sort.cpp index bea86c51..6dd79965 100644 --- a/Random_Problems/bubble_sort_recursive.cpp +++ b/Recursion/recursive_bubble_sort.cpp @@ -1,34 +1,34 @@ -// Implementation of Recursive Bubble sort. -// Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm -// that repeatedly steps through the input list element by element, -// comparing the current element with the one after it, swapping their values if needed. -// These passes through the list are repeated until no swaps had to be performed during a pass, -// meaning that the list has become fully sorted. (Source wiki) https://en.wikipedia.org/wiki/Bubble_sort - -// Time Complexity worst-case and average complexity O(n^{2}) -// Bubble sort is O(n) on a list that is already sorted i.e. Best case - -// Sample Input : [2, 1, 9, 3, 5, 4, 0] -// Output : [0 1 2 3 4 5 9] -// Program Author : Abhisek Kumar Gupta -#include -using namespace std; -void bubble_sort(int *A, int len){ - if(len == 1) - return; - for(int i = 0; i < len - 1; i++){ - if(A[i] > A[i+1]){ - swap(A[i], A[i+1]); - } - } - bubble_sort(A, len-1); -} -int main(){ - int A[] = {5, 4, 3, 2, 1, 7}; - int len = sizeof(A) / sizeof(int); - bubble_sort(A, len); - for(int x : A){ - cout << x << " "; - } - return 0; +// Implementation of Recursive Bubble sort. +// Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm +// that repeatedly steps through the input list element by element, +// comparing the current element with the one after it, swapping their values if needed. +// These passes through the list are repeated until no swaps had to be performed during a pass, +// meaning that the list has become fully sorted. (Source wiki) https://en.wikipedia.org/wiki/Bubble_sort + +// Time Complexity worst-case and average complexity O(n^{2}) +// Bubble sort is O(n) on a list that is already sorted i.e. Best case + +// Sample Input : [2, 1, 9, 3, 5, 4, 0] +// Output : [0 1 2 3 4 5 9] +// Program Author : Abhisek Kumar Gupta +#include +using namespace std; +void bubble_sort(int *A, int len){ + if(len == 1) + return; + for(int i = 0; i < len - 1; i++){ + if(A[i] > A[i+1]){ + swap(A[i], A[i+1]); + } + } + bubble_sort(A, len-1); +} +int main(){ + int A[] = {5, 4, 3, 2, 1, 7}; + int len = sizeof(A) / sizeof(int); + bubble_sort(A, len); + for(int x : A){ + cout << x << " "; + } + return 0; } \ No newline at end of file diff --git a/Recursion/tower_of_hannoi.cpp b/Recursion/tower_of_hannoi.cpp new file mode 100644 index 00000000..c172f674 --- /dev/null +++ b/Recursion/tower_of_hannoi.cpp @@ -0,0 +1,46 @@ +/* + Towers of Hanoi puzzle. + Source(https://en.wikipedia.org/wiki/Tower_of_Hanoi) + Object of the game is to move all the disks over to Tower 3. + But you cannot place a larger disk onto a smaller disk. + + Approach + 1 Move the top 􀝊 − 1 disks from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower, + 2 Move the 􀝊􀯧􀯛 disk from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower, + 3 Move the 􀝊 − 1disks from 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower. +*/ +#include + +void towerOfHanoi(int numDisks, char fromPeg, char toPeg, char auxPeg) { + if (numDisks == 1) { + std::cout << "Move disk 1 from peg " << fromPeg << " to peg " << toPeg << std::endl; + } else { + towerOfHanoi(numDisks - 1, fromPeg, auxPeg, toPeg); + std::cout << "Move disk " << numDisks << " from peg " << fromPeg << " to peg " << toPeg << std::endl; + towerOfHanoi(numDisks - 1, auxPeg, toPeg, fromPeg); + } +} + +int main() { + towerOfHanoi(3, 'A', 'C', 'B'); + return 0; +} + +// This implementation uses recursion to solve the problem. The towerOfHanoi function takes four arguments: numDisks (the number of disks to move), fromPeg (the peg the disks start on), toPeg (the peg the disks should end up on), and auxPeg (the auxiliary peg used for moving the disks). +// If numDisks is 1, the function simply moves the single disk from fromPeg to toPeg. Otherwise, it first moves numDisks - 1 disks from fromPeg to auxPeg using toPeg as the auxiliary peg. Then it moves the largest remaining disk from fromPeg to toPeg. Finally, it moves the numDisks - 1 disks from auxPeg to toPeg using fromPeg as the auxiliary peg. +// The main function simply calls towerOfHanoi with the appropriate arguments (in this case, numDisks is 3 and the pegs are labeled A, B, and C). + +// When run, the program outputs the following to the console: + +// Move disk 1 from peg A to peg C +// Move disk 2 from peg A to peg B +// Move disk 1 from peg C to peg B +// Move disk 3 from peg A to peg C +// Move disk 1 from peg B to peg A +// Move disk 2 from peg B to peg C +// Move disk 1 from peg A to peg C + + +// This output shows the sequence of moves that solve the Tower of Hanoi problem for 3 disks. + + diff --git a/Recursion/tower_of_hannoi.java b/Recursion/tower_of_hannoi.java new file mode 100644 index 00000000..dd48350e --- /dev/null +++ b/Recursion/tower_of_hannoi.java @@ -0,0 +1,53 @@ +/* + The Tower of Hanoi problem is a classic problem in computer science and mathematics that involves moving a stack of disks from one peg to another peg. The problem consists of three pegs and a set of disks of different sizes that can slide onto any peg. The objective of the puzzle is to move the entire stack to another peg, obeying the following simple rules: + + Only one disk can be moved at a time. + Each move consists of taking the upper disk from one of the stacks and placing it on top of another stack or an empty peg. + No disk may be placed on top of a smaller disk. + The Tower of Hanoi problem is often used as an example of recursive problem-solving. The solution to the problem can be divided into three parts: + + Move n-1 disks from the starting peg to the auxiliary peg. + Move the largest disk from the starting peg to the destination peg. + Move the n-1 disks from the auxiliary peg to the destination peg. + This process is repeated recursively until all the disks have been moved from the starting peg to the destination peg. The number of moves required to solve the puzzle for n disks can be calculated using the formula 2^n - 1. + + Here's an implementation of the Tower of Hanoi problem in Java using recursion: + +*/ +public class TowerOfHanoi { + public static void main(String[] args) { + int n = 3; // number of disks + char fromRod = 'A'; + char toRod = 'C'; + char auxRod = 'B'; + + System.out.println("Moves to solve Tower of Hanoi problem with " + n + " disks:"); + solveHanoi(n, fromRod, toRod, auxRod); + } + + public static void solveHanoi(int n, char fromRod, char toRod, char auxRod) { + if (n == 1) { + System.out.println("Move disk 1 from rod " + fromRod + " to rod " + toRod); + return; + } + solveHanoi(n - 1, fromRod, auxRod, toRod); + System.out.println("Move disk " + n + " from rod " + fromRod + " to rod " + toRod); + solveHanoi(n - 1, auxRod, toRod, fromRod); + } +} + + +// -----------------------Explanation------------------------------ + +// In this implementation, the solveHanoi method is called recursively to move the disks from one rod to another. The method takes in four parameters: + +// n: the number of disks +// fromRod: the rod from which the disks are to be moved +// toRod: the rod to which the disks are to be moved +// auxRod: the auxiliary rod which can be used to move the disks +// When the number of disks is 1, the method simply prints the move to be made. Otherwise, the method makes the following three recursive calls: + +// Move n-1 disks from fromRod to auxRod using toRod as the auxiliary rod. +// Move the nth disk from fromRod to toRod. +// Move the n-1 disks from auxRod to toRod using fromRod as the auxiliary rod. +// This process is repeated recursively until all the disks have been moved from the starting rod to the destination rod. \ No newline at end of file diff --git a/Recursion/tower_of_hannoi.js b/Recursion/tower_of_hannoi.js new file mode 100644 index 00000000..9ef6f9b5 --- /dev/null +++ b/Recursion/tower_of_hannoi.js @@ -0,0 +1,35 @@ +/* + Towers of Hanoi puzzle. + Source(https://en.wikipedia.org/wiki/Tower_of_Hanoi) + Object of the game is to move all the disks over to Tower 3. + But you cannot place a larger disk onto a smaller disk. + + Approach + 1 Move the top 􀝊 − 1 disks from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower, + 2 Move the 􀝊􀯧􀯛 disk from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower, + 3 Move the 􀝊 − 1disks from 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower. +*/ +function towerOfHanoi(numDisks, fromPeg, toPeg, auxPeg) { + if (numDisks === 1) { + console.log(`Move disk 1 from peg ${fromPeg} to peg ${toPeg}`); + } else { + towerOfHanoi(numDisks - 1, fromPeg, auxPeg, toPeg); + console.log(`Move disk ${numDisks} from peg ${fromPeg} to peg ${toPeg}`); + towerOfHanoi(numDisks - 1, auxPeg, toPeg, fromPeg); + } +} + +// Example usage: +towerOfHanoi(3, "A", "C", "B"); // Move disk 1 from peg A to peg C, Move disk 2 from peg A to peg B, Move disk 1 from peg C to peg B, Move disk 3 from peg A to peg C, Move disk 1 from peg B to peg A, Move disk 2 from peg B to peg C, Move disk 1 from peg A to peg C + +// This implementation uses recursion to solve the problem. The towerOfHanoi function takes four arguments: numDisks (the number of disks to move), fromPeg (the peg the disks start on), toPeg (the peg the disks should end up on), and auxPeg (the auxiliary peg used for moving the disks). +// If numDisks is 1, the function simply moves the single disk from fromPeg to toPeg. Otherwise, it first moves numDisks - 1 disks from fromPeg to auxPeg using toPeg as the auxiliary peg. Then it moves the largest remaining disk from fromPeg to toPeg. Finally, it moves the numDisks - 1 disks from auxPeg to toPeg using fromPeg as the auxiliary peg. +// The function prints out the moves it makes to the console, so running towerOfHanoi(3, "A", "C", "B") would output: + +// Move disk 1 from peg A to peg C +// Move disk 2 from peg A to peg B +// Move disk 1 from peg C to peg B +// Move disk 3 from peg A to peg C +// Move disk 1 from peg B to peg A +// Move disk 2 from peg B to peg C +// Move disk 1 from peg A to peg C diff --git a/Recursion/tower_of_hannoi.py b/Recursion/tower_of_hannoi.py new file mode 100644 index 00000000..8cef5cd0 --- /dev/null +++ b/Recursion/tower_of_hannoi.py @@ -0,0 +1,19 @@ + +''' + Towers of Hanoi puzzle. + Source(https://en.wikipedia.org/wiki/Tower_of_Hanoi) + Object of the game is to move all the disks over to Tower 3. + But you cannot place a larger disk onto a smaller disk. + + Approach + 1 Move the top 􀝊 − 1 disks from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower, + 2 Move the 􀝊􀯧􀯛 disk from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower, + 3 Move the 􀝊 − 1disks from 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower. +''' +#Tower of Hanoi explanation - https://youtu.be/YstLjLCGmgg +def TowerOfHanoi(n, A,B,C): + if n == 0: + return + TowerOfHanoi(n-1, A,C,B) + print("Move disk", n, "from", A, "to rod", B) + TowerOfHanoi(n-1, C, B, A) \ No newline at end of file diff --git a/Recursion/tower_of_hanoi.go b/Recursion/tower_of_hanoi.go new file mode 100644 index 00000000..ac49e9f7 --- /dev/null +++ b/Recursion/tower_of_hanoi.go @@ -0,0 +1,39 @@ +/* + Towers of Hanoi puzzle. + Source(https://en.wikipedia.org/wiki/Tower_of_Hanoi) + Object of the game is to move all the disks over to Tower 3. + But you cannot place a larger disk onto a smaller disk. +*/ +/* +Approach + 1 Move the top 􀝊 − 1 disks from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower, + 2 Move the 􀝊􀯧􀯛 disk from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower, + 3 Move the 􀝊 − 1disks from 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower. +*/ +package main + +import "fmt" +func TowerOfHanoiHelper(n int, from, to, temp string) { + // Base case + // If only 1 disk, make the move and return + if n == 1 { + fmt.Println("Move disk ", n, " from peg ", from, " to peg ", to) + return + } + + // Move top n-1 disks from A to B, using C as auxiliary + TowerOfHanoiHelper(n-1, from, temp, to) + + // Move remaining disks from A to C + fmt.Println("Move disk ", n, " from peg ", from, " to peg ", to) + + // Move n-1 disks from B to C using A as auxiliary + TowerOfHanoiHelper(n-1, temp, to, from) +} + +func TowersOfHanoi(n int) { + TowerOfHanoiHelper(n, "A", "C", "B") +} +func main() { + TowersOfHanoi(3) +} \ No newline at end of file diff --git a/Recursion/valid_palindrome_2.py b/Recursion/valid_palindrome_2.py new file mode 100644 index 00000000..1605db99 --- /dev/null +++ b/Recursion/valid_palindrome_2.py @@ -0,0 +1,30 @@ +''' + Write a function that takes a string as input and checks whether it can be a valid palindrome by removing at most one character from it. + + Constraints: string.length The string only consists of English letters + + Sample Input : "madame" + Output : True + + Sample Input : "masdasd" + Output : False + +''' +class Solution: + def subPalindrome(self,s,low,high,count): + if(low>high): + return True + if(count>1): + return False + if(s[low]!=s[high]): + return Solution.subPalindrome(self,s,low+1,high,count+1) or Solution.subPalindrome(self,s,low,high-1,count+1) + else: + return Solution.subPalindrome(self,s,low+1,high-1,count) + + def validPalindrome(self, s: str) -> bool: + if(s==s[::-1]): + return True + return Solution.subPalindrome(self,s,0,len(s)-1,0) + +print(Solution().validPalindrome("ebcbbececabbacecbbcbe")) +print(Solution().validPalindrome("cdbeeeabddddbaeedebdc")) diff --git a/Scheduling Algortihms/fcfs.c b/Scheduling Algortihms/fcfs.c new file mode 100644 index 00000000..1af2fc20 --- /dev/null +++ b/Scheduling Algortihms/fcfs.c @@ -0,0 +1,132 @@ +/* + +The First-Come, First-Served (FCFS) scheduling algorithm is one of the simplest process scheduling algorithms used in operating systems. It follows the principle of serving processes in the order they arrive in the ready queue. In FCFS, the process that arrives first will be the first one to be executed, and it continues until the process completes its execution or enters a waiting state. + +Algorithm Description: + +When a process enters the ready queue, it is added to the end of the queue (at the "tail" of the queue). + +The CPU scheduler selects the process at the front of the queue (the "head" of the queue) for execution. + +The selected process runs on the CPU until it completes its execution, enters a waiting state (e.g., I/O operation), or its time quantum (if there is a time-sharing system) expires. + +Once the currently running process finishes, the CPU scheduler selects the process at the front of the queue as the next process to run. This process will continue until it completes or enters a waiting state, and so on. + +This process of selecting and executing processes continues until all processes in the queue have executed. + +Example: +Let's illustrate the FCFS algorithm with a simple example. Consider three processes, P1, P2, and P3, with their respective burst times (the time they need to complete execution): + +Process P1: Burst time = 2 ms +Process P2: Burst time = 7 ms +Process P3: Burst time = 4 ms +Here's how FCFS will schedule and execute these processes: + +Initially, the ready queue is empty. + +Process P1 arrives first, so it is added to the ready queue: + +Ready Queue: [P1] +Execution: P1 (2 ms) +Process P1 completes its execution. Now, process P2 is selected because it's at the front of the queue: + +Ready Queue: [P2] +Execution: P2 (7 ms) +Process P2 completes its execution. Finally, process P3 is selected: + +Ready Queue: [P3] +Execution: P3 (4 ms) +Process P3 completes its execution. + +The order of execution in FCFS is P1 -> P2 -> P3. FCFS is non-preemptive, meaning once a process starts execution, it continues until it finishes or enters a waiting state. It can suffer from the "convoy effect," where a long process at the head of the queue can block shorter processes behind it. + +*/ + +#include +#include + +// Struct for the info of each process +typedef struct +{ + + int process_id; + int arrival_time; + int burst_time; + +} process; + + +int main() { + + // Number of processes + int number=3; + + // Size of the array which will show the order of execution of the processes + process *arr; + arr = malloc(number * sizeof(process)); + + // Initialize for our example + arr[0].process_id = 1; + arr[0].arrival_time = 0; + arr[0].burst_time = 2; + + arr[1].process_id = 2; + arr[1].arrival_time = 1; + arr[1].burst_time = 7; + + arr[2].process_id = 3; + arr[2].arrival_time = 3; + arr[2].burst_time = 4; + + //Sorting the struct by arrival time + process temp; + for(int i=0; i P2 -> P3. SJF aims to minimize the average waiting time for processes, making it an effective algorithm when you have information about the burst times of processes. However, it can suffer from starvation if a long job continually arrives after shorter jobs. +*/ + +#include +#include + +// Struct for the info of each process +typedef struct +{ + + int process_id; + int arrival_time; + int burst_time; + +} process; + + +int main() { + + // Number of processes + int number=3; + + // Size of the array which will show the order of execution of the processes + process *arr; + arr = malloc(number * sizeof(process)); + + // Initialize for our example + arr[0].process_id = 1; + arr[0].arrival_time = 0; + arr[0].burst_time = 7; + + arr[1].process_id = 2; + arr[1].arrival_time = 2; + arr[1].burst_time = 3; + + arr[2].process_id = 3; + arr[2].arrival_time = 4; + arr[2].burst_time = 8; + + //Sorting the struct by arrival time + process temp; + for(int i=0; i= arr[j].arrival_time && arr[j].burst_time <= min) + { + temp = arr[k]; + arr[k] = arr[j]; + arr[j] = temp; + } + } + k++; //increase index + } + + //Variable for the sum of burst time + int sum_of_burst_time=0; + + //Calculate sum of burst time + for (int i=0; i P2 -> P1 -> P3. SRTF provides optimal turnaround times but may suffer from frequent context switches due to its preemptive nature. +*/ + +#include +#include + +// Struct for the info of each process +typedef struct +{ + + int process_id; + int arrival_time; + int burst_time; + +} process; + + +int main() { + + // Number of processes + int number=3; + + // Size of the array which will show the order of execution of the processes + process *arr; + arr = malloc(number * sizeof(process)); + + // Initialize for our example + arr[0].process_id = 1; + arr[0].arrival_time = 0; + arr[0].burst_time = 7; + + arr[1].process_id = 2; + arr[1].arrival_time = 2; + arr[1].burst_time = 3; + + arr[2].process_id = 3; + arr[2].arrival_time = 4; + arr[2].burst_time = 8; + + //Sorting the struct by arrival time + process temp; + for (int i=0; i P2 -> P3. FCFS is non-preemptive, meaning once a process starts execution, it continues until it finishes or enters a waiting state. It can suffer from the "convoy effect," where a long process at the head of the queue can block shorter processes behind it. + +*/ + +#include +#include + +// Struct for the info of each process +typedef struct +{ + + int process_id; + int arrival_time; + int burst_time; + +} process; + + +int main() { + + // Number of processes + int number=3; + + // Size of the array which will show the order of execution of the processes + process *arr; + arr = malloc(number * sizeof(process)); + + // Initialize for our example + arr[0].process_id = 1; + arr[0].arrival_time = 0; + arr[0].burst_time = 2; + + arr[1].process_id = 2; + arr[1].arrival_time = 1; + arr[1].burst_time = 7; + + arr[2].process_id = 3; + arr[2].arrival_time = 3; + arr[2].burst_time = 4; + + //Sorting the struct by arrival time + process temp; + for(int i=0; i P2 -> P3. SJF aims to minimize the average waiting time for processes, making it an effective algorithm when you have information about the burst times of processes. However, it can suffer from starvation if a long job continually arrives after shorter jobs. +*/ + +#include +#include + +// Struct for the info of each process +typedef struct +{ + + int process_id; + int arrival_time; + int burst_time; + +} process; + + +int main() { + + // Number of processes + int number=3; + + // Size of the array which will show the order of execution of the processes + process *arr; + arr = malloc(number * sizeof(process)); + + // Initialize for our example + arr[0].process_id = 1; + arr[0].arrival_time = 0; + arr[0].burst_time = 7; + + arr[1].process_id = 2; + arr[1].arrival_time = 2; + arr[1].burst_time = 3; + + arr[2].process_id = 3; + arr[2].arrival_time = 4; + arr[2].burst_time = 8; + + //Sorting the struct by arrival time + process temp; + for(int i=0; i= arr[j].arrival_time && arr[j].burst_time <= min) + { + temp = arr[k]; + arr[k] = arr[j]; + arr[j] = temp; + } + } + k++; //increase index + } + + //Variable for the sum of burst time + int sum_of_burst_time=0; + + //Calculate sum of burst time + for (int i=0; i P2 -> P1 -> P3. SRTF provides optimal turnaround times but may suffer from frequent context switches due to its preemptive nature. +*/ + +#include +#include + +// Struct for the info of each process +typedef struct +{ + + int process_id; + int arrival_time; + int burst_time; + +} process; + + +int main() { + + // Number of processes + int number=3; + + // Size of the array which will show the order of execution of the processes + process *arr; + arr = malloc(number * sizeof(process)); + + // Initialize for our example + arr[0].process_id = 1; + arr[0].arrival_time = 0; + arr[0].burst_time = 7; + + arr[1].process_id = 2; + arr[1].arrival_time = 2; + arr[1].burst_time = 3; + + arr[2].process_id = 3; + arr[2].arrival_time = 4; + arr[2].burst_time = 8; + + //Sorting the struct by arrival time + process temp; + for (int i=0; i { + + // firstly iterate/loop through the given array + for(let i = 0; i < arr.length; i++){ + // then use the Array.prototype.lastIndexOf() method to check for duplicates. + // finally return the first number that appers more than once. + if(arr.lastIndexOf(arr[i]) !== i) return arr[i]; + }; + + // return the message No duplicate found, if no dupliacte is found after the iteration process. + return "No duplicate found!"; +} + +console.log(firstDuplicate1([2, 1, 5, 2, 3, 3, 4])); + + + + +// Big-O = O(n) time complexity +// Big-O = O(n) space complexity +const firstDuplicate2 = arr => { + + // first off, let's create our Set object + // this Set object will allow us to store each element from the given array as a unique value + let elementSet = new Set(); + + // then iterate/loop through the given array + for (let i = 0; i < arr.length; i++) { + // we'll check to see if the Set already contains the element that we're currently on in our loop + // if it exists, then we've found our first duplicate! We'll return that value and be done + if (elementSet.has(arr[i])) return arr[i]; + // if the element isn't in our Set yet, then we add it to the Set and move on to the next element in the array. + elementSet.add(arr[i]); + } + + // return the message No duplicate found, if no dupliacte is found after the iteration process. + return "No duplicates found!"; +} + +console.log(firstDuplicate2([2, 1, 5, 2, 3, 3, 4])); + diff --git a/Random_Problems/linear_search_string.cpp b/Searching/linear_search_string.cpp similarity index 95% rename from Random_Problems/linear_search_string.cpp rename to Searching/linear_search_string.cpp index 282f5f17..83b43947 100644 --- a/Random_Problems/linear_search_string.cpp +++ b/Searching/linear_search_string.cpp @@ -1,25 +1,25 @@ -// Linear search in an array of strings -#include -using namespace std; -int main(){ - char a[10][100]; - int n; - cin >> n; - cin.ignore(); - for(int i = 0 ; i < n; i++){ - cin.getline(a[i], 100); - } - char key[100]; - cout << "Enter string to search : "; - cin.getline(key, 100); - int i = 0; - for(i = 0; i < n; i++){ - if(strcmp(key, a[i]) == 0){ - cout << "Found at index : " << i; - break; - } - } - if(i == n) - cout << "Not Found" < +using namespace std; +int main(){ + char a[10][100]; + int n; + cin >> n; + cin.ignore(); + for(int i = 0 ; i < n; i++){ + cin.getline(a[i], 100); + } + char key[100]; + cout << "Enter string to search : "; + cin.getline(key, 100); + int i = 0; + for(i = 0; i < n; i++){ + if(strcmp(key, a[i]) == 0){ + cout << "Found at index : " << i; + break; + } + } + if(i == n) + cout << "Not Found" <= 0; i-- { + x = append(x, s[i]) + } + return string(x) +} diff --git a/Searching/separate_0s_and_1s.go b/Searching/separate_0s_and_1s.go new file mode 100644 index 00000000..d49f3def --- /dev/null +++ b/Searching/separate_0s_and_1s.go @@ -0,0 +1,32 @@ +// Program to separate Zeros and Ones +// Sample input: [0, 1, 0, 1, 0, 0, 1] +// Output : [0, 0, 0, 0, 1, 1, 1] + +package main + +import "fmt" + +func SeparateZerosAndOnes(Arr []int) []int { + start, end := 0, len(Arr) - 1 + + for start < end { + for Arr[start] == 0 && start < end { + start++ // element in correct place so just increment the counter + } + for Arr[end] == 1 && start < end { + end-- // element in correct place so just decrement the counter + } + // two pointers start and end which are not in correct place so need to be swapped + if start < end { + Arr[start], Arr[end] = Arr[end], Arr[start] + start++ + end-- + } + } + return Arr +} + +func main() { + Arr := []int{0, 1, 1, 0, 1, 0, 0, 1} + fmt.Println(SeparateZerosAndOnes(Arr)) +} \ No newline at end of file diff --git a/Searching/separate_even_odd.go b/Searching/separate_even_odd.go new file mode 100644 index 00000000..795e2b20 --- /dev/null +++ b/Searching/separate_even_odd.go @@ -0,0 +1,32 @@ +// Program to separate Even and Odd numbers +// Sample input: [1, 2, 3, 4, 5, 6] +// Output : [6 2 4 3 5 1] + +package main + +import "fmt" + +func SeparateEvenAndOdd(Arr []int) []int { + start, end := 0, len(Arr) - 1 + + for start < end { + for Arr[start] % 2 == 0 && start < end { + start++ // element in correct place so just increment the counter + } + for Arr[end] % 2 == 1 && start < end { + end-- // element in correct place so just decrement the counter + } + // two pointers start and end which are not in correct place so need to be swapped + if start < end { + Arr[start], Arr[end] = Arr[end], Arr[start] + start++ + end-- + } + } + return Arr +} + +func main() { + Arr := []int{1, 2, 3, 4, 5, 6} + fmt.Println(SeparateEvenAndOdd(Arr)) +} \ No newline at end of file diff --git a/Sliding Window/find_max.go b/Sliding Window/find_max.go new file mode 100644 index 00000000..7c43525c --- /dev/null +++ b/Sliding Window/find_max.go @@ -0,0 +1,60 @@ +// Statement: + +//// Given an array and an integer K, find the maximum for each and every contiguous subarray of size K. + +// Example: + +//// Input: N = 5 arr[] = {5, 4, 1, 7, 3}, K = 2 +//// Output: 5 4 7 7 +//// Explanation: Maximum of 5, 4 is 5 +//// Maximum of 4, 1 is 4 +//// Maximum of 1, 7 is 7 +//// Maximum of 7, 3 is 7 + +// Approach: + +//// We will travel from 0 to (N-K)th element of the given array. +//// At each index, we will find the maximum of K indexes including the current index and print. +//// Suppose we are at ith index then we will find maximum of i, i+1, i+2, .. i+k-1 elements and print it. + +// Complexity: + +//// Time Complexity: O(N*K) +//// Space Complexity: O(1) + +// Code: + +package main +import "fmt" + +func printKMax(arr []int, N int, K int) { + + var j, max int + + // Finding the maximum elemnt in each window + for i := 0; i <= N-K; i++ { + max = arr[i] + + for j = 1; j < K; j++ { + if arr[i+j] > max { + max = arr[i+j] + } + } + fmt.Printf("%d ", max) + } +} + +func main() { + + var N, K int // Variable declaration + fmt.Scan(&N) // Initialise length of array + + arr := make([]int, N) // Array Declaration + for i := 0; i < N; i++ { // Array Initialisation + fmt.Scan(&arr[i]) + } + + fmt.Scan(&K) // Window length initialisation + + printKMax(arr, N, K) // Function call to print maximum element in the window +} diff --git a/Sliding Window/fruits_into_basket.cpp b/Sliding Window/fruits_into_basket.cpp new file mode 100644 index 00000000..cf5571a1 --- /dev/null +++ b/Sliding Window/fruits_into_basket.cpp @@ -0,0 +1,79 @@ +/* + Fruits into basket + Summary: + The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits + from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. + The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under + this constraint. + + - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. + + - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at + most two distinct elements and the maximum number of fruits collected, respectively. + + - The `startWindow` variable represents the left boundary of the sliding window. + + - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. + + - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse + the array from left to right. + + - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it + ensures that there are at most two different types of fruits in the window by adjusting the window as needed. + + - The `max` variable is updated with the maximum window size seen so far. + + - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under + the given constraint. + + Space Complexity: + - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used + for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size + of the `fruits` array. + + Time Complexity: + - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the + `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall + time complexity is linear in the size of the input array. +*/ +#include +#include +#include + +using namespace std; + +int totalFruit(vector& fruits) { + int longest = 0; // Initialize a variable to track the longest subarray length + int maxFruits = 0; // Initialize a variable to store the maximum number of fruits + int startWindow = 0; // Initialize the left boundary of the window + unordered_map basket; // Create an unordered_map to track fruit counts + + for (int endWindow = 0; endWindow < fruits.size(); endWindow++) { + basket[fruits[endWindow]]++; + // Add the current fruit to the basket and increment its count + + while (basket.size() > 2) { + basket[fruits[startWindow]]--; + if (basket[fruits[startWindow]] == 0) { + basket.erase(fruits[startWindow]); + } + // Adjust the window to maintain at most two fruit types + startWindow++; // Move the left boundary of the window to the right + } + + maxFruits = max(maxFruits, endWindow - startWindow + 1); + // Update maxFruits with the maximum window size seen so far + } + + return maxFruits; // Return the maximum number of fruits that can be collected +} + +int main() { + vector fruits = {1, 2, 1, 2, 3}; + + int result = totalFruit(fruits); + + cout << result << endl; + + return 0; +} diff --git a/Sliding Window/fruits_into_basket.go b/Sliding Window/fruits_into_basket.go new file mode 100644 index 00000000..aedb3758 --- /dev/null +++ b/Sliding Window/fruits_into_basket.go @@ -0,0 +1,77 @@ +/* + Fruits into basket + Summary: + The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits + from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. + The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under + this constraint. + + - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. + + - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at + most two distinct elements and the maximum number of fruits collected, respectively. + + - The `startWindow` variable represents the left boundary of the sliding window. + + - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. + + - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse + the array from left to right. + + - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it + ensures that there are at most two different types of fruits in the window by adjusting the window as needed. + + - The `max` variable is updated with the maximum window size seen so far. + + - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under + the given constraint. + + Space Complexity: + - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used + for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size + of the `fruits` array. + + Time Complexity: + - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the + `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall + time complexity is linear in the size of the input array. +*/ +package main + +import "fmt" + +func totalFruit(fruits []int) int { + longest := 0 // Initialize a variable to track the longest subarray length + maxFruits := 0 // Initialize a variable to store the maximum number of fruits + startWindow := 0 // Initialize the left boundary of the window + basket := make(map[int]int) // Create a map to track fruit counts + + for endWindow := 0; endWindow < len(fruits); endWindow++ { + basket[fruits[endWindow]]++ + // Add the current fruit to the basket and increment its count + + for len(basket) > 2 { + basket[fruits[startWindow]]-- + if basket[fruits[startWindow]] == 0 { + delete(basket, fruits[startWindow]) + } + // Adjust the window to maintain at most two fruit types + startWindow++ // Move the left boundary of the window to the right + } + + if endWindow-startWindow+1 > maxFruits { + maxFruits = endWindow - startWindow + 1 + } + // Update maxFruits with the maximum window size seen so far + } + + return maxFruits // Return the maximum number of fruits that can be collected +} + +func main() { + fruits := []int{1, 2, 1, 2, 3} + + result := totalFruit(fruits) + + fmt.Println(result) +} diff --git a/Sliding Window/fruits_into_basket.java b/Sliding Window/fruits_into_basket.java new file mode 100644 index 00000000..fd2d8649 --- /dev/null +++ b/Sliding Window/fruits_into_basket.java @@ -0,0 +1,63 @@ +/* + Fruits into basket + Summary: + The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits + from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. + The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under + this constraint. + + - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. + + - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at + most two distinct elements and the maximum number of fruits collected, respectively. + + - The `startWindow` variable represents the left boundary of the sliding window. + + - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. + + - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse + the array from left to right. + + - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it + ensures that there are at most two different types of fruits in the window by adjusting the window as needed. + + - The `max` variable is updated with the maximum window size seen so far. + + - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under + the given constraint. + + Space Complexity: + - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used + for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size + of the `fruits` array. + + Time Complexity: + - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the + `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall + time complexity is linear in the size of the input array. +*/ +class Solution { + public int totalFruit(int[] fruits) { + int longest = 0; // Initialize a variable to track the longest subarray length + int max = 0; // Initialize a variable to store the maximum number of fruits + int startWindow = 0; // Initialize the left boundary of the window + HashMap basket = new HashMap<>(); // Create a HashMap to track fruit counts + + for(int endWindow = 0; endWindow < fruits.length; endWindow++) { + basket.put(fruits[endWindow], basket.getOrDefault(fruits[endWindow], 0) + 1); + // Add the current fruit to the basket and increment its count + + while(basket.size() > 2) { + basket.put(fruits[startWindow], basket.get(fruits[startWindow]) - 1); + basket.remove(fruits[startWindow], 0); + // Adjust the window to maintain at most two fruit types + startWindow++; // Move the left boundary of the window to the right + } + + max = Math.max(max, (endWindow - startWindow) + 1); + // Update max with the maximum window size seen so far + } + + return max; // Return the maximum number of fruits that can be collected + } +} diff --git a/Sliding Window/fruits_into_basket.js b/Sliding Window/fruits_into_basket.js new file mode 100644 index 00000000..76fd3006 --- /dev/null +++ b/Sliding Window/fruits_into_basket.js @@ -0,0 +1,63 @@ +/* + Fruits into basket + Summary: + The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits + from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. + The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under + this constraint. + + - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. + + - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at + most two distinct elements and the maximum number of fruits collected, respectively. + + - The `startWindow` variable represents the left boundary of the sliding window. + + - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. + + - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse + the array from left to right. + + - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it + ensures that there are at most two different types of fruits in the window by adjusting the window as needed. + + - The `max` variable is updated with the maximum window size seen so far. + + - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under + the given constraint. + + Space Complexity: + - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used + for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size + of the `fruits` array. + + Time Complexity: + - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the + `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall + time complexity is linear in the size of the input array. + */ +var totalFruit = function (fruits) { + let longest = 0; // Initialize a variable to track the longest subarray length + let maxFruits = 0; // Initialize a variable to store the maximum number of fruits + let startWindow = 0; // Initialize the left boundary of the window + const basket = new Map(); // Create a Map to track fruit counts + + for (let endWindow = 0; endWindow < fruits.length; endWindow++) { + basket.set(fruits[endWindow], (basket.get(fruits[endWindow]) || 0) + 1); + // Add the current fruit to the basket and increment its count + + while (basket.size > 2) { + basket.set(fruits[startWindow], basket.get(fruits[startWindow]) - 1); + if (basket.get(fruits[startWindow]) === 0) { + basket.delete(fruits[startWindow]); + } + // Adjust the window to maintain at most two fruit types + startWindow++; // Move the left boundary of the window to the right + } + + maxFruits = Math.max(maxFruits, endWindow - startWindow + 1); + // Update maxFruits with the maximum window size seen so far + } + + return maxFruits; // Return the maximum number of fruits that can be collected +}; diff --git a/Sliding Window/fruits_into_basket.py b/Sliding Window/fruits_into_basket.py new file mode 100644 index 00000000..123c184a --- /dev/null +++ b/Sliding Window/fruits_into_basket.py @@ -0,0 +1,60 @@ +''' + Fruits into basket + Summary: + The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits + from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. + The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under + this constraint. + + - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. + + - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at + most two distinct elements and the maximum number of fruits collected, respectively. + + - The `startWindow` variable represents the left boundary of the sliding window. + + - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. + + - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse + the array from left to right. + + - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it + ensures that there are at most two different types of fruits in the window by adjusting the window as needed. + + - The `max` variable is updated with the maximum window size seen so far. + + - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under + the given constraint. + + Space Complexity: + - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used + for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size + of the `fruits` array. + + Time Complexity: + - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the + `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall + time complexity is linear in the size of the input array. +''' +class Solution: + def totalFruit(self, fruits): + longest = 0 # Initialize a variable to track the longest subarray length + max_fruits = 0 # Initialize a variable to store the maximum number of fruits + start_window = 0 # Initialize the left boundary of the window + basket = {} # Create a dictionary to track fruit counts + + for end_window in range(len(fruits)): + basket[fruits[end_window]] = basket.get(fruits[end_window], 0) + 1 + # Add the current fruit to the basket and increment its count + + while len(basket) > 2: + basket[fruits[start_window]] -= 1 + if basket[fruits[start_window]] == 0: + del basket[fruits[start_window]] + # Adjust the window to maintain at most two fruit types + start_window += 1 # Move the left boundary of the window to the right + + max_fruits = max(max_fruits, (end_window - start_window) + 1) + # Update max_fruits with the maximum window size seen so far + + return max_fruits # Return the maximum number of fruits that can be collected diff --git a/Sliding Window/longest_repeated_character_replacement.cpp b/Sliding Window/longest_repeated_character_replacement.cpp new file mode 100644 index 00000000..784512a3 --- /dev/null +++ b/Sliding Window/longest_repeated_character_replacement.cpp @@ -0,0 +1,64 @@ +/* + You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. + + Return the length of the longest substring containing the same letter you can get after performing the above operations. + + Example 1: + + Input: s = "ABAB", k = 2 + Output: 4 + Explanation: Replace the two 'A's with two 'B's or vice versa. + + Summary: + The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the + longest substring within the input string `s` such that it can be created by replacing at most `k` characters + with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a + substring. + + Time Complexity: + - The code iterates through the input string `s` using a sliding window with two pointers + (startWindow and endWindow). During each iteration, it updates character counts and evaluates the + maximum length of a valid substring. Since each character is processed exactly once, the time complexity + is O(N), where N is the length of the input string `s`. + + Space Complexity: + - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). + The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is + O(1), as the space used is constant and does not depend on the input size. + + In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient + for finding the longest substring with at most 'k' replacements in a given string. + */ +#include +#include +#include + +class Solution { +public: + int characterReplacement(std::string s, int k) { + std::vector count(26, 0); // Initialize an array to count the occurrences of characters (26 letters in the English alphabet) + int startWindow = 0; // The left end of the sliding window + int maxCount = 0; // The maximum count of any character within the window + int max = 0; // The maximum length of a substring that can be formed + + // Iterate through the string using a sliding window approach + for (int endWindow = 0; endWindow < s.length(); endWindow++) { + int val = s[endWindow] - 'A'; // Convert the character to an index (0-25) + count[val]++; // Increment the count for the current character + maxCount = std::max(maxCount, count[val]); // Update the maximum character count + + // While the length of the current window minus the maximum character count exceeds 'k', shrink the window + while (endWindow - startWindow + 1 - maxCount > k) { + val = s[startWindow] - 'A'; // Get the character at the start of the window + count[val]--; // Decrement the count for the character at the start of the window + startWindow++; // Move the start of the window to the right + } + + // Update the maximum length of a substring that can be formed + max = std::max(max, endWindow - startWindow + 1); + } + + // Return the maximum length, which represents the longest substring with at most 'k' replacements + return max; + } +}; diff --git a/Sliding Window/longest_repeated_character_replacement.go b/Sliding Window/longest_repeated_character_replacement.go new file mode 100644 index 00000000..a3a438de --- /dev/null +++ b/Sliding Window/longest_repeated_character_replacement.go @@ -0,0 +1,64 @@ +/* + You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. + + Return the length of the longest substring containing the same letter you can get after performing the above operations. + + Example 1: + + Input: s = "ABAB", k = 2 + Output: 4 + Explanation: Replace the two 'A's with two 'B's or vice versa. + + Summary: + The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the + longest substring within the input string `s` such that it can be created by replacing at most `k` characters + with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a + substring. + + Time Complexity: + - The code iterates through the input string `s` using a sliding window with two pointers + (startWindow and endWindow). During each iteration, it updates character counts and evaluates the + maximum length of a valid substring. Since each character is processed exactly once, the time complexity + is O(N), where N is the length of the input string `s`. + + Space Complexity: + - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). + The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is + O(1), as the space used is constant and does not depend on the input size. + + In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient + for finding the longest substring with at most 'k' replacements in a given string. + */ + func characterReplacement(s string, k int) int { + count := make([]int, 26) // Initialize an array to count the occurrences of characters (26 letters in the English alphabet) + startWindow := 0 // The left end of the sliding window + maxCount := 0 // The maximum count of any character within the window + max := 0 // The maximum length of a substring that can be formed + + // Iterate through the string using a sliding window approach + for endWindow := 0; endWindow < len(s); endWindow++ { + val := int(s[endWindow] - 'A') // Convert the character to an index (0-25) + count[val]++ // Increment the count for the current character + maxCount = maxInt(maxCount, count[val]) // Update the maximum character count + + // While the length of the current window minus the maximum character count exceeds 'k', shrink the window + for endWindow - startWindow + 1 - maxCount > k { + val = int(s[startWindow] - 'A') // Get the character at the start of the window + count[val]-- // Decrement the count for the character at the start of the window + startWindow++ // Move the start of the window to the right + } + + // Update the maximum length of a substring that can be formed + max = maxInt(max, endWindow - startWindow + 1) + } + + // Return the maximum length, which represents the longest substring with at most 'k' replacements + return max +} + +func maxInt(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/Sliding Window/longest_repeated_character_replacement.java b/Sliding Window/longest_repeated_character_replacement.java new file mode 100644 index 00000000..51defebd --- /dev/null +++ b/Sliding Window/longest_repeated_character_replacement.java @@ -0,0 +1,60 @@ +/* + You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. + + Return the length of the longest substring containing the same letter you can get after performing the above operations. + + Example 1: + + Input: s = "ABAB", k = 2 + Output: 4 + Explanation: Replace the two 'A's with two 'B's or vice versa. + + Summary: + The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the + longest substring within the input string `s` such that it can be created by replacing at most `k` characters + with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a + substring. + + Time Complexity: + - The code iterates through the input string `s` using a sliding window with two pointers + (startWindow and endWindow). During each iteration, it updates character counts and evaluates the + maximum length of a valid substring. Since each character is processed exactly once, the time complexity + is O(N), where N is the length of the input string `s`. + + Space Complexity: + - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). + The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is + O(1), as the space used is constant and does not depend on the input size. + + In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient + for finding the longest substring with at most 'k' replacements in a given string. + */ +class Solution { + public int characterReplacement(String s, int k) { + // Initialize an array to count the occurrences of characters (26 letters in the English alphabet) + int[] count = new int[26]; + int startWindow = 0; // The left end of the sliding window + int maxCount = 0; // The maximum count of any character within the window + int max = 0; // The maximum length of a substring that can be formed + + // Iterate through the string using a sliding window approach + for (int endWindow = 0; endWindow < s.length(); endWindow++) { + int val = s.charAt(endWindow) - 'A'; // Convert the character to an index (0-25) + count[val]++; // Increment the count for the current character + maxCount = Math.max(maxCount, count[val]); // Update the maximum character count + + // While the length of the current window minus the maximum character count exceeds 'k', shrink the window + while (endWindow - startWindow + 1 - maxCount > k) { + val = s.charAt(startWindow) - 'A'; // Get the character at the start of the window + count[val]--; // Decrement the count for the character at the start of the window + startWindow++; // Move the start of the window to the right + } + + // Update the maximum length of a substring that can be formed + max = Math.max(max, endWindow - startWindow + 1); + } + + // Return the maximum length, which represents the longest substring with at most 'k' replacements + return max; + } +} diff --git a/Sliding Window/longest_repeated_character_replacement.js b/Sliding Window/longest_repeated_character_replacement.js new file mode 100644 index 00000000..5b239116 --- /dev/null +++ b/Sliding Window/longest_repeated_character_replacement.js @@ -0,0 +1,64 @@ +/* + You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. + + Return the length of the longest substring containing the same letter you can get after performing the above operations. + + Example 1: + + Input: s = "ABAB", k = 2 + Output: 4 + Explanation: Replace the two 'A's with two 'B's or vice versa. + + Summary: + The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the + longest substring within the input string `s` such that it can be created by replacing at most `k` characters + with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a + substring. + + Time Complexity: + - The code iterates through the input string `s` using a sliding window with two pointers + (startWindow and endWindow). During each iteration, it updates character counts and evaluates the + maximum length of a valid substring. Since each character is processed exactly once, the time complexity + is O(N), where N is the length of the input string `s`. + + Space Complexity: + - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). + The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is + O(1), as the space used is constant and does not depend on the input size. + + In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient + for finding the longest substring with at most 'k' replacements in a given string. +*/ +class Solution { + characterReplacement(s, k) { + const count = Array(26).fill(0); // Initialize an array to count the occurrences of characters (26 letters in the English alphabet) + let startWindow = 0; // The left end of the sliding window + let maxCount = 0; // The maximum count of any character within the window + let max_length = 0; // The maximum length of a substring that can be formed + + // Iterate through the string using a sliding window approach + for (let endWindow = 0; endWindow < s.length; endWindow++) { + const val = s.charCodeAt(endWindow) - "A".charCodeAt(0); // Convert the character to an index (0-25) + count[val]++; // Increment the count for the current character + maxCount = Math.max(maxCount, count[val]); // Update the maximum character count + + // While the length of the current window minus the maximum character count exceeds 'k', shrink the window + while (endWindow - startWindow + 1 - maxCount > k) { + const val = s.charCodeAt(startWindow) - "A".charCodeAt(0); // Get the character at the start of the window + count[val]--; // Decrement the count for the character at the start of the window + startWindow++; // Move the start of the window to the right + } + + // Update the maximum length of a substring that can be formed + max_length = Math.max(max_length, endWindow - startWindow + 1); + } + + // Return the maximum length, which represents the longest substring with at most 'k' replacements + return max_length; + } +} + +// Example usage +const solution = new Solution(); +const result = solution.characterReplacement("ABAB", 2); +console.log(result); // Output: 4 diff --git a/Sliding Window/longest_repeated_character_replacement.py b/Sliding Window/longest_repeated_character_replacement.py new file mode 100644 index 00000000..82ae1559 --- /dev/null +++ b/Sliding Window/longest_repeated_character_replacement.py @@ -0,0 +1,55 @@ +''' + You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. + + Return the length of the longest substring containing the same letter you can get after performing the above operations. + + Example 1: + + Input: s = "ABAB", k = 2 + Output: 4 + Explanation: Replace the two 'A's with two 'B's or vice versa. + + Summary: + The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the + longest substring within the input string `s` such that it can be created by replacing at most `k` characters + with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a + substring. + + Time Complexity: + - The code iterates through the input string `s` using a sliding window with two pointers + (startWindow and endWindow). During each iteration, it updates character counts and evaluates the + maximum length of a valid substring. Since each character is processed exactly once, the time complexity + is O(N), where N is the length of the input string `s`. + + Space Complexity: + - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). + The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is + O(1), as the space used is constant and does not depend on the input size. + + In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient + for finding the longest substring with at most 'k' replacements in a given string. +''' +class Solution: + def characterReplacement(self, s: str, k: int) -> int: + count = [0] * 26 # Initialize a list to count the occurrences of characters (26 letters in the English alphabet) + startWindow = 0 # The left end of the sliding window + maxCount = 0 # The maximum count of any character within the window + max_length = 0 # The maximum length of a substring that can be formed + + # Iterate through the string using a sliding window approach + for endWindow in range(len(s)): + val = ord(s[endWindow]) - ord('A') # Convert the character to an index (0-25) + count[val] += 1 # Increment the count for the current character + maxCount = max(maxCount, count[val]) # Update the maximum character count + + # While the length of the current window minus the maximum character count exceeds 'k', shrink the window + while endWindow - startWindow + 1 - maxCount > k: + val = ord(s[startWindow]) - ord('A') # Get the character at the start of the window + count[val] -= 1 # Decrement the count for the character at the start of the window + startWindow += 1 # Move the start of the window to the right + + # Update the maximum length of a substring that can be formed + max_length = max(max_length, endWindow - startWindow + 1) + + # Return the maximum length, which represents the longest substring with at most 'k' replacements + return max_length diff --git a/Sliding Window/longest_substring_with_k_distinct_chars.go b/Sliding Window/longest_substring_with_k_distinct_chars.go new file mode 100644 index 00000000..63491976 --- /dev/null +++ b/Sliding Window/longest_substring_with_k_distinct_chars.go @@ -0,0 +1,51 @@ +/* + * Given a string, find the length of the longest substring in it with no more than K distinct characters. + + */ +package main + +import ( + "fmt" +) + +func longestSubstringWithKDistinctChars(s string, k int) int { + // Check for edge cases where s is empty, or k is 0. + if len(s) == 0 || k == 0 { + return 0 + } + + startWindow := 0 // Initialize the start of the sliding window. + maxLen := 0 // Initialize the maximum length of the substring. + charCount := make(map[byte]int) // Create a map to store character frequencies. + + for endWindow := 0; endWindow < len(s); endWindow++ { + right := s[endWindow] // Get the character at the end of the window. + charCount[right]++ // Update the character count in the map. + + // While there are more than k distinct characters in the window. + for len(charCount) > k { + left := s[startWindow] // Get the character at the start of the window. + charCount[left]-- // Decrease the count of the character. + if charCount[left] == 0 { + delete(charCount, left) // If the count becomes 0, remove the character from the map. + } + startWindow++ // Move the start of the window to the right. + } + + maxLen = max(maxLen, endWindow-startWindow+1) // Update the maximum length. + } + return maxLen +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func main() { + fmt.Println(longestSubstringWithKDistinctChars("araaci", 2)) + fmt.Println(longestSubstringWithKDistinctChars("araaci", 1)) + fmt.Println(longestSubstringWithKDistinctChars("cbbebi", 3)) +} diff --git a/Sliding Window/longest_substring_with_k_distinct_chars.java b/Sliding Window/longest_substring_with_k_distinct_chars.java new file mode 100644 index 00000000..59e4b554 --- /dev/null +++ b/Sliding Window/longest_substring_with_k_distinct_chars.java @@ -0,0 +1,43 @@ + +/* + * Given a string, find the length of the longest substring in it with no more than K distinct characters. + + */ +public class LongestSubstringWithKDistinctChars { + public int longestSubstringWithKDistinctChars(String s, int k) { + // Check for edge cases where s is empty, null, or k is 0. + if (s.length() == 0 || s == null || k == 0) { + return 0; + } + + int startWindow = 0; // Initialize the start of the sliding window. + int maxlen = 0; // Initialize the maximum length of the substring. + HashMap mp = new HashMap(); // Create a HashMap to store character frequencies. + + for (int endWindow = 0; endWindow < s.length(); endWindow++) { + char right = s.charAt(endWindow); // Get the character at the end of the window. + mp.put(right, mp.getOrDefault(right, 0) + 1); // Update the character count in the HashMap. + + // While there are more than k distinct characters in the window. + while (mp.size() > k) { + char left = s.charAt(startWindow); // Get the character at the start of the window. + mp.put(left, mp.get(left) - 1); // Decrease the count of the character. + startWindow++; // Move the start of the window to the right. + + if (mp.get(left) == 0) { + mp.remove(left); // If the count becomes 0, remove the character from the HashMap. + } + } + + maxlen = Math.max(maxlen, endWindow - startWindow + 1); // Update the maximum length. + } + return maxlen; + } + + public static void main(String[] args) { + LongestSubstringWithKDistinctChars l = new LongestSubstringWithKDistinctChars(); + System.out.println(l.longestSubstringWithKDistinctChars("araaci", 2)); + System.out.println(l.longestSubstringWithKDistinctChars("araaci", 1)); + System.out.println(l.longestSubstringWithKDistinctChars("cbbebi", 3)); + } +} diff --git a/Sliding Window/longest_substring_with_k_distinct_chars.js b/Sliding Window/longest_substring_with_k_distinct_chars.js new file mode 100644 index 00000000..5a418427 --- /dev/null +++ b/Sliding Window/longest_substring_with_k_distinct_chars.js @@ -0,0 +1,41 @@ +/* + * Given a string, find the length of the longest substring in it with no more than K distinct characters. + + */ +function longestSubstringWithKDistinctChars(s, k) { + // Check for edge cases where s is empty, or k is 0. + if (!s || k === 0) { + return 0; + } + + let startWindow = 0; // Initialize the start of the sliding window. + let maxLen = 0; // Initialize the maximum length of the substring. + const charCount = {}; // Create an object to store character frequencies. + + for (let endWindow = 0; endWindow < s.length; endWindow++) { + const right = s[endWindow]; // Get the character at the end of the window. + charCount[right] = (charCount[right] || 0) + 1; // Update the character count in the object. + + // While there are more than k distinct characters in the window. + while (Object.keys(charCount).length > k) { + const left = s[startWindow]; // Get the character at the start of the window. + charCount[left]--; // Decrease the count of the character. + if (charCount[left] === 0) { + delete charCount[left]; // If the count becomes 0, remove the character from the object. + } + startWindow++; // Move the start of the window to the right. + } + + maxLen = Math.max(maxLen, endWindow - startWindow + 1); // Update the maximum length. + } + + return maxLen; +} + +function max(a, b) { + return a > b ? a : b; +} + +console.log(longestSubstringWithKDistinctChars("araaci", 2)); +console.log(longestSubstringWithKDistinctChars("araaci", 1)); +console.log(longestSubstringWithKDistinctChars("cbbebi", 3)); diff --git a/Sliding Window/longest_substring_with_k_distinct_chars.py b/Sliding Window/longest_substring_with_k_distinct_chars.py new file mode 100644 index 00000000..adb1fc7c --- /dev/null +++ b/Sliding Window/longest_substring_with_k_distinct_chars.py @@ -0,0 +1,38 @@ +''' +Given a string, find the length of the longest substring in it with no more than K distinct characters. + +''' +def longestSubstringWithKDistinctChars(s, k): + # Check for edge cases where s is empty, or k is 0. + if not s or k == 0: + return 0 + + start_window = 0 # Initialize the start of the sliding window. + max_len = 0 # Initialize the maximum length of the substring. + char_count = {} # Create a dictionary to store character frequencies. + + for end_window in range(len(s)): + right = s[end_window] # Get the character at the end of the window. + char_count[right] = char_count.get(right, 0) + 1 # Update the character count in the dictionary. + + # While there are more than k distinct characters in the window. + while len(char_count) > k: + left = s[start_window] # Get the character at the start of the window. + char_count[left] -= 1 # Decrease the count of the character. + if char_count[left] == 0: + del char_count[left] # If the count becomes 0, remove the character from the dictionary. + start_window += 1 # Move the start of the window to the right. + + max_len = max(max_len, end_window - start_window + 1) # Update the maximum length. + + return max_len + +def max(a, b): + if a > b: + return a + return b + +if __name__ == "__main__": + print(longestSubstringWithKDistinctChars("araaci", 2)) + print(longestSubstringWithKDistinctChars("araaci", 1)) + print(longestSubstringWithKDistinctChars("cbbebi", 3)) diff --git a/Sliding Window/longest_substring_without_repeating_characters.go b/Sliding Window/longest_substring_without_repeating_characters.go new file mode 100644 index 00000000..6be51382 --- /dev/null +++ b/Sliding Window/longest_substring_without_repeating_characters.go @@ -0,0 +1,83 @@ +package main + +/* +Given a string s, find the length of the longest substring without repeating characters. + + + +Example 1: + +Input: s = "abcabcbb" +Output: 3 +Explanation: The answer is "abc", with the length of 3. +Example 2: + +Input: s = "bbbbb" +Output: 1 +Explanation: The answer is "b", with the length of 1. +Example 3: + +Input: s = "pwwkew" +Output: 3 +Explanation: The answer is "wke", with the length of 3. +Notice that the answer must be a substring, "pwke" is a subsequence and not a substring. + + +Constraints: + +0 <= s.length <= 5 * 104 +s consists of English letters, digits, symbols and spaces. +*/ +// Approach 1 Brute force +// Intuition +// Check all the substring one by one to see if it has no duplicate character. +// Time complexity O(n³ ) Space complexity O(m) where m is size of hMap +func LengthOfLongestSubstring1(s string) int { + n := len(s) + res := 0 + for i := 0; i < n; i++ { + for j := i; j < n; j++ { + if check(s, i, j) { + res = max(res, j - i + 1) + } + } + } + return res +} +func max(a, b int) int { + if a >= b { + return a + } else { + return b + } +} +func check(s string, start int, end int) bool { + hMap := [128]int{} + for i := start; i <= end; i++ { + hMap[s[i]]++ + if hMap[s[i]] > 1 { + return false + } + } + return true +} + +// Approach 2: Sliding window +// Time complexity O(n) Space complexity O(m) where m is size of char +func LengthOfLongestSubstring2(s string) int { + result := 0 + for i := 0; i < len(s); i++ { + char := make(map[byte]bool) + char[s[i]] = true + for j := i + 1; j < len(s); j++ { + if _, ok := char[s[j]]; ok { + break + } + char[s[j]] = true + } + if len(char) > result { + result = len(char) + } + } + return result +} \ No newline at end of file diff --git a/Sliding Window/max_eraser_value.go b/Sliding Window/max_eraser_value.go new file mode 100644 index 00000000..fb964df2 --- /dev/null +++ b/Sliding Window/max_eraser_value.go @@ -0,0 +1,82 @@ +/* + You are given an array of positive integers nums and want to erase a subarray containing unique elements. The score you get by erasing the subarray is equal to the sum of its elements. + + Return the maximum score you can get by erasing exactly one subarray. + + An array b is called to be a subarray of a if it forms a contiguous subsequence of a, that is, if it is equal to a[l],a[l+1],...,a[r] for some (l,r). + + + + Example 1: + + Input: nums = [4,2,4,5,6] + Output: 17 + Explanation: The optimal subarray here is [2,4,5,6]. + Example 2: + + Input: nums = [5,2,1,2,5,2,1,2,5] + Output: 8 + Explanation: The optimal subarray here is [5,2,1] or [1,2,5]. + + Explanation: + + startWindow: The left end of the sliding window. + windowSum: Sum of elements within the current window. + res: Result, initialized to 0, representing the maximum unique subarray sum. + mp: HashMap to store the last index where each element was seen. + Sliding Window: + + Use a for loop to iterate through the array with the endWindow as the right end of the window. + Check if the current element is already in the window using a while loop. + If yes, remove the element at the start of the window from the HashMap and update windowSum and startWindow. + Update HashMap and windowSum: + + Add the current element to the HashMap and update windowSum. + Update Result: + + Update the result (res) with the maximum unique subarray sum. + Return Result: + + Return the final result, which represents the maximum unique subarray sum. + This code efficiently finds the maximum sum of a subarray where all elements are unique using a sliding window and a HashMap to keep track of the last index of each element encountered. + +*/ +package main + +import "fmt" + +func maximumUniqueSubarray(nums []int) int { + startWindow := 0 // The left end of the sliding window + windowSum := 0 // Sum of elements within the current window + res := 0 // Result, which represents the maximum unique subarray sum + mp := make(map[int]int) // Map to store the last index where each element was seen + + // Iterate through the array using a sliding window approach + for endWindow := 0; endWindow < len(nums); endWindow++ { + // Check if the current element is already in the window + for mp[nums[endWindow]] > 0 { + // Remove the element at the start of the window from the map and update windowSum + mp[nums[startWindow]]-- + windowSum -= nums[startWindow] + startWindow++ + } + + // Add the current element to the map and update windowSum + mp[nums[endWindow]]++ + windowSum += nums[endWindow] + + // Update the result with the maximum unique subarray sum + if windowSum > res { + res = windowSum + } + } + + // Return the result, which represents the maximum unique subarray sum + return res +} + +func main() { + nums := []int{4, 2, 4, 5, 6} + result := maximumUniqueSubarray(nums) + fmt.Println(result) // Output: 17 +} diff --git a/Sliding Window/max_eraser_value.java b/Sliding Window/max_eraser_value.java new file mode 100644 index 00000000..9f9744a0 --- /dev/null +++ b/Sliding Window/max_eraser_value.java @@ -0,0 +1,75 @@ +/* + You are given an array of positive integers nums and want to erase a subarray containing unique elements. The score you get by erasing the subarray is equal to the sum of its elements. + + Return the maximum score you can get by erasing exactly one subarray. + + An array b is called to be a subarray of a if it forms a contiguous subsequence of a, that is, if it is equal to a[l],a[l+1],...,a[r] for some (l,r). + + + + Example 1: + + Input: nums = [4,2,4,5,6] + Output: 17 + Explanation: The optimal subarray here is [2,4,5,6]. + Example 2: + + Input: nums = [5,2,1,2,5,2,1,2,5] + Output: 8 + Explanation: The optimal subarray here is [5,2,1] or [1,2,5]. + + Explanation: + + startWindow: The left end of the sliding window. + windowSum: Sum of elements within the current window. + res: Result, initialized to 0, representing the maximum unique subarray sum. + mp: HashMap to store the last index where each element was seen. + Sliding Window: + + Use a for loop to iterate through the array with the endWindow as the right end of the window. + Check if the current element is already in the window using a while loop. + If yes, remove the element at the start of the window from the HashMap and update windowSum and startWindow. + Update HashMap and windowSum: + + Add the current element to the HashMap and update windowSum. + Update Result: + + Update the result (res) with the maximum unique subarray sum. + Return Result: + + Return the final result, which represents the maximum unique subarray sum. + This code efficiently finds the maximum sum of a subarray where all elements are unique using a sliding window and a HashMap to keep track of the last index of each element encountered. + +*/ +class Solution { + public int maximumUniqueSubarray(int[] nums) { + // Initialize pointers, windowSum, and result + int startWindow = 0; // The left end of the sliding window + int windowSum = 0; // Sum of elements within the current window + int res = 0; // Result, which represents the maximum unique subarray sum + + // HashMap to store the last index where each element was seen + HashMap mp = new HashMap<>(); + + // Iterate through the array using a sliding window approach + for (int endWindow = 0; endWindow < nums.length; endWindow++) { + // Check if the current element is already in the window + while (mp.containsKey(nums[endWindow])) { + // Remove the element at the start of the window from the HashMap and update windowSum + mp.remove(nums[startWindow]); + windowSum -= nums[startWindow]; + startWindow++; + } + + // Add the current element to the HashMap and update windowSum + mp.put(nums[endWindow], 1); + windowSum += nums[endWindow]; + + // Update the result with the maximum unique subarray sum + res = Math.max(res, windowSum); + } + + // Return the result, which represents the maximum unique subarray sum + return res; + } +} diff --git a/Sliding Window/sliding_window_max.java b/Sliding Window/sliding_window_max.java new file mode 100644 index 00000000..7951979a --- /dev/null +++ b/Sliding Window/sliding_window_max.java @@ -0,0 +1,61 @@ +/* + Given an integer array and a window of size windowSize, find the current maximum value in the window as it slides through the entire array +*/ +import java.util.*; + +public class SlidingWindowMax { + public static int[] findMaxSlidingWindow(int[] nums, int windowSize) { + if (nums == null || windowSize <= 0 || nums.length < windowSize) { + return new int[0]; + } + + int n = nums.length; + int[] result = new int[n - windowSize + 1]; + Deque deque = new ArrayDeque<>(); // deque stores the indices of elements in the window + + // process the first window separately + for (int i = 0; i < windowSize; i++) { + while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { + deque.removeLast(); + } + deque.addLast(i); + } + result[0] = nums[deque.peekFirst()]; + + // process the remaining windows + for (int i = windowSize; i < n; i++) { + while (!deque.isEmpty() && deque.peekFirst() <= i - windowSize) { + deque.removeFirst(); // remove elements outside the window + } + while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { + deque.removeLast(); // remove elements smaller than nums[i] + } + deque.addLast(i); + result[i - windowSize + 1] = nums[deque.peekFirst()]; + } + + return result; + } + + public static void main(String[] args) { + int[] nums = {1, 3, -1, -3, 5, 3, 6, 7}; + int windowSize = 3; + + int[] maxInWindow = findMaxSlidingWindow(nums, windowSize); + + System.out.println("Max values in sliding window of size " + windowSize + ": "); + for (int i = 0; i < maxInWindow.length; i++) { + System.out.print(maxInWindow[i] + " "); + } + } +} + +---------------Explanation--------------------- + +This program defines a findMaxSlidingWindow method that takes an array nums and a window size windowSize as input and returns an array containing the maximum value in each sliding window. The method first checks if the input is valid and creates a deque to store the indices of elements in the window. + +The method then processes the first window separately by adding the indices of its elements to the deque in decreasing order of their values. This way, the first element in the deque will always be the maximum value in the window. The method stores this maximum value in the first element of the result array. + +The method then processes the remaining windows by first removing any elements in the deque that are outside the current window. The method then adds the index of the current element to the deque and removes any elements in the deque that are smaller than the current element. The method stores the maximum value in the window in the corresponding element of the result array. + +Finally, the method returns the result array. \ No newline at end of file diff --git a/Sliding Window/sliding_window_max.js b/Sliding Window/sliding_window_max.js new file mode 100644 index 00000000..2724177f --- /dev/null +++ b/Sliding Window/sliding_window_max.js @@ -0,0 +1,55 @@ +/* + Given an integer array and a window of size windowSize, find the current maximum value in the window as it slides through the entire array +*/ +function findMaxSlidingWindow(nums, windowSize) { + if (!nums || windowSize <= 0 || nums.length < windowSize) { + return []; + } + + const n = nums.length; + const result = new Array(n - windowSize + 1).fill(0); + const deque = []; // deque stores the indices of elements in the window + + // process the first window separately + for (let i = 0; i < windowSize; i++) { + while (deque.length > 0 && nums[i] >= nums[deque[deque.length - 1]]) { + deque.pop(); + } + deque.push(i); + } + result[0] = nums[deque[0]]; + + // process the remaining windows + for (let i = windowSize; i < n; i++) { + while (deque.length > 0 && deque[0] <= i - windowSize) { + deque.shift(); // remove elements outside the window + } + while (deque.length > 0 && nums[i] >= nums[deque[deque.length - 1]]) { + deque.pop(); // remove elements smaller than nums[i] + } + deque.push(i); + result[i - windowSize + 1] = nums[deque[0]]; + } + + return result; +} + +const nums = [1, 3, -1, -3, 5, 3, 6, 7]; +const windowSize = 3; + +const maxInWindow = findMaxSlidingWindow(nums, windowSize); + +console.log(`Max values in sliding window of size ${windowSize}:`); +console.log(maxInWindow.join(' ')); + +-------------Explanation--------------------- + +This code defines a findMaxSlidingWindow function that takes an array nums and a window size windowSize as input and returns an array containing the maximum value in each sliding window. The function first checks if the input is valid and creates an empty array result and a deque to store the indices of elements in the window. + +The function then processes the first window separately by adding the indices of its elements to the deque in decreasing order of their values. This way, the first element in the deque will always be the maximum value in the window. The function stores this maximum value in the first element of the result array. + +The function then processes the remaining windows by first removing any elements in the deque that are outside the current window. The function then adds the index of the current element to the deque and removes any elements in the deque that are smaller than the current element. The function stores the maximum value in the window in the corresponding element of the result array. + +Finally, the function returns the result array. + +The code also includes an example usage of the findMaxSlidingWindow function on an example array and window size. When the code is executed, it prints the maximum values in each sliding window to the console. \ No newline at end of file diff --git a/Sliding Window/sliding_window_max.py b/Sliding Window/sliding_window_max.py new file mode 100644 index 00000000..cb3bfa9c --- /dev/null +++ b/Sliding Window/sliding_window_max.py @@ -0,0 +1,20 @@ +# Given an integer array and a window of size windowSize, find the current maximum value in the window as it slides through the entire array + +from collections import deque + +def slidingWindowMax(arr,windowSize) -> list[int]: + dq = deque() + max_elements = [] + for i in range(len(arr)): + if(len(dq) != 0 and dq[0]== i - windowSize): + dq.popleft() + while(len(dq) !=0 and arr[dq[-1]] < arr[i]): + dq.pop() + dq.append(i) + if(i >= windowSize - 1): + max_elements.append(arr[dq[0]]) + return max_elements + +arr = list(map(int, input().split())) +windowSize = int(input()) +print(slidingWindowMax(arr, windowSize)) diff --git a/Sliding Window/subaray_sum_equals_k.cpp b/Sliding Window/subaray_sum_equals_k.cpp new file mode 100644 index 00000000..13f1b5b6 --- /dev/null +++ b/Sliding Window/subaray_sum_equals_k.cpp @@ -0,0 +1,101 @@ +/* + Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. + + A subarray is a contiguous non-empty sequence of elements within an array. + + + + Example 1: + + Input: nums = [1,1,1], k = 2 + Output: 2 + Example 2: + + Input: nums = [1,2,3], k = 3 + Output: 2 + + + ### Explanation: + + The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. + It uses a hashmap to efficiently keep track of cumulative sums and their counts. + + Here's the code's key logic: + + 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. + + 2. It initializes variables `result` and `currentSum`. + + 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). + + 4. It iterates through the elements of the 'nums' array. + + 5. For each element, it adds the element's value to `currentSum`. + + 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. + + 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. + + 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, + it adds it with a count of 1. + + 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. + + ### Time Complexity: + + The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' + array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). + + ### Space Complexity: + + The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, + which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, + each with a count of 1. + + In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. + */ +#include +#include +#include + +int subarraySum(std::vector& nums, int k) { + // Create an unordered_map to store cumulative sums as keys and their counts as values. + std::unordered_map sumIndex; + // Initialize the result to 0. + int result = 0; + // Initialize a variable to track the current cumulative sum. + int currentSum = 0; + // Initialize the unordered_map with a key-value pair representing the sum of an empty subarray (0) and its count (1). + sumIndex[0] = 1; + + // Iterate through the elements of the 'nums' vector. + for (int i = 0; i < nums.size(); i++) { + // Add the current element to the cumulative sum. + currentSum += nums[i]; + // Calculate the value to find in the unordered_map by subtracting 'k' from the current cumulative sum. + int toFind = currentSum - k; + + // Check if the unordered_map contains the value 'toFind'. + if (sumIndex.find(toFind) != sumIndex.end()) { + // If found, add the count of subarrays that sum to 'toFind' to the 'result'. + result += sumIndex[toFind]; + } + + // Update the unordered_map with the current cumulative sum. + // If it's already in the unordered_map, increment its count by 1. + // If it's not in the unordered_map, add it with a count of 1. + sumIndex[currentSum]++; + + } + + // Return the final result, which represents the total number of subarrays with a sum of 'k'. + return result; +} + +int main() { + std::vector nums = {1, 1, 1}; + int k = 2; + int result = subarraySum(nums, k); + std::cout << "Number of subarrays with sum " << k << " is: " << result << std::endl; + return 0; +} diff --git a/Sliding Window/subaray_sum_equals_k.go b/Sliding Window/subaray_sum_equals_k.go new file mode 100644 index 00000000..808cc626 --- /dev/null +++ b/Sliding Window/subaray_sum_equals_k.go @@ -0,0 +1,88 @@ +/* + Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. + + A subarray is a contiguous non-empty sequence of elements within an array. + + + + Example 1: + + Input: nums = [1,1,1], k = 2 + Output: 2 + Example 2: + + Input: nums = [1,2,3], k = 3 + Output: 2 + + + ### Explanation: + + The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. + It uses a hashmap to efficiently keep track of cumulative sums and their counts. + + Here's the code's key logic: + + 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. + + 2. It initializes variables `result` and `currentSum`. + + 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). + + 4. It iterates through the elements of the 'nums' array. + + 5. For each element, it adds the element's value to `currentSum`. + + 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. + + 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. + + 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, + it adds it with a count of 1. + + 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. + + ### Time Complexity: + + The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' + array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). + + ### Space Complexity: + + The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, + which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, + each with a count of 1. + + In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. + */ + func subarraySum(nums []int, k int) int { + // Create a map to store cumulative sums as keys and their counts as values. + sumIndex := make(map[int]int) + // Initialize the result to 0. + result := 0 + // Initialize a variable to track the current cumulative sum. + currentSum := 0 + // Initialize the map with a key-value pair representing the sum of an empty subarray (0) and its count (1). + sumIndex[0] = 1 + + // Iterate through the elements of the 'nums' slice. + for _, num := range nums { + // Add the current element to the cumulative sum. + currentSum += num + // Calculate the value to find in the map by subtracting 'k' from the current cumulative sum. + toFind := currentSum - k + + // Check if the map contains the value 'toFind'. + if count, ok := sumIndex[toFind]; ok { + // If found, add the count of subarrays that sum to 'toFind' to the 'result'. + result += count + } + + // Update the map with the current cumulative sum. + // If it's already in the map, increment its count by 1. + // If it's not in the map, add it with a count of 1. + sumIndex[currentSum]++ + } + + // Return the final result, which represents the total number of subarrays with a sum of 'k'. + return result +} diff --git a/Sliding Window/subaray_sum_equals_k.java b/Sliding Window/subaray_sum_equals_k.java new file mode 100644 index 00000000..f1e0d988 --- /dev/null +++ b/Sliding Window/subaray_sum_equals_k.java @@ -0,0 +1,88 @@ +/* + Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. + + A subarray is a contiguous non-empty sequence of elements within an array. + + + + Example 1: + + Input: nums = [1,1,1], k = 2 + Output: 2 + Example 2: + + Input: nums = [1,2,3], k = 3 + Output: 2 + + + ### Explanation: + + The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. + It uses a hashmap to efficiently keep track of cumulative sums and their counts. + + Here's the code's key logic: + + 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. + + 2. It initializes variables `result` and `currentSum`. + + 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). + + 4. It iterates through the elements of the 'nums' array. + + 5. For each element, it adds the element's value to `currentSum`. + + 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. + + 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. + + 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, + it adds it with a count of 1. + + 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. + + ### Time Complexity: + + The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' + array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). + + ### Space Complexity: + + The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, + which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, + each with a count of 1. + + In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. + */ +public int subarraySum(int[] nums, int k) { + // Create a hashmap to store cumulative sums as keys and their counts as values. + HashMap sumIndex = new HashMap<>(); + // Initialize the result to 0. + int result = 0; + // Initialize a variable to track the current cumulative sum. + int currentSum = 0; + // Initialize the hashmap with a key-value pair representing the sum of an empty subarray (0) and its count (1). + sumIndex.put(0, 1); + + // Iterate through the elements of the 'nums' array. + for (int i = 0; i < nums.length; i++) { + // Add the current element to the cumulative sum. + currentSum += nums[i]; + // Calculate the value to find in the hashmap by subtracting 'k' from the current cumulative sum. + int toFind = currentSum - k; + + // Check if the hashmap contains the value 'toFind'. + if (sumIndex.containsKey(toFind)) { + // If found, add the count of subarrays that sum to 'toFind' to the 'result'. + result += sumIndex.get(toFind); + } + + // Update the hashmap with the current cumulative sum. + // If it's already in the hashmap, increment its count by 1. + // If it's not in the hashmap, add it with a count of 1. + sumIndex.put(currentSum, sumIndex.getOrDefault(currentSum, 0) + 1); + } + + // Return the final result, which represents the total number of subarrays with a sum of 'k'. + return result; +} diff --git a/Sliding Window/subaray_sum_equals_k.js b/Sliding Window/subaray_sum_equals_k.js new file mode 100644 index 00000000..8f3d19e6 --- /dev/null +++ b/Sliding Window/subaray_sum_equals_k.js @@ -0,0 +1,88 @@ +/* + Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. + + A subarray is a contiguous non-empty sequence of elements within an array. + + + + Example 1: + + Input: nums = [1,1,1], k = 2 + Output: 2 + Example 2: + + Input: nums = [1,2,3], k = 3 + Output: 2 + + + ### Explanation: + + The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. + It uses a hashmap to efficiently keep track of cumulative sums and their counts. + + Here's the code's key logic: + + 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. + + 2. It initializes variables `result` and `currentSum`. + + 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). + + 4. It iterates through the elements of the 'nums' array. + + 5. For each element, it adds the element's value to `currentSum`. + + 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. + + 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. + + 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, + it adds it with a count of 1. + + 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. + + ### Time Complexity: + + The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' + array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). + + ### Space Complexity: + + The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, + which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, + each with a count of 1. + + In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. + */ +function subarraySum(nums, k) { + // Create a map to store cumulative sums as keys and their counts as values. + const sumIndex = new Map(); + // Initialize the result to 0. + let result = 0; + // Initialize a variable to track the current cumulative sum. + let currentSum = 0; + // Initialize the map with a key-value pair representing the sum of an empty subarray (0) and its count (1). + sumIndex.set(0, 1); + + // Iterate through the elements of the 'nums' array. + for (let i = 0; i < nums.length; i++) { + // Add the current element to the cumulative sum. + currentSum += nums[i]; + // Calculate the value to find in the map by subtracting 'k' from the current cumulative sum. + const toFind = currentSum - k; + + // Check if the map contains the value 'toFind'. + if (sumIndex.has(toFind)) { + // If found, add the count of subarrays that sum to 'toFind' to the 'result'. + result += sumIndex.get(toFind); + } + + // Update the map with the current cumulative sum. + // If it's already in the map, increment its count by 1. + // If it's not in the map, add it with a count of 1. + sumIndex.set(currentSum, (sumIndex.get(currentSum) || 0) + 1); + } + + // Return the final result, which represents the total number of subarrays with a sum of 'k'. + return result; +} diff --git a/Sliding Window/subaray_sum_equals_k.py b/Sliding Window/subaray_sum_equals_k.py new file mode 100644 index 00000000..0eaa7da3 --- /dev/null +++ b/Sliding Window/subaray_sum_equals_k.py @@ -0,0 +1,85 @@ +''' +Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. + + A subarray is a contiguous non-empty sequence of elements within an array. + + + + Example 1: + + Input: nums = [1,1,1], k = 2 + Output: 2 + Example 2: + + Input: nums = [1,2,3], k = 3 + Output: 2 + + + ### Explanation: + + The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. + It uses a hashmap to efficiently keep track of cumulative sums and their counts. + + Here's the code's key logic: + + 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. + + 2. It initializes variables `result` and `currentSum`. + + 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). + + 4. It iterates through the elements of the 'nums' array. + + 5. For each element, it adds the element's value to `currentSum`. + + 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. + + 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. + + 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, + it adds it with a count of 1. + + 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. + + ### Time Complexity: + + The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' + array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). + + ### Space Complexity: + + The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, + which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, + each with a count of 1. + + In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. + +''' +def subarraySum(nums, k): + # Create a dictionary to store cumulative sums as keys and their counts as values. + sum_index = {0: 1} + # Initialize the result to 0. + result = 0 + # Initialize a variable to track the current cumulative sum. + current_sum = 0 + + # Iterate through the elements of the 'nums' list. + for num in nums: + # Add the current element to the cumulative sum. + current_sum += num + # Calculate the value to find in the dictionary by subtracting 'k' from the current cumulative sum. + to_find = current_sum - k + + # Check if the dictionary contains the value 'to_find'. + if to_find in sum_index: + # If found, add the count of subarrays that sum to 'to_find' to the 'result'. + result += sum_index[to_find] + + # Update the dictionary with the current cumulative sum. + # If it's already in the dictionary, increment its count by 1. + # If it's not in the dictionary, add it with a count of 1. + sum_index[current_sum] = sum_index.get(current_sum, 0) + 1 + + # Return the final result, which represents the total number of subarrays with a sum of 'k'. + return result + diff --git a/Sliding Window/subarray_product_less_than_k,js b/Sliding Window/subarray_product_less_than_k,js new file mode 100644 index 00000000..de4b2474 --- /dev/null +++ b/Sliding Window/subarray_product_less_than_k,js @@ -0,0 +1,50 @@ +/** + Given an array of integers nums and an integer k, + return the number of contiguous subarrays where the product of all the elements in the subarray is + strictly less than k. + + The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number + of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window + approach to efficiently compute this count. + + Time Complexity: + + The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the + sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. + Space Complexity: + + The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). + Therefore, the space complexity is O(1), which means it is independent of the size of the input array. + */ +class Solution { + numSubarrayProductLessThanK(nums, k) { + let startWindow = 0; // The left end of the sliding window + let product = 1; // Initialize product to 1 to accumulate the product + let count = 0; // Count of subarrays with a product less than k + + // Iterate through the array using a sliding window approach + for (let endWindow = 0; endWindow < nums.length; endWindow++) { + // Multiply the current element to the product + product *= nums[endWindow]; + + // Shrink the window by moving the start pointer as long as the product is greater than or equal to k + while (startWindow <= endWindow && product >= k) { + // Divide the product by the element at the start of the window + product /= nums[startWindow]; + // Move the start of the window to the right + startWindow++; + } + + // Update the count with the number of valid subarrays within the current window + count += endWindow - startWindow + 1; + } + + // Return the count, which represents the number of subarrays with a product less than k + return count; + } +} + +// Example usage +const solution = new Solution(); +const result = solution.numSubarrayProductLessThanK([10, 5, 2, 6], 100); +console.log(result); // Output: 8 diff --git a/Sliding Window/subarray_product_less_than_k.cpp b/Sliding Window/subarray_product_less_than_k.cpp new file mode 100644 index 00000000..6855d9d9 --- /dev/null +++ b/Sliding Window/subarray_product_less_than_k.cpp @@ -0,0 +1,49 @@ +/** + Given an array of integers nums and an integer k, + return the number of contiguous subarrays where the product of all the elements in the subarray is + strictly less than k. + + The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number + of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window + approach to efficiently compute this count. + + Time Complexity: + + The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the + sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. + Space Complexity: + + The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). + Therefore, the space complexity is O(1), which means it is independent of the size of the input array. + */ + +#include + +class Solution { +public: + int numSubarrayProductLessThanK(std::vector& nums, int k) { + int startWindow = 0; // The left end of the sliding window + int product = 1; // Initialize product to 1 to accumulate the product + int count = 0; // Count of subarrays with a product less than k + + // Iterate through the array using a sliding window approach + for (int endWindow = 0; endWindow < nums.size(); endWindow++) { + // Multiply the current element to the product + product *= nums[endWindow]; + + // Shrink the window by moving the start pointer as long as the product is greater than or equal to k + while (startWindow <= endWindow && product >= k) { + // Divide the product by the element at the start of the window + product /= nums[startWindow]; + // Move the start of the window to the right + startWindow++; + } + + // Update the count with the number of valid subarrays within the current window + count += endWindow - startWindow + 1; + } + + // Return the count, which represents the number of subarrays with a product less than k + return count; + } +}; diff --git a/Sliding Window/subarray_product_less_than_k.go b/Sliding Window/subarray_product_less_than_k.go new file mode 100644 index 00000000..a3c9cab7 --- /dev/null +++ b/Sliding Window/subarray_product_less_than_k.go @@ -0,0 +1,44 @@ +/** + Given an array of integers nums and an integer k, + return the number of contiguous subarrays where the product of all the elements in the subarray is + strictly less than k. + + The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number + of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window + approach to efficiently compute this count. + + Time Complexity: + + The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the + sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. + Space Complexity: + + The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). + Therefore, the space complexity is O(1), which means it is independent of the size of the input array. +*/ + + func numSubarrayProductLessThanK(nums []int, k int) int { + startWindow := 0 // The left end of the sliding window + product := 1 // Initialize product to 1 to accumulate the product + count := 0 // Count of subarrays with a product less than k + + // Iterate through the array using a sliding window approach + for endWindow := 0; endWindow < len(nums); endWindow++ { + // Multiply the current element to the product + product *= nums[endWindow] + + // Shrink the window by moving the start pointer as long as the product is greater than or equal to k + for startWindow <= endWindow && product >= k { + // Divide the product by the element at the start of the window + product /= nums[startWindow] + // Move the start of the window to the right + startWindow++ + } + + // Update the count with the number of valid subarrays within the current window + count += endWindow - startWindow + 1 + } + + // Return the count, which represents the number of subarrays with a product less than k + return count +} diff --git a/Sliding Window/subarray_product_less_than_k.java b/Sliding Window/subarray_product_less_than_k.java new file mode 100644 index 00000000..6edd74fe --- /dev/null +++ b/Sliding Window/subarray_product_less_than_k.java @@ -0,0 +1,46 @@ +/** + Given an array of integers nums and an integer k, + return the number of contiguous subarrays where the product of all the elements in the subarray is + strictly less than k. + + The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number + of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window + approach to efficiently compute this count. + + Time Complexity: + + The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the + sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. + Space Complexity: + + The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). + Therefore, the space complexity is O(1), which means it is independent of the size of the input array. + */ +class Solution { + public int numSubarrayProductLessThanK(int[] nums, int k) { + // Initialize pointers and a count variable + int startWindow = 0; // The left end of the sliding window + int product = 1; // Initialize product to 1 to accumulate the product + int count = 0; // Count of subarrays with a product less than k + + // Iterate through the array using a sliding window approach + for (int endWindow = 0; endWindow < nums.length; endWindow++) { + // Multiply the current element to the product + product *= nums[endWindow]; + + // Shrink the window by moving the start pointer as long as the product is greater than or equal to k + while (startWindow <= endWindow && product >= k) { + // Divide the product by the element at the start of the window + product /= nums[startWindow]; + // Move the start of the window to the right + startWindow++; + } + + // Update the count with the number of valid subarrays within the current window + count += endWindow - startWindow + 1; + } + + // Return the count, which represents the number of subarrays with a product less than k + return count; + } +} diff --git a/Sliding Window/subarray_product_less_than_k.py b/Sliding Window/subarray_product_less_than_k.py new file mode 100644 index 00000000..e4821ca3 --- /dev/null +++ b/Sliding Window/subarray_product_less_than_k.py @@ -0,0 +1,43 @@ +''' + Given an array of integers nums and an integer k, + return the number of contiguous subarrays where the product of all the elements in the subarray is + strictly less than k. + + The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number + of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window + approach to efficiently compute this count. + + Time Complexity: + + The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the + sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. + Space Complexity: + + The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). + Therefore, the space complexity is O(1), which means it is independent of the size of the input array. +''' +from typing import List + +class Solution: + def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: + startWindow = 0 # The left end of the sliding window + product = 1 # Initialize product to 1 to accumulate the product + count = 0 # Count of subarrays with a product less than k + + # Iterate through the list using a sliding window approach + for endWindow in range(len(nums)): + # Multiply the current element to the product + product *= nums[endWindow] + + # Shrink the window by moving the start pointer as long as the product is greater than or equal to k + while startWindow <= endWindow and product >= k: + # Divide the product by the element at the start of the window + product /= nums[startWindow] + # Move the start of the window to the right + startWindow += 1 + + # Update the count with the number of valid subarrays within the current window + count += endWindow - startWindow + 1 + + # Return the count, which represents the number of subarrays with a product less than k + return count diff --git a/Stacks/Stack_with_max_API.cpp b/Stacks/Stack_with_max_API.cpp new file mode 100644 index 00000000..f12761c8 --- /dev/null +++ b/Stacks/Stack_with_max_API.cpp @@ -0,0 +1,44 @@ +/* +Time Complexity of all Operations is O(1) +Space Complexity is O(n) +In this implementation, we use two stacks: s is the main stack that holds the elements of the stack, and max_s is a secondary stack that holds the maximum values in s. When a new value is pushed onto the stack, we compare it with the current maximum value in max_s and add it to max_s if it's greater than or equal to the current maximum. +*/ +#include +using namespace std; + +class MaxStack { +private: + stack s; // Main stack that holds the elements of the stack + stack max_s; // Secondary stack that holds the maximum values in s + +public: + void push(int val) { + s.push(val); // Push the value onto the main stack + if (max_s.empty() || val >= max_s.top()) { + // If max_s is empty or the new value is greater than or equal to the current maximum, + // push the value onto max_s + max_s.push(val); + } + } + + void pop() { + if (s.top() == max_s.top()) { + // If the top value of s is equal to the top value of max_s, it means that the top value + // of s is the current maximum, so we need to pop it from max_s as well + max_s.pop(); + } + s.pop(); // Pop the value from the main stack + } + + int top() { + return s.top(); // Return the top value of the main stack + } + + int max() { + return max_s.top(); // Return the top value of max_s, which is the maximum value in the stack + } + + bool empty() { + return s.empty(); // Check if the main stack is empty + } +}; diff --git a/Stacks/Stacks_using_queues.py b/Stacks/Stacks_using_queues.py new file mode 100644 index 00000000..ca6cfdc1 --- /dev/null +++ b/Stacks/Stacks_using_queues.py @@ -0,0 +1,81 @@ +''' Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Implement Stack using Queues in Python +Issue Number : #261 +Problem statement : + +Explanation of the below python code : + +__init__(self) initializes the two queues (q1 and q2) in the constructor. + +push(self, x: int) adds a new element x to the top of the stack. It first adds the element to q2, and then moves all the elements from q1 to q2 using a while loop. Finally, it swaps q1 and q2 so that q1 always contains the elements in the stack. + +pop(self) -> int removes and returns the element at the top of the stack, which is the first element in q1. + +top(self) -> int returns the element at the top of the stack, which is also the first element in q1. + +empty(self) -> bool returns True if the stack is empty (i.e., q1 is empty), and False otherwise. + +The deque data structure from the collections module is used to implement the two queues, since it provides efficient O(1) operations for adding and removing elements from both ends of the queue. + +Overall, this implementation is quite simple and efficient, since the push and pop operations take O(1) time complexity on average, while the top and empty operations take constant time. + +-------------------------------------------------------------------------//Python code begins here---------------------------------------------------------------------------- +''' + +from collections import deque + +class Stack: + def __init__(self): #The Stack class is defined with a constructor that initializes a deque object as the Stack's underlying data structure. + self.queue = deque() + + def push(self, x: int) -> None: + self.queue.append(x) + # Move all existing elements to the end of the queue #The push method takes an integer value as input and appends it to the end of the deque. It then moves all the existing elements in the deque to the end, effectively simulating the addition of the new element to the top of the Stack. + for _ in range(len(self.queue) - 1): + self.queue.append(self.queue.popleft()) + + def pop(self) -> int: + return self.queue.popleft() #The pop method removes and returns the element at the top of the Stack, which is the first element in the deque. + + def top(self) -> int: #The top method returns the element at the top of the Stack without removing it, which is the first element in the deque. + return self.queue[0] + + def empty(self) -> bool: #The empty method returns True if the Stack is empty (i.e., the deque has length 0), and False otherwise. + return len(self.queue) == 0 + + +stack = Stack() +while True: + print("1. Push") + print("2. Pop") + print("3. Top") + print("4. Empty") + print("5. Quit") + choice = int(input("Enter your choice: ")) + if choice == 1: + x = int(input("Enter the element to push: ")) + stack.push(x) + print("Element pushed to stack") + elif choice == 2: + if stack.empty(): '''The code above shows an example usage of the Stack class, where the user can interact with the Stack through a command-line interface.The user is prompted with a menu of options to choose from, and their choice is read in as an integer value using the input() functiom''' Depending on the user's choice, the appropriate method is called on the stack object and the results are printed to the console. + print("Stack is empty") + else: + x = stack.pop() + print("Popped element:", x) + elif choice == 3: + if stack.empty(): + print("Stack is empty") + else: + x = stack.top() + print("Top element:", x) + elif choice == 4: + if stack.empty(): + print("Stack is empty") + else: + print("Stack is not empty") + elif choice == 5: + break + else: + print("Invalid choice") diff --git a/Stacks/next_greater_element.c++ b/Stacks/next_greater_element.c++ new file mode 100644 index 00000000..730fc941 --- /dev/null +++ b/Stacks/next_greater_element.c++ @@ -0,0 +1,72 @@ +/* + + Write a function that takes in an array of integers and returns a new array containing, at each index, the next + element in the input array that's greater than the element at that index in the input array. + + Sample Input:[2, 5, -3, -4, 6, 7, 2] + Output: [5, 6, 6, 6, 7, -1, 5] + Explanation: + + The given code snippet implements the Next Greater Element algorithm. Here's how it works: + + 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length + where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. + + 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. + + 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a + way that maintains a decreasing order of values from the input array. + + 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a + circular manner by using the modulo operator `%` on the index. + + 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. + If it is, it means the current element is the next greater element for the element at the top of the stack. + + 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the + `result` slice is updated with the current element from the input array. + + 7. After updating the `result` slice, the top index is removed from the stack. + + 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in + the future. + + 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater + elements for each element in the input array, or -1 if there is no greater element. + + 10. The `result` slice is returned as the output. + + The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. + By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. + + Note that this implementation assumes the availability of the built-in append function to modify slices in Go. + + The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. + This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. + + The space complexity of the function is O(n) as well. This is because the function creates two additional + slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. +*/ + +#include +#include + +std::vector nextGreaterElement(std::vector& array) { + std::vector result(array.size(), -1); // Initialize result vector with -1 for all elements + std::stack stack; // Stack to store indices of elements + + for (int idx = 0; idx < array.size() * 2; idx++) { + int circularIdx = idx % array.size(); // Obtain the circular index of the current element + + while (!stack.empty() && array[circularIdx] > array[stack.top()]) { + // While the stack is not empty and the current element is greater than the element at the top of the stack + int top = stack.top(); // Get the index from the top of the stack + stack.pop(); // Pop the index from the stack + result[top] = array[circularIdx]; // Update the result for the popped index + } + + stack.push(circularIdx); // Push the current index to the stack + } + + return result; +} diff --git a/Stacks/next_greater_element.go b/Stacks/next_greater_element.go new file mode 100644 index 00000000..d1af1558 --- /dev/null +++ b/Stacks/next_greater_element.go @@ -0,0 +1,85 @@ +/* + + Write a function that takes in an array of integers and returns a new array containing, at each index, the next + element in the input array that's greater than the element at that index in the input array. + + Sample Input:[2, 5, -3, -4, 6, 7, 2] + Output: [5, 6, 6, 6, 7, -1, 5] + Explanation: + + The given code snippet implements the Next Greater Element algorithm. Here's how it works: + + 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length + where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. + + 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. + + 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a + way that maintains a decreasing order of values from the input array. + + 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a + circular manner by using the modulo operator `%` on the index. + + 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. + If it is, it means the current element is the next greater element for the element at the top of the stack. + + 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the + `result` slice is updated with the current element from the input array. + + 7. After updating the `result` slice, the top index is removed from the stack. + + 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in + the future. + + 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater + elements for each element in the input array, or -1 if there is no greater element. + + 10. The `result` slice is returned as the output. + + The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. + By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. + + Note that this implementation assumes the availability of the built-in append function to modify slices in Go. + + The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. + This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. + + The space complexity of the function is O(n) as well. This is because the function creates two additional + slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. +*/ +package main + +// NextGreaterElement finds the next greater element for each element in the input array. +func NextGreaterElement(array []int) []int { + // Create a result slice to store the next greater elements. + result := make([]int, 0) + // Initialize the result slice with -1 values indicating no greater elements initially. + for range array { + result = append(result, -1) + } + + // Create a stack to keep track of indices of elements from the input array. + stack := make([]int, 0) + + // Perform two passes over the input array, considering elements in a circular manner. + for idx := 0; idx < 2*len(array); idx++ { + // Calculate the circular index by using the modulo operator. + circularIdx := idx % len(array) + + // Check if the current element is greater than the element at the top of the stack. + for len(stack) > 0 && array[circularIdx] > array[stack[len(stack)-1]] { + // Retrieve the top index from the stack. + top := stack[len(stack)-1] + // Update the corresponding element in the result slice with the current element. + result[top] = array[circularIdx] + // Remove the top index from the stack. + stack = stack[:len(stack)-1] + } + + // Push the current circular index onto the stack. + stack = append(stack, circularIdx) + } + + // Return the result slice containing the next greater elements. + return result +} diff --git a/Stacks/next_greater_element.java b/Stacks/next_greater_element.java new file mode 100644 index 00000000..5efc1adb --- /dev/null +++ b/Stacks/next_greater_element.java @@ -0,0 +1,71 @@ +/* + + Write a function that takes in an array of integers and returns a new array containing, at each index, the next + element in the input array that's greater than the element at that index in the input array. + + Sample Input:[2, 5, -3, -4, 6, 7, 2] + Output: [5, 6, 6, 6, 7, -1, 5] + Explanation: + + The given code snippet implements the Next Greater Element algorithm. Here's how it works: + + 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length + where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. + + 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. + + 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a + way that maintains a decreasing order of values from the input array. + + 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a + circular manner by using the modulo operator `%` on the index. + + 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. + If it is, it means the current element is the next greater element for the element at the top of the stack. + + 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the + `result` slice is updated with the current element from the input array. + + 7. After updating the `result` slice, the top index is removed from the stack. + + 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in + the future. + + 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater + elements for each element in the input array, or -1 if there is no greater element. + + 10. The `result` slice is returned as the output. + + The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. + By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. + + The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. + This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. + + The space complexity of the function is O(n) as well. This is because the function creates two additional + slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. +*/ + +import java.util.*; + +public class NextGreaterElement { + public int[] nextGreaterElement(int[] array) { + int[] result = new int[array.length]; + Arrays.fill(result, -1); // Initialize result array with -1 for all elements + Stack stack = new Stack<>(); // Stack to store indices of elements + + for (int idx = 0; idx < array.length * 2; idx++) { + int circularIdx = idx % array.length; // Obtain the circular index of the current element + + while (!stack.isEmpty() && array[circularIdx] > array[stack.peek()]) { + // While the stack is not empty and the current element is greater than the element at the top of the stack + int top = stack.pop(); // Pop the index from the stack + result[top] = array[circularIdx]; // Update the result for the popped index + } + + stack.push(circularIdx); // Push the current index to the stack + } + + return result; + } +} diff --git a/Stacks/next_greater_element.js b/Stacks/next_greater_element.js new file mode 100644 index 00000000..e2b2a6e9 --- /dev/null +++ b/Stacks/next_greater_element.js @@ -0,0 +1,69 @@ +/* + + Write a function that takes in an array of integers and returns a new array containing, at each index, the next + element in the input array that's greater than the element at that index in the input array. + + Sample Input:[2, 5, -3, -4, 6, 7, 2] + Output: [5, 6, 6, 6, 7, -1, 5] + Explanation: + + The given code snippet implements the Next Greater Element algorithm. Here's how it works: + + 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length + where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. + + 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. + + 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a + way that maintains a decreasing order of values from the input array. + + 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a + circular manner by using the modulo operator `%` on the index. + + 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. + If it is, it means the current element is the next greater element for the element at the top of the stack. + + 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the + `result` slice is updated with the current element from the input array. + + 7. After updating the `result` slice, the top index is removed from the stack. + + 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in + the future. + + 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater + elements for each element in the input array, or -1 if there is no greater element. + + 10. The `result` slice is returned as the output. + + The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. + By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. + + + The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. + This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. + + The space complexity of the function is O(n) as well. This is because the function creates two additional + slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. +*/ +function nextGreaterElement(array) { + const result = new Array(array.length).fill(-1); // Initialize result array with -1 for all elements + const stack = []; // Stack to store indices of elements + + for (let idx = 0; idx < array.length * 2; idx++) { + const circularIdx = idx % array.length; // Obtain the circular index of the current element + + while ( + stack.length && + array[circularIdx] > array[stack[stack.length - 1]] + ) { + // While the stack is not empty and the current element is greater than the element at the top of the stack + const top = stack.pop(); // Pop the index from the stack + result[top] = array[circularIdx]; // Update the result for the popped index + } + + stack.push(circularIdx); // Push the current index to the stack + } + + return result; +} diff --git a/Stacks/next_greater_element.py b/Stacks/next_greater_element.py new file mode 100644 index 00000000..34464d3f --- /dev/null +++ b/Stacks/next_greater_element.py @@ -0,0 +1,63 @@ +''' + + Write a function that takes in an array of integers and returns a new array containing, at each index, the next + element in the input array that's greater than the element at that index in the input array. + + Sample Input:[2, 5, -3, -4, 6, 7, 2] + Output: [5, 6, 6, 6, 7, -1, 5] + Explanation: + + The given code snippet implements the Next Greater Element algorithm. Here's how it works: + + 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length + where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. + + 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. + + 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a + way that maintains a decreasing order of values from the input array. + + 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a + circular manner by using the modulo operator `%` on the index. + + 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. + If it is, it means the current element is the next greater element for the element at the top of the stack. + + 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the + `result` slice is updated with the current element from the input array. + + 7. After updating the `result` slice, the top index is removed from the stack. + + 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in + the future. + + 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater + elements for each element in the input array, or -1 if there is no greater element. + + 10. The `result` slice is returned as the output. + + The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. + By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. + + The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. + This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. + + The space complexity of the function is O(n) as well. This is because the function creates two additional + slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. +''' + +def next_greater_element(array): + result = [-1] * len(array) # Initialize result array with -1 for all elements + stack = [] # Stack to store indices of elements + + for idx in range(len(array) * 2): + circular_idx = idx % len(array) # Obtain the circular index of the current element + + while stack and array[circular_idx] > array[stack[-1]]: + # While the stack is not empty and the current element is greater than the element at the top of the stack + top = stack.pop() # Pop the index from the stack + result[top] = array[circular_idx] # Update the result for the popped index + + stack.append(circular_idx) # Push the current index to the stack + + return result diff --git a/Stacks/queue using stack.go b/Stacks/queue using stack.go new file mode 100644 index 00000000..d279209f --- /dev/null +++ b/Stacks/queue using stack.go @@ -0,0 +1,60 @@ +// Queue using stack +package main + +import "fmt" + +// Queue represents a queue data structure using two stacks. +type Queue struct { + enqueueStack []int // Stack for enqueue operations + dequeueStack []int // Stack for dequeue operations +} + +// Enqueue adds an element to the back of the queue. +func (q *Queue) Enqueue(value int) { + q.enqueueStack = append(q.enqueueStack, value) +} + +// Dequeue removes and returns the front element of the queue. +func (q *Queue) Dequeue() int { + // If the dequeue stack is empty, transfer elements from the enqueue stack + if len(q.dequeueStack) == 0 { + for len(q.enqueueStack) > 0 { + // Pop an element from the enqueue stack and push it onto the dequeue stack + element := q.enqueueStack[len(q.enqueueStack)-1] + q.enqueueStack = q.enqueueStack[:len(q.enqueueStack)-1] + q.dequeueStack = append(q.dequeueStack, element) + } + } + + // If the dequeue stack is still empty, the queue is empty + if len(q.dequeueStack) == 0 { + panic("Queue is empty") + } + + // Pop and return the front element from the dequeue stack + element := q.dequeueStack[len(q.dequeueStack)-1] + q.dequeueStack = q.dequeueStack[:len(q.dequeueStack)-1] + return element +} + +func main() { + queue := Queue{} + + // Enqueue elements + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + // Dequeue elements + fmt.Println(queue.Dequeue()) // Output: 1 + fmt.Println(queue.Dequeue()) // Output: 2 + + // Enqueue more elements + queue.Enqueue(4) + queue.Enqueue(5) + + // Dequeue the remaining elements + fmt.Println(queue.Dequeue()) // Output: 3 + fmt.Println(queue.Dequeue()) // Output: 4 + fmt.Println(queue.Dequeue()) // Output: 5 +} diff --git a/Stacks/queue using stack.java b/Stacks/queue using stack.java new file mode 100644 index 00000000..c6d8fe66 --- /dev/null +++ b/Stacks/queue using stack.java @@ -0,0 +1,112 @@ +/* +Issue:#253 +Author:maneesha +Date:13/06/2023 +##Assignee:Mani1881 +//About: + +Implement Queue using Stacks in java +//Input: + +>>Implement a first in first out (FIFO) queue using only two stacks. +>>The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty). +Implement the MyQueue class: +~void push(int x) Pushes element x to the back of the queue. +~int pop() Removes the element from the front of the queue and returns it. +~int peek() Returns the element at the front of the queue. +~boolean empty() Returns true if the queue is empty, false . +//Example: + +>> Sample Input: +["MyQueue", "push", "push", "peek", "pop", "empty"] +[[], [1], [2], [], [], []] +>>Sample Output: +[null, null, null, 1, 1, false] +Explanation +MyQueue myQueue = new MyQueue(); +myQueue.push(1); // queue is: [1](here we are not returning push element so in output we got null) +myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue) +myQueue.peek(); // return 1 +myQueue.pop(); // return 1, queue is [2] +myQueue.empty(); // return false +//Complexity: + +>>Time Complexity +push:O(n) +pop:O(1) +peek:O(1) +empty:O(1) +>>Space Complexity:O(n) +//Example: + +//Explanation + +>>The provided code implements a queue using two stacks (`s1` and `s2`). +>>The `push()` method transfers elements from `s1` to `s2`, adds the new element to `s1`, and then transfers elements back to `s1`. +>>The `pop()` method removes and returns the top element of `s1`. +>>The `peek()` method returns the top element of `s1` without removing it. +>>The `empty()` method checks if `s1` is empty. +>>This implementation maintains FIFO order. +*/ +class MyQueue +{ +Stacks1=new Stack(); +Stacks2=new Stack(); + + public void push(int x) + { + while(!s1.isEmpty()) + { + s2.push(s1.pop()); + } + s1.push(x); + while(!s2.empty()) + { + s1.push(s2.pop()); + } + } + + public int pop() + { + return s1.pop(); + } + + public int peek() + { + return s1.peek(); + } + + public boolean empty() + { + return s1.isEmpty(); + } + public static void main(String[] args) + { + MyQueue queue = new MyQueue(); + + // Pushing elements into the queue + queue.push(1); + queue.push(2); + queue.push(3); + + // Checking the front element of the queue + System.out.println("Front element: " + queue.peek()); // Output: Front element: 1 + + // Removing elements from the queue + System.out.println("Removed element: " + queue.pop()); // Output: Removed element: 1 + System.out.println("Removed element: " + queue.pop()); // Output: Removed element: 2 + + // Checking if the queue is empty + System.out.println("Is the queue empty? " + queue.empty()); // Output: Is the queue empty? false + + // Pushing another element into the queue + queue.push(4); + + // Removing the remaining elements from the queue + System.out.println("Removed element: " + queue.pop()); // Output: Removed element: 3 + System.out.println("Removed element: " + queue.pop()); // Output: Removed element: 4 + + // Checking if the queue is empty after removing all elements + System.out.println("Is the queue empty? " + queue.empty()); // Output: Is the queue empty? true +} +} diff --git a/Stacks/queue using stack.js b/Stacks/queue using stack.js new file mode 100644 index 00000000..1473391a --- /dev/null +++ b/Stacks/queue using stack.js @@ -0,0 +1,45 @@ +// Queue using stack +class Queue { + constructor() { + this.enqueueStack = []; // Stack for enqueue operations + this.dequeueStack = []; // Stack for dequeue operations + } + + enqueue(value) { + // Enqueue: Add an element to the back of the queue. + this.enqueueStack.push(value); + } + + dequeue() { + // Dequeue: Remove and return the front element of the queue. + // If the dequeue stack is empty, transfer elements from the enqueue stack + if (this.dequeueStack.length === 0) { + while (this.enqueueStack.length > 0) { + // Pop an element from the enqueue stack and push it onto the dequeue stack + const element = this.enqueueStack.pop(); + this.dequeueStack.push(element); + } + } + + // If the dequeue stack is still empty, the queue is empty + if (this.dequeueStack.length === 0) { + throw new Error("Queue is empty"); + } + + // Pop and return the front element from the dequeue stack + return this.dequeueStack.pop(); + } +} + +// Example usage +const queue = new Queue(); +queue.enqueue(1); +queue.enqueue(2); +queue.enqueue(3); +console.log(queue.dequeue()); // Output: 1 +console.log(queue.dequeue()); // Output: 2 +queue.enqueue(4); +queue.enqueue(5); +console.log(queue.dequeue()); // Output: 3 +console.log(queue.dequeue()); // Output: 4 +console.log(queue.dequeue()); // Output: 5 diff --git a/Stacks/queue using stack.py b/Stacks/queue using stack.py new file mode 100644 index 00000000..802cacfd --- /dev/null +++ b/Stacks/queue using stack.py @@ -0,0 +1,38 @@ +# Queue using stack +class Queue: + def __init__(self): + self.enqueue_stack = [] # Stack for enqueue operations + self.dequeue_stack = [] # Stack for dequeue operations + + def enqueue(self, value): + """Enqueue: Add an element to the back of the queue.""" + self.enqueue_stack.append(value) + + def dequeue(self): + """Dequeue: Remove and return the front element of the queue.""" + # If the dequeue stack is empty, transfer elements from the enqueue stack + if not self.dequeue_stack: + while self.enqueue_stack: + # Pop an element from the enqueue stack and push it onto the dequeue stack + element = self.enqueue_stack.pop() + self.dequeue_stack.append(element) + + # If the dequeue stack is still empty, the queue is empty + if not self.dequeue_stack: + raise IndexError("Queue is empty") + + # Pop and return the front element from the dequeue stack + return self.dequeue_stack.pop() + +# Example usage +queue = Queue() +queue.enqueue(1) +queue.enqueue(2) +queue.enqueue(3) +print(queue.dequeue()) # Output: 1 +print(queue.dequeue()) # Output: 2 +queue.enqueue(4) +queue.enqueue(5) +print(queue.dequeue()) # Output: 3 +print(queue.dequeue()) # Output: 4 +print(queue.dequeue()) # Output: 5 diff --git a/Stacks/queue_using_stacks.cpp b/Stacks/queue_using_stacks.cpp new file mode 100644 index 00000000..b3335e03 --- /dev/null +++ b/Stacks/queue_using_stacks.cpp @@ -0,0 +1,79 @@ +/* The basic difference in stack and queues is the order they are filled in practically.In Stacks, +they are inserted on top of previous element while In Queues they are inserted behind the last element. +The push() function is what we have to look out for. First we will empty out our primary stack s1 to another stack s2. +Then We push the element to be inserted x in s1(or s2). +Then just empty out the stack s2 back into s1. But the element x would be at the last place just like in a Queue. +The other Operations are the same as we have already implemented as a queue.*/ + + +/*Complexity Analysis: + +Time Complexity: +Push operation : O(1). +Same as pop operation in stack. +Pop operation : O(N). +The difference from above method is that in this method element is returned and all elements are restored back in a single call. +Auxiliary Space: O(N). +Use of stack for storing values. +*/ + + +#include +using namespace std; + +struct Queue { + stack s; + + // Enqueue an item to the queue + void enQueue(int x) + { + s.push(x); + } + + // Dequeue an item from the queue + int deQueue() + { + if (s.empty()) { + cout << "Q is empty"; + exit(0); + } + + // pop an item from the stack + int x = s.top(); + s.pop(); + + // if stack becomes empty, return + // the popped item + if (s.empty()) + return x; + + // recursive call + int item = deQueue(); + + // push popped item back to the stack + s.push(x); + + // return the result of deQueue() call + return item; + } +}; + +// Driver code +int main() +{ + Queue q; + q.enQueue(10); + q.enQueue(20); + q.enQueue(30); + + cout << q.deQueue() << '\n'; + cout << q.deQueue() << '\n'; + cout << q.deQueue() << '\n'; + + return 0; +} + + + + + diff --git a/Stacks/stack.cpp b/Stacks/stack.cpp new file mode 100644 index 00000000..ed8dafea --- /dev/null +++ b/Stacks/stack.cpp @@ -0,0 +1,77 @@ +// Implemenmtation of stack data structure +/* + This implementation creates a class Stack that has a private integer top to keep track of the index + of the topmost element in the stack, and an integer array arr to store the elements of the stack. + The push, pop, peek, and isEmpty methods are used to add elements to, remove elements from, view + the topmost element in, and check if the stack is empty, respectively. The main function creates + an instance of Stack and demonstrates the use of the stack by pushing some elements onto it, + printing the topmost element, popping an element from the stack, and checking if the stack is empty. +*/ +#include +using namespace std; + +const int MAX_SIZE = 100; // Maximum size of stack + +class Stack { +private: + int top; // Index of topmost element in stack + int arr[MAX_SIZE]; // Array to store elements of stack +public: + Stack() { + top = -1; // Initialize top index to -1, indicating empty stack + } + + // Pushes an element onto the top of the stack + void push(int x) { + if (top == MAX_SIZE - 1) { // Check if stack is full + cout << "Stack Overflow\n"; + return; + } + arr[++top] = x; // Increment top index and add element to array + } + + // Removes and returns the topmost element from the stack + int pop() { + if (top == -1) { // Check if stack is empty + cout << "Stack Underflow\n"; + return -1; + } + int val = arr[top]; // Store topmost element in a variable + top--; // Decrement top index to remove element from stack + return val; // Return topmost element + } + + // Returns the topmost element in the stack without removing it + int peek() { + if (top == -1) { // Check if stack is empty + cout << "Stack Underflow\n"; + return -1; + } + return arr[top]; // Return topmost element + } + + // Returns true if stack is empty, false otherwise + bool isEmpty() { + return top == -1; + } +}; + +int main() { + Stack s; + + // Push some elements onto the stack + s.push(5); + s.push(10); + s.push(15); + + // Print the topmost element without removing it + cout << "Top element: " << s.peek() << endl; + + // Pop an element from the stack and print it + cout << "Popped element: " << s.pop() << endl; + + // Check if stack is empty + cout << "Is stack empty? " << (s.isEmpty() ? "Yes" : "No") << endl; + + return 0; +} diff --git a/Stacks/stack.java b/Stacks/stack.java new file mode 100644 index 00000000..05afc9f1 --- /dev/null +++ b/Stacks/stack.java @@ -0,0 +1,70 @@ +// Implemenmtation of stack data structure +/* + This implementation uses a generic type T to allow the stack to store elements of any type. + The stack is implemented using an array and has methods for pushing, popping, peeking, checking + if the stack is empty, and getting the size of the stack. The comments explain each step of the + implementation. +*/ +public class Stack { + // array to store elements of the stack + private T[] elements; + // top index of the stack + private int top; + + // constructor to initialize the stack with a given capacity + public Stack(int capacity) { + // create a new array of type T with the given capacity + elements = (T[]) new Object[capacity]; + // initialize top index to -1 + top = -1; + } + + // push an element onto the top of the stack + public void push(T element) { + // check if the stack is full + if (top == elements.length - 1) { + // if the stack is full, throw an exception + throw new RuntimeException("Stack overflow"); + } + // increment top index + top++; + // insert the element at the top index + elements[top] = element; + } + + // pop the top element from the stack and return it + public T pop() { + // check if the stack is empty + if (top == -1) { + // if the stack is empty, throw an exception + throw new RuntimeException("Stack underflow"); + } + // get the top element + T element = elements[top]; + // decrement top index + top--; + // return the top element + return element; + } + + // return the top element of the stack without removing it + public T peek() { + // check if the stack is empty + if (top == -1) { + // if the stack is empty, throw an exception + throw new RuntimeException("Stack underflow"); + } + // return the top element + return elements[top]; + } + + // return true if the stack is empty, false otherwise + public boolean isEmpty() { + return top == -1; + } + + // return the size of the stack + public int size() { + return top + 1; + } +} diff --git a/Stacks/stack.js b/Stacks/stack.js new file mode 100644 index 00000000..30239f1f --- /dev/null +++ b/Stacks/stack.js @@ -0,0 +1,74 @@ +// Implemenmtation of stack data structure +/* + We define a class called Stack with an empty array items to store the stack elements. + The class has four methods: + push(item): Adds an item to the top of the stack by using the push method of the Array object. + pop(): Removes and returns the item at the top of the stack using the pop method of the Array object. + peek(): Returns the item at the top of the stack without removing it using the slice method of the Array object. + isEmpty(): Returns true if the stack is empty, false otherwise by checking the length of the items array. + + This implementation uses the built-in Array object of JavaScript to implement the stack data structure. + The push, pop, and slice methods are used to manipulate the items array to add, remove, and access the + top item of the stack, respectively. The isEmpty method simply checks if the items array is empty or not. +*/ +class Stack { + constructor() { + this.items = []; // initializing an empty array + } + + // push function to add an element to the stack + push(element) { + this.items.push(element); + } + + // pop function to remove the topmost element from the stack + pop() { + // checking if the stack is empty + if (this.items.length === 0) return "Underflow"; + return this.items.pop(); + } + + // peek function to get the topmost element of the stack without removing it + peek() { + // checking if the stack is empty + if (this.items.length === 0) return "No elements in Stack"; + return this.items[this.items.length - 1]; + } + + // isEmpty function to check if the stack is empty + isEmpty() { + return this.items.length === 0; + } + + // printStack function to print the entire stack + printStack() { + let str = ""; + for (let i = 0; i < this.items.length; i++) { + str += this.items[i] + " "; + } + return str; + } +} + +// creating an instance of the Stack class +let stack = new Stack(); + +// adding elements to the stack +stack.push(10); +stack.push(20); +stack.push(30); + +// printing the stack +console.log(stack.printStack()); // Output: 10 20 30 + +// removing the topmost element from the stack +stack.pop(); + +// printing the stack +console.log(stack.printStack()); // Output: 10 20 + +// checking the topmost element of the stack +console.log(stack.peek()); // Output: 20 + +// checking if the stack is empty +console.log(stack.isEmpty()); // Output: false diff --git a/Stacks/stack.py b/Stacks/stack.py new file mode 100644 index 00000000..5b23b597 --- /dev/null +++ b/Stacks/stack.py @@ -0,0 +1,76 @@ +# Implemenmtation of stack data structure +''' + In this implementation, the Stack class contains the following methods: + + __init__: Constructor function to initialize an empty stack. + is_empty: Check if stack is empty. + push: Push an item onto the stack. + pop: Remove and return the top item from the stack. + peek: Return the top item from the stack without removing it. + size: Return the number of items in the stack. + Each method includes a docstring that explains what it does, along with any arguments and return values. +''' +class Stack: + """ + Stack class implementation using list in Python + """ + def __init__(self): + """ + Constructor function to initialize an empty stack + """ + self.items = [] + + def is_empty(self): + """ + Check if stack is empty + + Returns: + bool: True if stack is empty, False otherwise + """ + return len(self.items) == 0 + + def push(self, item): + """ + Push an item onto the stack + + Args: + item: Item to be pushed onto the stack + """ + self.items.append(item) + + def pop(self): + """ + Remove and return the top item from the stack + + Returns: + item: Top item from the stack + + Raises: + IndexError: If stack is empty + """ + if self.is_empty(): + raise IndexError("Stack is empty") + return self.items.pop() + + def peek(self): + """ + Return the top item from the stack without removing it + + Returns: + item: Top item from the stack + + Raises: + IndexError: If stack is empty + """ + if self.is_empty(): + raise IndexError("Stack is empty") + return self.items[-1] + + def size(self): + """ + Return the number of items in the stack + + Returns: + int: Number of items in the stack + """ + return len(self.items) diff --git a/Stacks/stack_using_queue.cpp b/Stacks/stack_using_queue.cpp new file mode 100644 index 00000000..f58ed22b --- /dev/null +++ b/Stacks/stack_using_queue.cpp @@ -0,0 +1,87 @@ +// stack using queues +#include +#include + +class StackUsingQueues { +public: + StackUsingQueues() {} + + // Push an element onto the stack. + void push(int x) { + // Add the element to the primary queue. + primaryQueue.push(x); + } + + // Remove and return the top element of the stack. + int pop() { + if (isEmpty()) { + throw std::runtime_error("Stack is empty"); + } + + // Move elements from the primary queue to the temporary queue except the last one. + while (primaryQueue.size() > 1) { + tempQueue.push(primaryQueue.front()); + primaryQueue.pop(); + } + + // Get the last element from the primary queue (top of the stack). + int topElement = primaryQueue.front(); + primaryQueue.pop(); + + // Swap the primary and temporary queues. + primaryQueue = tempQueue; + while (!tempQueue.empty()) { + tempQueue.pop(); + } + + return topElement; + } + + // Return the top element of the stack without removing it. + int top() { + if (isEmpty()) { + throw std::runtime_error("Stack is empty"); + } + + int topElement = pop(); + + // Add the top element back to the stack. + push(topElement); + + return topElement; + } + + // Check if the stack is empty. + bool isEmpty() { + return primaryQueue.empty(); + } + +private: + std::queue primaryQueue; + std::queue tempQueue; +}; + +int main() { + StackUsingQueues stack; + + // Push elements onto the stack. + stack.push(1); + stack.push(2); + stack.push(3); + + // Pop elements from the stack. + std::cout << stack.pop() << std::endl; // Output: 3 + std::cout << stack.pop() << std::endl; // Output: 2 + + // Push more elements. + stack.push(4); + stack.push(5); + + // Peek at the top element. + std::cout << stack.top() << std::endl; // Output: 5 + + // Check if the stack is empty. + std::cout << stack.isEmpty() << std::endl; // Output: 0 (false) + + return 0; +} diff --git a/Stacks/stack_using_queue.go b/Stacks/stack_using_queue.go new file mode 100644 index 00000000..13676064 --- /dev/null +++ b/Stacks/stack_using_queue.go @@ -0,0 +1,84 @@ +// stack using queues +package main + +import ( + "container/list" + "fmt" +) + +// Stack represents a stack data structure using two queues. +type Stack struct { + queue1 *list.List // Primary queue + queue2 *list.List // Temporary queue for operations +} + +// Constructor creates a new stack. +func NewStack() *Stack { + return &Stack{ + queue1: list.New(), + queue2: list.New(), + } +} + +// Push adds an element to the top of the stack. +func (s *Stack) Push(value int) { + // Add the element to the primary queue + s.queue1.PushBack(value) +} + +// Pop removes and returns the top element of the stack. +func (s *Stack) Pop() int { + // Move elements from the primary queue to the temporary queue except the last one + for s.queue1.Len() > 1 { + element := s.queue1.Front() + s.queue1.Remove(element) + s.queue2.PushBack(element.Value) + } + + // Get the last element from the primary queue (top of the stack) + topElement := s.queue1.Front().Value + + // Swap the primary and temporary queues + s.queue1, s.queue2 = s.queue2, s.queue1 + + return topElement.(int) +} + +// Top returns the top element of the stack without removing it. +func (s *Stack) Top() int { + // Similar to Pop, but don't remove the last element + topElement := s.Pop() + + // Add the top element back to the stack + s.Push(topElement) + + return topElement +} + +// IsEmpty checks if the stack is empty. +func (s *Stack) IsEmpty() bool { + return s.queue1.Len() == 0 +} + +func main() { + stack := NewStack() + + // Push elements onto the stack + stack.Push(1) + stack.Push(2) + stack.Push(3) + + // Pop elements from the stack + fmt.Println(stack.Pop()) // Output: 3 + fmt.Println(stack.Pop()) // Output: 2 + + // Push more elements + stack.Push(4) + stack.Push(5) + + // Peek at the top element + fmt.Println(stack.Top()) // Output: 5 + + // Check if the stack is empty + fmt.Println(stack.IsEmpty()) // Output: false +} diff --git a/Stacks/stack_using_queue.java b/Stacks/stack_using_queue.java new file mode 100644 index 00000000..20acafad --- /dev/null +++ b/Stacks/stack_using_queue.java @@ -0,0 +1,79 @@ +// stack using queues +import java.util.LinkedList; +import java.util.Queue; + +public class StackUsingQueues { + private Queue primaryQueue; + private Queue tempQueue; + + public StackUsingQueues() { + primaryQueue = new LinkedList<>(); + tempQueue = new LinkedList<>(); + } + + public void push(int value) { + // Add the element to the primary queue + primaryQueue.offer(value); + } + + public int pop() { + if (isEmpty()) { + throw new IllegalStateException("Stack is empty."); + } + + // Move elements from the primary queue to the temporary queue except the last one + while (primaryQueue.size() > 1) { + tempQueue.offer(primaryQueue.poll()); + } + + // Get the last element from the primary queue (top of the stack) + int topElement = primaryQueue.poll(); + + // Swap the primary and temporary queues + Queue swap = primaryQueue; + primaryQueue = tempQueue; + tempQueue = swap; + + return topElement; + } + + public int top() { + if (isEmpty()) { + throw new IllegalStateException("Stack is empty."); + } + + int topElement = pop(); + + // Add the top element back to the stack + push(topElement); + + return topElement; + } + + public boolean isEmpty() { + return primaryQueue.isEmpty(); + } + + public static void main(String[] args) { + StackUsingQueues stack = new StackUsingQueues(); + + // Push elements onto the stack + stack.push(1); + stack.push(2); + stack.push(3); + + // Pop elements from the stack + System.out.println(stack.pop()); // Output: 3 + System.out.println(stack.pop()); // Output: 2 + + // Push more elements + stack.push(4); + stack.push(5); + + // Peek at the top element + System.out.println(stack.top()); // Output: 5 + + // Check if the stack is empty + System.out.println(stack.isEmpty()); // Output: false + } +} diff --git a/Stacks/stack_using_queue.js b/Stacks/stack_using_queue.js new file mode 100644 index 00000000..cfcf0f64 --- /dev/null +++ b/Stacks/stack_using_queue.js @@ -0,0 +1,73 @@ +// Stack using queues +class StackUsingQueues { + constructor() { + this.primaryQueue = []; + this.tempQueue = []; + } + + // Push an element onto the stack. + push(x) { + // Add the element to the primary queue. + this.primaryQueue.push(x); + } + + // Remove and return the top element of the stack. + pop() { + if (this.isEmpty()) { + throw new Error("Stack is empty"); + } + + // Move elements from the primary queue to the temporary queue except the last one. + while (this.primaryQueue.length > 1) { + this.tempQueue.push(this.primaryQueue.shift()); + } + + // Get the last element from the primary queue (top of the stack). + const topElement = this.primaryQueue.shift(); + + // Swap the primary and temporary queues. + [this.primaryQueue, this.tempQueue] = [this.tempQueue, this.primaryQueue]; + + return topElement; + } + + // Return the top element of the stack without removing it. + top() { + if (this.isEmpty()) { + throw new Error("Stack is empty"); + } + + const topElement = this.pop(); + + // Add the top element back to the stack. + this.push(topElement); + + return topElement; + } + + // Check if the stack is empty. + isEmpty() { + return this.primaryQueue.length === 0; + } +} + +const stack = new StackUsingQueues(); + +// Push elements onto the stack. +stack.push(1); +stack.push(2); +stack.push(3); + +// Pop elements from the stack. +console.log(stack.pop()); // Output: 3 +console.log(stack.pop()); // Output: 2 + +// Push more elements. +stack.push(4); +stack.push(5); + +// Peek at the top element. +console.log(stack.top()); // Output: 5 + +// Check if the stack is empty. +console.log(stack.isEmpty()); // Output: false diff --git a/Stacks/stacks_API.cpp b/Stacks/stacks_API.cpp new file mode 100644 index 00000000..59f1730e --- /dev/null +++ b/Stacks/stacks_API.cpp @@ -0,0 +1,67 @@ +/* + In this implementation, we use a std::priority_queue named heap to simulate the behavior of a stack. The push operation inserts elements into the heap with a unique sequence number generated for each element. The pop operation removes the top element from the heap. The top operation returns the top element without removing it. The empty operation checks if the heap is empty. + +The time complexity of the stack operations implemented using a heap is as follows: + +push: O(log n), where n is the number of elements in the stack. +pop: O(log n), where n is the number of elements in the stack. +top: O(1) +empty: O(1) +The push and pop operations have a time complexity of O(log n) because the std::priority_queue internally maintains the heap property, ensuring that the highest priority (in this case, the highest sequence number) element is always at the top + */ + + + +#include +#include + +class Stack { +private: + std::priority_queue heap; // Using max heap to simulate stack behavior + int sequenceNumber; // To keep track of the order of elements + +public: + Stack() : sequenceNumber(0) {} + + void push(int value) { + heap.push(std::make_pair(sequenceNumber++, value)); + } + + int pop() { + if (heap.empty()) { + throw std::runtime_error("Stack is empty"); + } + + int value = heap.top().second; + heap.pop(); + return value; + } + + int top() const { + if (heap.empty()) { + throw std::runtime_error("Stack is empty"); + } + + return heap.top().second; + } + + bool empty() const { + return heap.empty(); + } +}; + +int main() { + Stack stack; + + stack.push(5); + stack.push(10); + stack.push(3); + + std::cout << stack.top() << std::endl; // Output: 3 + + stack.pop(); + + std::cout << stack.top() << std::endl; // Output: 10 + + return 0; +} diff --git a/Stacks/stacks_using_queues.java b/Stacks/stacks_using_queues.java new file mode 100644 index 00000000..46c3d18d --- /dev/null +++ b/Stacks/stacks_using_queues.java @@ -0,0 +1,100 @@ +/* Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Implement Stack using Queues in Java +Issue Number : #259 +Problem statement : + +Explanation of the below Java code : + +In the above code, the main method demonstrates the usage of the stack by providing the user with a menu to select the operation they want to perform - push, pop, or quit. The user can input the value to push, and the program prints the pushed or popped value accordingly. + +The program uses the Scanner class to take user input from the console. The push method adds the element to the first queue (q1) and updates the top variable. The pop method transfers all elements except the last one from q1 to q2, removes the last element from q1 and swaps the two queues. The peek method returns the top variable, and the isEmpty method checks if q1 is empty. + +When the user selects the push operation, the program prompts the user to enter the value to push, and it calls the push method to push the value onto the stack. When the user selects the pop operation, the program checks if the stack is empty and prints an error message if it is. Otherwise, it calls the pop method to pop the value from the stack and prints it. + +The program continues to prompt the user for input until the user selects the quit operation + + +*/ +-------------------------------------------------------------------------------------------------------//Java code begins here-------------------------------------------------------------------------------------------------------------------------- + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Scanner; + +public class StackUsingQueues { +// Create two queues as instance variables +private Queue q1 = new LinkedList<>(); +private Queue q2 = new LinkedList<>(); + +// Create a variable to hold the top element of the stack +private int top; + +// Method to push an element onto the stack +public void push(int x) { + q1.add(x); // Add the element to the first queue + top = x; // Update the top variable to hold the new element +} + +// Method to pop an element from the stack +public int pop() { + // Move all elements except the last one from the first queue to the second queue + while (q1.size() > 1) { + top = q1.remove(); + q2.add(top); + } + // Remove the last element from the first queue, which is the element to be popped + int popValue = q1.remove(); + // Swap the queues so that the second queue becomes the first queue for the next operation + Queue temp = q1; + q1 = q2; + q2 = temp; + return popValue; // Return the popped element +} + +// Method to peek at the top element of the stack +public int peek() { + return top; // Return the top element of the stack +} + +// Method to check if the stack is empty +public boolean isEmpty() { + return q1.isEmpty(); // Return whether the first queue is empty +} + +// Main method to run the program +public static void main(String[] args) { + // Create a new instance of the StackUsingQueues class + StackUsingQueues stack = new StackUsingQueues(); + // Create a new Scanner object to read input from the user + Scanner scanner = new Scanner(System.in); + // Create a loop to continuously prompt the user for input + while (true) { + System.out.println("Select operation -\n" + + "1. Push\n" + + "2. Pop\n" + + "3. Quit"); + // Read the user's choice + int choice = scanner.nextInt(); + // Check the user's choice and perform the corresponding operation + if (choice == 1) { + System.out.print("Enter value to push: "); + int val = scanner.nextInt(); + stack.push(val); + System.out.println("Pushed value: " + val); + } else if (choice == 2) { + if (stack.isEmpty()) { + System.out.println("Stack is empty."); + } else { + int val = stack.pop(); + System.out.println("Popped value: " + val); + } + } else if (choice == 3) { + break; + } else { + System.out.println("Invalid choice. Please try again."); + } + } +} +} diff --git a/Stacks/stacks_with_queues.cpp b/Stacks/stacks_with_queues.cpp new file mode 100644 index 00000000..6931018a --- /dev/null +++ b/Stacks/stacks_with_queues.cpp @@ -0,0 +1,66 @@ +/* +This implementation maintains two queues, queue1 and queue2, where queue1 always holds the elements in the stack. When pushing a new element, it is added to queue2, and then all the elements from queue1 are moved to queue2, making the newly added element the front/top of the stack. Finally, the names of the two queues are swapped to maintain the order. + +The pop() function removes and returns the front/top element of queue1, while the top() function returns the front/top element without removing it. Both functions check if queue1 is empty and throw an exception if the stack is empty. + +The empty() function checks if queue1 is empty and returns true if it is, indicating an empty stack. + +Time complexity + +push(x): O(n) +pop(): O(1) +top(): O(1) +empty(): O(1) +*/ + + + +#include + +class MyStack { +private: + std::queue queue1; + std::queue queue2; + +public: + MyStack() { + + } + + void push(int x) { + // Add the new element to queue2 + queue2.push(x); + + // Move all elements from queue1 to queue2 + while (!queue1.empty()) { + queue2.push(queue1.front()); + queue1.pop(); + } + + // Swap the names of the two queues + std::swap(queue1, queue2); + } + + int pop() { + if (queue1.empty()) { + throw std::runtime_error("Stack is empty"); + } + + int topElement = queue1.front(); + queue1.pop(); + return topElement; + } + + int top() { + if (queue1.empty()) { + throw std::runtime_error("Stack is empty"); + } + + return queue1.front(); + } + + bool empty() { + return queue1.empty(); + } +}; + diff --git a/Stacks/stacks_with_queues.py b/Stacks/stacks_with_queues.py new file mode 100644 index 00000000..9cc3a655 --- /dev/null +++ b/Stacks/stacks_with_queues.py @@ -0,0 +1,84 @@ +''' +__init__() - This method initializes the stack by creating two queues. Since no operations are performed on the queues in this method, the time complexity is O(1). + +push(item) - This method adds an item to the top of the stack. Initially, it adds the item to an empty queue, which takes O(1) time. Then, it moves all the elements from the other queue to the empty queue, which takes O(n) time, where n is the number of elements in the stack. Finally, it swaps the names of the two queues, which is a constant time operation. Therefore, the overall time complexity of push(item) is O(n). + +pop() - This method removes and returns the item at the top of the stack. It checks if the stack is empty, which takes O(1) time. Then, it removes and returns the front element of the non-empty queue, which is a constant time operation. Therefore, the overall time complexity of pop() is O(1). + +top() - This method returns the item at the top of the stack without removing it. It checks if the stack is empty, which takes O(1) time. Then, it retrieves the front element of the non-empty queue, which is a constant time operation. Therefore, the overall time complexity of top() is O(1). + +is_empty() - This method checks if the stack is empty by checking if the queue is empty, which takes O(1) time. Therefore, the time complexity of is_empty() is O(1). + +size() - This method returns the number of items in the stack by returning the size of the queue, which is a constant time operation. Therefore, the time complexity of size() is O(1). + +the time complexity of the operations in the Stack class implemented using queues is as follows: + +push(item): O(n) +pop(): O(1) +top(): O(1) +is_empty(): O(1) +size(): O(1) + +''' + +from queue import Queue + +class Stack: + """ + Stack implementation using queues. + """ + + def __init__(self): + """ + Initialize an empty stack. + """ + self.queue1 = Queue() + self.queue2 = Queue() + + def push(self, item): + """ + Add an item to the top of the stack. + """ + # Add the item to the empty queue + self.queue1.put(item) + + # Move all the elements from the other queue to the empty queue + while not self.queue2.empty(): + self.queue1.put(self.queue2.get()) + + # Swap the names of the two queues + self.queue1, self.queue2 = self.queue2, self.queue1 + + def pop(self): + """ + Remove and return the item at the top of the stack. + """ + if self.queue2.empty(): + raise IndexError("Stack is empty") + + # Remove and return the front element of the non-empty queue + return self.queue2.get() + + def top(self): + """ + Return the item at the top of the stack without removing it. + """ + if self.queue2.empty(): + raise IndexError("Stack is empty") + + # Get the front element of the non-empty queue + return self.queue2.queue[0] + + def is_empty(self): + """ + Check if the stack is empty. + """ + return self.queue2.empty() + + def size(self): + """ + Return the number of items in the stack. + """ + return self.queue2.qsize() + + diff --git a/Stacks/valid_parentheses.cpp b/Stacks/valid_parentheses.cpp new file mode 100644 index 00000000..8b125bb4 --- /dev/null +++ b/Stacks/valid_parentheses.cpp @@ -0,0 +1,53 @@ +/* +Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. + +An input string is valid if: + +Open brackets must be closed by the same type of brackets. +Open brackets must be closed in the correct order. +Every close bracket has a corresponding open bracket of the same type. + + +Example 1: +Input: s = "()" +Output: true + +Example 2: +Input: s = "()[]{}" +Output: true + +Example 3: +Input: s = "(]" +Output: false + +Constraints: + +1 <= s.length <= 104 +s consists of parentheses only '()[]{}'. +*/ + +class Solution { +public: + bool Are_pair(char opening, char closing){ + if (opening == '(' && closing == ')') return true; + else if (opening == '[' && closing == ']') return true; + else if (opening == '{' && closing == '}') return true; + return false; + } + bool isValid(string s) { + stack Sta; + int len = s.size(); + for(int i = 0; i < len; i++){ + if(s[i] == '(' || s[i] == '[' || s[i] == '{'){ + Sta.push(s[i]); + } + else if(s[i] == ')' || s[i] == ']' || s[i] == '}'){ + if(Sta.empty() || !Are_pair(Sta.top(), s[i])) + return false; + else + Sta.pop(); + } + } + return Sta.empty() ? true : false; + } +}; \ No newline at end of file diff --git a/Strings/Dp_plaindrome.py b/Strings/Dp_plaindrome.py new file mode 100644 index 00000000..ce9b8fda --- /dev/null +++ b/Strings/Dp_plaindrome.py @@ -0,0 +1,17 @@ +def find_min_insertion_steps(s): + n = len(s) + dp = [[0] * n for _ in range(n)] + + for length in range(2, n + 1): + for i in range(n - length + 1): + j = i + length - 1 + if s[i] == s[j]: + dp[i][j] = dp[i + 1][j - 1] + else: + dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1 + + return dp[0][n - 1] + +# Test the function +string = "abcd" +print("Minimum Insertion Steps:", find_min_insertion_steps(string)) diff --git a/Strings/KMP.go b/Strings/KMP.go new file mode 100644 index 00000000..527b6c76 --- /dev/null +++ b/Strings/KMP.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" +) + +// computeLPSArray computes the Longest Prefix Suffix (LPS) array for the given pattern. +func computeLPSArray(pattern string) []int { + length := len(pattern) + lps := make([]int, length) + lps[0] = 0 + j := 0 + + for i := 1; i < length; { + if pattern[i] == pattern[j] { + j++ + lps[i] = j + i++ + } else { + if j != 0 { + j = lps[j-1] + } else { + lps[i] = 0 + i++ + } + } + } + + return lps +} + +// KMPSearch searches for occurrences of the pattern within the given text using the KMP algorithm. +func KMPSearch(text, pattern string) []int { + n := len(text) + m := len(pattern) + lps := computeLPSArray(pattern) + + i := 0 // Index for text + j := 0 // Index for pattern + + positions := make([]int, 0) + + for i < n { + if pattern[j] == text[i] { + i++ + j++ + } + + if j == m { + positions = append(positions, i-j) + j = lps[j-1] + } else if i < n && pattern[j] != text[i] { + if j != 0 { + j = lps[j-1] + } else { + i++ + } + } + } + + return positions +} + +func main() { + text := "ABABDABACDABABCABAB" + pattern := "ABABCABAB" + positions := KMPSearch(text, pattern) + + if len(positions) == 0 { + fmt.Println("Pattern not found in the text.") + } else { + fmt.Printf("Pattern found at positions: %v\n", positions) + } +} diff --git a/Strings/Longest_palindromic_substring.py b/Strings/Longest_palindromic_substring.py new file mode 100644 index 00000000..f30871df --- /dev/null +++ b/Strings/Longest_palindromic_substring.py @@ -0,0 +1,43 @@ +''' + Given a string s, return the longest palindromic substring in s. + + Example 1: + Input: s = "babad" + Output: "bab" + Explanation: "aba" is also a valid answer. + + Example 2: + Input: s = "cbbd" + Output: "bb" + + Constraints: + 1 <= s.length <= 1000 + s consist of only digits and English letters. +''' +class Solution: + #Instead of checking if a value is palindrome from the end, we can consider each index to be the center + #Then, Check if the left and right indexed values to it are different. + #The edge case would to find even lengthed palindrome like the second example + #Reference: https://www.youtube.com/watch?v=XYQecbcd6_c + + def __init__(self): + self.max_length = -1 + self.max_palindrome = "" + def CheckLeftRight(self, s, index, value): + low, high = index, index + if(value == "even"): + high = index + 1 + while(low >= 0 and high < len(s) and s[low] == s[high]): + if(high - low + 1 > self.max_length): + self.max_length = high - low + 1 + self.max_palindrome = s[low:high + 1] + low -= 1 + high += 1 + + def longestPalindrome(self, s: str) -> str: + for i in range(len(s)): + #odd values + Solution.CheckLeftRight(self, s, i, "odd") + #Even values + Solution.CheckLeftRight(self, s, i, "even") + return self.max_palindrome \ No newline at end of file diff --git a/Strings/MaxConcatenatedstr.java b/Strings/MaxConcatenatedstr.java new file mode 100644 index 00000000..4aef02bd --- /dev/null +++ b/Strings/MaxConcatenatedstr.java @@ -0,0 +1,47 @@ +class Solution { +public: + // i: arr[i] we are currently looking at + // mask: Bitmask that represents all the characters that have been added to the current string + // If 0-th bit in mask is set, it means that we have added "a" in the current string + int solve(vector &arr, int i, int mask) { + int n = arr.size(); + + if (i >= n) + return 0; + + // Skip concatenating arr[i] + int curRes = solve(arr, i+1, mask) + + // Mask to keep track of the characters that are present in arr[i] + int curMask = 0; + + // Check whether any character in arr[i] is present in current string, i.e. + // Check whether (arr[i]-'a')-th bit is set in mask + // If any existing character's bit is set, it means that we cannot concatenate arr[i] + // to the given string and so return curRes only which contains the result of skipping arr[i] + // Also, use curMask to maintain the characters in arr[i] that have been seen. + // It is possible that arr[i] itself has duplicate characters in which case, we will not be able to concatenate arr[i] + // So check whether (c-'a')-th bit is set in curMask and after that set the (c-'a')-th bit in curMask + for (char &c: arr[i]) { + if (mask & (1 << (c - 'a'))) + return curRes; + + if (curMask & (1 << (c - 'a'))) + return curRes; + + curMask |= (1 << (c - 'a')); + } + + // All the bits that were set in curMask will be now set in mask, + // in order to add all characters of arr[i] to the current string + mask |= curMask; + + // We make a call to i+1 with the updated mask and arr[i]'s length being added + curRes = max(curRes, (int) arr[i].length() + solve(arr, i+1, mask)); + return curRes; + } + + int maxLength(vector& arr) { + return solve(arr, 0, 0); + } +}; diff --git a/Strings/Min_palindrome.js b/Strings/Min_palindrome.js new file mode 100644 index 00000000..543bd29d --- /dev/null +++ b/Strings/Min_palindrome.js @@ -0,0 +1,31 @@ +function minInsertionStepsToPalindrome(str) { + const n = str.length; + // Create a 2D array to store the minimum steps needed to make substrings palindrome + const dp = Array.from({ length: n }, () => Array(n).fill(0)); + + // Base case: single characters are palindromes, so dp[i][i] = 0 + for (let i = 0; i < n; i++) { + dp[i][i] = 0; + } + + // Fill the dp table in bottom-up manner + for (let len = 2; len <= n; len++) { + for (let i = 0; i < n - len + 1; i++) { + const j = i + len - 1; + if (str[i] === str[j]) { + // If the characters at the ends are the same, no additional insertion is needed + dp[i][j] = dp[i + 1][j - 1]; + } else { + // Otherwise, choose the minimum between inserting character at i or j + dp[i][j] = 1 + Math.min(dp[i + 1][j], dp[i][j - 1]); + } + } + } + + return dp[0][n - 1]; +} + +// Example usage: +const str = "abcd"; +const minInsertions = minInsertionStepsToPalindrome(str); +console.log("Minimum insertion steps:", minInsertions); // Output: 3 (abcd -> dabcbad) diff --git a/Strings/Valid_palindrome.py b/Strings/Valid_palindrome.py new file mode 100644 index 00000000..c6b8fbcc --- /dev/null +++ b/Strings/Valid_palindrome.py @@ -0,0 +1,32 @@ +'''Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem : Find Number of Good Pairs in Go +Issue Number : #648 +Problem statement : Check whether a given string is a Valid Palindrome in Python + +Explanation of the below cpp code : + +The is_valid_palindrome() function is the same as the one in the previous example, which checks whether a given string is a valid palindrome or not. + +The input() function is used to take input from the user. +It displays a prompt message "Enter a string:" and waits for the user to enter a value followed by pressing the Enter key. The entered value is stored in the variable s. + +The if statement checks if the input string s is a valid palindrome or not using the is_valid_palindrome() function. +If it is, it prints the message "The string is a valid palindrome", otherwise it prints "The string is not a valid palindrome". + +The time complexity of the given code is O(n) + +''' +------------------------------------------------------------------------------------------------------//Python code begins here---------------------------------------------------------------------------------------------------------------------------------- + +def is_valid_palindrome(s): + s = ''.join(filter(str.isalnum, s)).lower() + return s == s[::-1] + +s = input("Enter a string: ") #Taking input from user + +if is_valid_palindrome(s): + print("The string is a valid palindrome") #True case +else: + print("The string is not a valid palindrome") #False case diff --git a/Strings/case_specific_sorting_of_strings.cpp b/Strings/case_specific_sorting_of_strings.cpp new file mode 100644 index 00000000..939b6bfe --- /dev/null +++ b/Strings/case_specific_sorting_of_strings.cpp @@ -0,0 +1,65 @@ +case-specific sorting of strings (GFG) +Given a string S consisting of only uppercase and lowercase characters. The task is to sort uppercase and lowercase letters separately such that if the ith place in the original string had an Uppercase character then it should not have a lowercase character after being sorted and vice versa. + +Example 1: + +Input: +N = 12 +S = defRTSersUXI +Output: deeIRSfrsTUX +Explanation: Sorted form of given string +with the same case of character as that +in original string is deeIRSfrsTUX +Example 2: + +Input: +N = 6 +S = srbDKi +Output: birDKs +Explanation: Sorted form of given string +with the same case of character +result in output as birDKs. +Your Task: +You only need to complete the function caseSort, that takes a string str and the length of the string n and returns sorted string. + +Explanation: In this problem, we create three strings based on the given string.One for storing the capital letters named as caps string (string caps=""), second for small letters (string small="") and finally the third string (string ans="") which would store the final result. +We will sort the strings in order to maintain alphabetical order. +Then we traverse the given string using x (pointer) , i and j pointers for the caps and small strings. j pointer is used to traverse the string caps while i used to traverse the string small. +x pointer is used to iterate through the given string as a whole and for each capital letter found we will insert the the letter from string caps into the string ans. +Similarly, for each small letter, we will insert it from the string small into the ans string. +Finally we return the ans string. + +CODE: + +class Solution +{ + public: + //Function to perform case-specific sorting of strings. + string caseSort(string str, int n) + { + string small="", caps = "",ans=""; + for(auto x:str){ + if(isupper(x)){ + caps+=x; + } + else{ + small+=x; + } + } + sort(caps.begin(),caps.end()); + sort(small.begin(),small.end()); + int i=0,j=0; + for(auto x:str){ + if(isupper(x)){ + ans+=caps[j]; + j++; + } + else{ + ans+=small[i]; + i++; + } + } + return ans; + // your code here + } +}; diff --git a/Strings/check panagram.java b/Strings/check panagram.java new file mode 100644 index 00000000..cea8a9ec --- /dev/null +++ b/Strings/check panagram.java @@ -0,0 +1,93 @@ +/* + Check whether a string is a Panagram or not. + + Note that a string is a Panagram if it contains all the character of the alphabets ignoring the case of the alphabets + + For example, str = “Abcdefghijklmnopqrstuvwxyz” + Output: Yes + Explanation: The given string contains all the letters from a to z (ignoring case). + + str = "AbhayChetri" + Output: No + Explaination: The given string doesn't contain all the letters from a to z. + +Approach:- 1.Convert each letter of the string to the lower or upper case. + 2.Create a frequency array to mark the frequency of each letter from a to z. + 3.Then, traverse the frequency array and if there is any letter that is not present in the given string then print "No", otherwise print "Yes". + + Time Complexity: O(N) + Auxiliary Space: O(26) + +*/ + +class Abhay { + + static int size = 26; + + // Function to check if ch is a letter + static boolean isLetter(char ch) + { + if (!Character.isLetter(ch)) + return false; + + return true; + } + + // Function to check if a string + // contains all the letters from + // a to z + static boolean allLetter(String str, + int len) + { + // Convert the given string + // into lowercase + str = str.toLowerCase(); + + // Create a frequency array to + // mark the present letters + boolean[] present = new boolean[size]; + + // Traverse for each character + // of the string + for (int i = 0; i < len; i++) { + + // If the current character + // is a letter + if (isLetter(str.charAt(i))) { + + // Mark current letter as present + int letter = str.charAt(i) - 'a'; + present[letter] = true; + } + } + + // Traverse for every letter + // from a to z + for (int i = 0; i < size; i++) { + + // If the current character + // is not present in string + // then return false, + // otherwise return true + if (!present[i]) + return false; + } + return true; + } + + // Driver Code + public static void main(String args[]) + { + + // Given string str + String str = "Abcdefghijklmnopqrstuvwxyz"; + int len = str.length(); + + // Function Call + if (allLetter(str, len)) + System.out.println("Yes"); + else + System.out.println("No"); + } +} + diff --git a/Strings/check_anagrams.java b/Strings/check_anagrams.java new file mode 100644 index 00000000..3cea21f7 --- /dev/null +++ b/Strings/check_anagrams.java @@ -0,0 +1,84 @@ +/** + * You are given two lowercase strings A and B each of length N. Return 1 if they are anagrams to each other and 0 if not. + * + * Note : Two strings A and B are called anagrams to each other if A can be formed after rearranging the letters of B. + * + * + * Problem Constraints + * 1 <= N <= 105 + * A and B are lowercase strings + * + * + * Input Format + * Both arguments A and B are a string. + * + * + * Output Format + * Return 1 if they are anagrams and 0 if not + * + * + * Example Input + * Input 1: + * A = "cat" + * B = "bat" + * Input 2: + * A = "secure" + * B = "rescue" + * + * + * Example Output + * Output 1: + * 0 + * Output 2: + * 1 + * + * + * Example Explanation + * For Input 1: + * The words cannot be rearranged to form the same word. So, they are not anagrams. + * For Input 2: + * They are an anagram. + */ +package Strings; + +public class CheckAnagrams { + public static void main(String[] args) { + String string1 = "secure"; + String string2 = "rescue"; + + int res = solve(string1, string2); + System.out.println(res); + } + + public static int solve(String string1, String string2) { + + // O(N) time | O(1) space + + int[] freq1 = new int[26]; + int[] freq2 = new int[26]; + // build freq array and store count for each letter + for (int i = 0; i < string1.length(); i++) { + freq1[string1.charAt(i) - 'a']++; + freq2[string2.charAt(i) - 'a']++; + } + // compare if count of each letter + // if mismatch occurs then return 0 + for (int i = 0; i < 26; i++) { + if (freq1[i] != freq2[i]) return 0; + } + + return 1; + +// char[] char1 = string1.toCharArray(); +// Arrays.sort(char1); +// +// char[] char2 = string2.toCharArray(); +// Arrays.sort(char2); +// +// String str1 = new String(char1); +// String str2 = new String(char2); +// +// if (str1.equals(str2)) return 1; +// return 0; + } +} diff --git a/Strings/check_palindrome.cpp b/Strings/check_palindrome.cpp new file mode 100644 index 00000000..93f344c6 --- /dev/null +++ b/Strings/check_palindrome.cpp @@ -0,0 +1,39 @@ +#include +#include + +using namespace std; + +// 2 pointer approach to find if a number is palindrome or not +// Time complexity O(n), Space complexity O(1). + +int palindrome_checker(string str) // palindrome checker function +{ + int left = 0; + int right = str.length() - 1; // initializing left and right variables + while(left < right) + { + if(str[left] != str[right]) + { + return 0; + } + left = left + 1; // updating left and right variables + right = right - 1; + } + return 1; +} + +int main() +{ + string str; + cin>>str; + int isPal = palindrome_checker(str); // calling palindrome checker function + if(isPal == 1) + { + cout<<"String is palindrome"; + } + else + { + cout<<"String is not palindrome"; + } + return 0; +} \ No newline at end of file diff --git a/Random_Problems/check_permutations.cpp b/Strings/check_permutations.cpp similarity index 58% rename from Random_Problems/check_permutations.cpp rename to Strings/check_permutations.cpp index 05f794f3..6d6b3e6d 100644 --- a/Random_Problems/check_permutations.cpp +++ b/Strings/check_permutations.cpp @@ -1,30 +1,33 @@ -// Check if a string is a permutation of other -#include -using namespace std; -bool check_permutations(string a, string b){ - vector Freq(26, 0); - for(int i = 0; i < a.length(); i++){ - Freq[a[i] - 'a']++; - } - for(int x : Freq) cout << x << ","; - cout << endl; - for(int i = 0; i < b.length(); i++){ - if(Freq[b[i] - 'a'] > 0) - Freq[b[i] - 'a']--; - else - Freq[b[i] - 'a']++; - } - for(int x : Freq) cout << x << ","; - cout << endl; - int res = accumulate(Freq.begin(), Freq.end(), 0); - return res == 0 ? true : false; -} -int main(){ - string a, b; - cin >> a >> b; - if(check_permutations(a, b)) - cout << "TRUE"; - else - cout << "FALSE"; - return 0; +// Check if a string is a permutation of other +// Sample Input: s1 = abba s2 = baba +// Output: true +// Time Complexity O(n) Space complexity O(1) +#include +using namespace std; +bool check_permutations(string a, string b){ + vector Freq(26, 0); // assuming only letters + for(int i = 0; i < a.length(); i++){ + Freq[a[i] - 'a']++; + } + for(int x : Freq) cout << x << ","; + cout << endl; + for(int i = 0; i < b.length(); i++){ + if(Freq[b[i] - 'a'] > 0) // seen a letter + Freq[b[i] - 'a']--; // reduce count + else + Freq[b[i] - 'a']++; // not seen before so increase count + } + for(int x : Freq) cout << x << ","; + cout << endl; + int res = accumulate(Freq.begin(), Freq.end(), 0); // checl of sum of elements in freq is 0 + return res == 0 ? true : false; +} +int main(){ + string a, b; + cin >> a >> b; + if(check_permutations(a, b)) + cout << "TRUE"; + else + cout << "FALSE"; + return 0; } \ No newline at end of file diff --git a/Strings/count_occurances.java b/Strings/count_occurances.java new file mode 100644 index 00000000..7328fa4e --- /dev/null +++ b/Strings/count_occurances.java @@ -0,0 +1,79 @@ +/** + * Find the number of occurrences of bob in string A consisting of lowercase English alphabets. + * + * + * + * Problem Constraints + * 1 <= |A| <= 1000 + * + * + * Input Format + * The first and only argument contains the string A, consisting of lowercase English alphabets. + * + * + * Output Format + * Return an integer containing the answer. + * + * + * Example Input + * Input 1: + * + * "abobc" + * Input 2: + * + * "bobob" + * + * + * Example Output + * Output 1: + * + * 1 + * Output 2: + * + * 2 + * + * + * Example Explanation + * Explanation 1: + * + * The only occurrence is at second position. + * Explanation 2: + * + * Bob occures at first and third position. + */ + +package Strings; + +public class CountOccurrences { + public static void main(String[] args) { + String string = "bobob"; + + int res = solve(string); + System.out.println(res); + } + public static int solve(String string) { + + // O(N) time | O(1) space + int res = 0; + + for (int i = 0; i + 2 < string.length(); i++) { + if (string.charAt(i) == 'b' && + string.charAt(i + 1) == 'o' && + string.charAt(i + 2) == 'b') + ++res; + } + +// for (int i = 0; i < string.length(); i++) { +// char c = string.charAt(i); +// StringBuilder sb = new StringBuilder(); +// sb.append(c); +// for (int j = i + 1; j < string.length(); j++) { +// sb.append(string.charAt(j)); +// if (sb.toString().equals("bob")) { +// res++; +// } +// } +// } + return res; + } +} diff --git a/Strings/group_anagrams.cpp b/Strings/group_anagrams.cpp new file mode 100644 index 00000000..460cbf84 --- /dev/null +++ b/Strings/group_anagrams.cpp @@ -0,0 +1,78 @@ +/* + Group Anagrams + + Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] + Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] + + Explanation: + + The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging + the letters of another word or phrase. + + The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words + as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. + The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. + + The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are + lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the + sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. + + The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort + the byte array. The function then returns a string that contains the sorted byte array. + + O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word + +*/ + +#include +#include +#include +#include + +// Function to sort a word and return the sorted string +std::string sortWord(const std::string& word) { + std::string sortedWord = word; + std::sort(sortedWord.begin(), sortedWord.end()); + return sortedWord; +} + +// Function to group anagrams together +std::vector> GroupAnagrams(const std::vector& words) { + // Create a map where the keys are sorted strings of words and the values are lists of words + // that have the same sorted string. + std::unordered_map> anagrams; + + // Iterate through the words + for (const std::string& word : words) { + // Get the sorted string for the word + std::string sortedWord = sortWord(word); + + // Add the word to the list of words associated with the sorted string in the map + anagrams[sortedWord].push_back(word); + } + + // Create a vector of vectors, where each inner vector contains all the anagrams of a word in the original list + std::vector> result; + for (const auto& pair : anagrams) { + result.push_back(pair.second); + } + + // Return the vector of vectors + return result; +} + +int main() { + // Example usage + std::vector words = { "eat", "tea", "tan", "ate", "nat", "bat" }; + std::vector> groupedAnagrams = GroupAnagrams(words); + + // Print the grouped anagrams + for (const std::vector& group : groupedAnagrams) { + for (const std::string& word : group) { + std::cout << word << " "; + } + std::cout << std::endl; + } + + return 0; +} diff --git a/Strings/group_anagrams.go b/Strings/group_anagrams.go new file mode 100644 index 00000000..bdd3209d --- /dev/null +++ b/Strings/group_anagrams.go @@ -0,0 +1,65 @@ +/* + Group Anagrams + + Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] + Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] + + Explanation: + + The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging + the letters of another word or phrase. + + The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words + as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. + The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. + + The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are + lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the + sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. + + The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort + the byte array. The function then returns a string that contains the sorted byte array. + + O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word + +*/ +package main + +import "sort" + +// `GroupAnagrams` groups anagrams together. +func GroupAnagrams(words []string) [][]string { + // Create a map where the keys are sorted strings of words and the values are lists of words + // that have the same sorted string. + anagrams := map[string][]string{} + for _, word := range words { + // Get the sorted string for the word. + sortedWord := sortWord(word) + + // Add the word to the list of words associated with the sorted string in the map. + anagrams[sortedWord] = append(anagrams[sortedWord], word) + } + + // Create a list of lists, where each inner list contains all the anagrams of a word in the original list. + result := [][]string{} + for _, group := range anagrams { + result = append(result, group) + } + + // Return the list of lists. + return result +} + +// `sortWord` takes a word as input and returns a string that contains the word's letters in sorted order. +func sortWord(word string) string { + // Convert the word to a byte array. + wordBytes := []byte(word) + + // Sort the byte array. + sort.Slice(wordBytes, func(i, j int) bool { + return wordBytes[i] < wordBytes[j] + }) + + // Return a string that contains the sorted byte array. + return string(wordBytes) +} diff --git a/Strings/group_anagrams.java b/Strings/group_anagrams.java new file mode 100644 index 00000000..64d4dbd3 --- /dev/null +++ b/Strings/group_anagrams.java @@ -0,0 +1,80 @@ +/* + Group Anagrams + + Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] + Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] + + Explanation: + + The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging + the letters of another word or phrase. + + The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words + as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. + The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. + + The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are + lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the + sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. + + The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort + the byte array. The function then returns a string that contains the sorted byte array. + + O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word + +*/ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GroupAnagrams { + public List> groupAnagrams(String[] words) { + // Create a map where the keys are sorted strings of words and the values are lists of words + // that have the same sorted string. + Map> anagrams = new HashMap<>(); + + // Iterate through the words + for (String word : words) { + // Get the sorted string for the word + String sortedWord = sortWord(word); + + // Add the word to the list of words associated with the sorted string in the map + if (!anagrams.containsKey(sortedWord)) { + anagrams.put(sortedWord, new ArrayList<>()); + } + anagrams.get(sortedWord).add(word); + } + + // Create a list of lists, where each inner list contains all the anagrams of a word in the original array + List> result = new ArrayList<>(anagrams.values()); + + // Return the list of lists + return result; + } + + // Helper method to sort the characters in a word and return the sorted string + private String sortWord(String word) { + // Convert the word to a character array + char[] wordChars = word.toCharArray(); + + // Sort the character array + Arrays.sort(wordChars); + + // Return the sorted string + return new String(wordChars); + } + + public static void main(String[] args) { + // Example usage + String[] words = {"eat", "tea", "tan", "ate", "nat", "bat"}; + GroupAnagrams groupAnagrams = new GroupAnagrams(); + List> groupedAnagrams = groupAnagrams.groupAnagrams(words); + + // Print the grouped anagrams + for (List group : groupedAnagrams) { + System.out.println(group); + } + } +} diff --git a/Strings/group_anagrams.js b/Strings/group_anagrams.js new file mode 100644 index 00000000..7d524343 --- /dev/null +++ b/Strings/group_anagrams.js @@ -0,0 +1,62 @@ +/* + Group Anagrams + + Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] + Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] + + Explanation: + + The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging + the letters of another word or phrase. + + The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words + as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. + The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. + + The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are + lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the + sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. + + The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort + the byte array. The function then returns a string that contains the sorted byte array. + + O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word + +*/ +function sortWord(word) { + // Sort the characters in the word and return the sorted string + return word.split("").sort().join(""); +} + +function groupAnagrams(words) { + // Create an object where the keys are sorted strings of words and the values are arrays of words + // that have the same sorted string. + const anagrams = {}; + + // Iterate through the words + for (const word of words) { + // Get the sorted string for the word + const sortedWord = sortWord(word); + + // Add the word to the array associated with the sorted string in the object + if (!anagrams[sortedWord]) { + anagrams[sortedWord] = []; + } + anagrams[sortedWord].push(word); + } + + // Create an array of arrays, where each inner array contains all the anagrams of a word in the original array + const result = Object.values(anagrams); + + // Return the array of arrays + return result; +} + +// Example usage +const words = ["eat", "tea", "tan", "ate", "nat", "bat"]; +const groupedAnagrams = groupAnagrams(words); + +// Print the grouped anagrams +for (const group of groupedAnagrams) { + console.log(group); +} diff --git a/Strings/group_anagrams.py b/Strings/group_anagrams.py new file mode 100644 index 00000000..82eb2e02 --- /dev/null +++ b/Strings/group_anagrams.py @@ -0,0 +1,58 @@ +''' + Group Anagrams + + Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] + Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] + + Explanation: + + The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging + the letters of another word or phrase. + + The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words + as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. + The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. + + The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are + lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the + sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. + + The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort + the byte array. The function then returns a string that contains the sorted byte array. + + O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word + +''' +from typing import List +from collections import defaultdict + +def sortWord(word: str) -> str: + # Sort the characters in the word and return the sorted string + return ''.join(sorted(word)) + +def GroupAnagrams(words: List[str]) -> List[List[str]]: + # Create a defaultdict where the keys are sorted strings of words and the values are lists of words + # that have the same sorted string. + anagrams = defaultdict(list) + + # Iterate through the words + for word in words: + # Get the sorted string for the word + sortedWord = sortWord(word) + + # Add the word to the list of words associated with the sorted string in the defaultdict + anagrams[sortedWord].append(word) + + # Create a list of lists, where each inner list contains all the anagrams of a word in the original list + result = list(anagrams.values()) + + # Return the list of lists + return result + +# Example usage +words = ["eat", "tea", "tan", "ate", "nat", "bat"] +groupedAnagrams = GroupAnagrams(words) + +# Print the grouped anagrams +for group in groupedAnagrams: + print(group) diff --git a/Strings/is_pallindrome.cpp b/Strings/is_pallindrome.cpp new file mode 100644 index 00000000..e0be1293 --- /dev/null +++ b/Strings/is_pallindrome.cpp @@ -0,0 +1,55 @@ +/* + Write a function that takes in a non-empty string and that returns a boolean + representing whether the string is a palindrome. + Sample Input: abba + Output: True + Sample Input: aberba + Output: False + + Explanation: + we define a function isPalindrome that takes in a string and returns true if the string is a palindrome, + and false otherwise. The function uses two indices, left and right, initialized to the beginning and + end of the string respectively. It then iterates through the string by moving the left index to the + right and the right index to the left, checking whether the characters at these indices match. + If the characters do not match, the function returns false, indicating that the string is not a + palindrome. If the entire string is iterated through and all characters match, + the function returns true, indicating that the string is a palindrome. + + In the main function, we call the isPalindrome function on the string "racecar" and print the result. + The output will be "racecar is a palindrome". +*/ +#include +#include + +using namespace std; + +/** + * @brief Function to check whether a string is a palindrome or not + * + * @param str String to check + * @return true if the string is a palindrome, false otherwise + */ +bool isPalindrome(string str) { + int left = 0; // initialize left index to 0 + int right = str.length() - 1; // initialize right index to last character + + while (left < right) { // while left index is less than right index + if (str[left] != str[right]) { // if characters at left and right indices do not match + return false; // the string is not a palindrome + } + left++; // move left index to the right + right--; // move right index to the left + } + return true; // the string is a palindrome +} + +int main() { + string str = "racecar"; + bool isPal = isPalindrome(str); + if (isPal) { + cout << str << " is a palindrome" << endl; + } else { + cout << str << " is not a palindrome" << endl; + } + return 0; +} diff --git a/Arrays/is_pallindrome.go b/Strings/is_pallindrome.go similarity index 100% rename from Arrays/is_pallindrome.go rename to Strings/is_pallindrome.go diff --git a/Strings/is_pallindrome.java b/Strings/is_pallindrome.java new file mode 100644 index 00000000..f76c1425 --- /dev/null +++ b/Strings/is_pallindrome.java @@ -0,0 +1,47 @@ +/* + Write a function that takes in a non-empty string and that returns a boolean + representing whether the string is a palindrome. + Sample Input: abba + Output: True + Sample Input: aberba + Output: False + + Explanation: + This function takes in a non-empty string and returns a boolean representing whether the string + is a palindrome. It first removes all non-alphanumeric characters from the string and converts + it to lowercase using the replaceAll() and toLowerCase() string methods, respectively. + + It then initializes two pointers, one at the beginning and one at the end of the cleaned string. + It iterates through the string from both ends, comparing characters at each step. + If the characters do not match, it returns false. + + If the loop completes without returning false, the string is a palindrome and the function + returns true. +*/ +/** + * This function takes in a non-empty string and returns a boolean representing + * whether the string is a palindrome. + * + * @param str the string to check for palindrome + * @return true if str is a palindrome, false otherwise + */ +public static boolean isPalindrome(String str) { + // Remove all non-alphanumeric characters from the string and convert to lowercase + String cleanStr = str.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); + + // Initialize two pointers, one at the beginning and one at the end of the string + int left = 0; + int right = cleanStr.length() - 1; + + // Iterate through the string from both ends, comparing characters + while (left < right) { + if (cleanStr.charAt(left) != cleanStr.charAt(right)) { + return false; + } + left++; + right--; + } + + // If the loop completes without returning false, the string is a palindrome + return true; +} diff --git a/Strings/is_pallindrome.js b/Strings/is_pallindrome.js new file mode 100644 index 00000000..134076d1 --- /dev/null +++ b/Strings/is_pallindrome.js @@ -0,0 +1,40 @@ +/* + Write a function that takes in a non-empty string and that returns a boolean + representing whether the string is a palindrome. + Sample Input: abba + Output: True + Sample Input: aberba + Output: False + + Explanation : + The function takes in a non-empty string as input and returns a boolean value indicating whether the string + is a palindrome or not. It first converts the input string to lowercase and removes any non-alphanumeric + characters using a regular expression. It then uses two pointers (one starting from the beginning of the + string and the other starting from the end) to compare characters from opposite ends of the string. + If any mismatch is found, the function returns false immediately. If the entire string is traversed + without finding any mismatches, the function returns true, indicating that the string is a palindrome. + + Note that this implementation treats uppercase and lowercase letters as equivalent (i.e., "A" is the same as "a") + and ignores any non-alphanumeric characters (such as spaces or punctuation marks) when checking for palindromicity. +*/ +/** + * Checks if a given string is a palindrome. + * @param {string} str - The input string to check. + * @returns {boolean} True if the string is a palindrome, false otherwise. + */ +function isPalindrome(str) { + // Convert the input string to lowercase and remove any non-alphanumeric characters + str = str.toLowerCase().replace(/[^a-z0-9]/g, ""); + + // Use two pointers to compare characters from the start and end of the string + let left = 0; + let right = str.length - 1; + while (left < right) { + if (str[left] !== str[right]) { + return false; + } + left++; + right--; + } + return true; +} diff --git a/Strings/is_pallindrome.py b/Strings/is_pallindrome.py new file mode 100644 index 00000000..0f84f9be --- /dev/null +++ b/Strings/is_pallindrome.py @@ -0,0 +1,32 @@ +''' + Write a function that takes in a non-empty string and that returns a boolean + representing whether the string is a palindrome. + Sample Input: abba + Output: True + Sample Input: aberba + Output: False + + Explanation: + + In this implementation, we use Python's filter() function to remove all non-alphanumeric characters + from the string and lower() method to convert the string to lowercase. Then we check if the reversed + string is equal to the original string using the slice notation [::-1]. If the two strings are equal, + we return True, indicating that the input string is a palindrome; otherwise, we return False. +''' + +def is_palindrome(s: str) -> bool: + """ + This function takes in a non-empty string and returns a boolean indicating + whether the string is a palindrome or not. + + Parameters: + s (str): The input string + + Returns: + bool: True if s is a palindrome, False otherwise + """ + # Remove all non-alphanumeric characters and convert to lowercase + s = ''.join(filter(str.isalnum, s)).lower() + + # Check if the reversed string is equal to the original string + return s == s[::-1] diff --git a/Strings/is_unique.cpp b/Strings/is_unique.cpp new file mode 100644 index 00000000..bc7c674e --- /dev/null +++ b/Strings/is_unique.cpp @@ -0,0 +1,47 @@ +/* + Implement an algorithm to determine if a string has all unique characters. + what if you cannot use additional data structures? + + Explanation: + - The `isUniqueUsingBitVector` function takes a constant reference to a string `s` as input and returns a boolean value indicating whether the string has all unique characters. + - The variable `checker` is initialized as an integer, representing the bit vector to keep track of character occurrences. + - The function iterates over each character `c` in the string using a range-based for loop. + - For each character, the variable `val` is computed by subtracting the ASCII value of `'a'` from the ASCII value of the character. This gives the corresponding index (0-25) for lowercase alphabetic characters. + - The program checks if the bit at position `val` in `checker` is already set. If it is, it means the character has occurred before, and the function returns `false`. + - If the character is unique, the bit at position `val` in `checker` is set by performing a bitwise OR operation with `(1 << val)`. This marks the occurrence of the character in the bit vector. + - After iterating through all the characters, if no duplicate characters are found, the function returns `true`. + - In the `main` function, a few test cases are provided, and the result of calling `isUniqueUsingBitVector` with each test case is printed. + +*/ +#include +#include + +bool isUniqueUsingBitVector(const std::string& s) { + int checker = 0; // Bit vector to keep track of character occurrences + for (char c : s) { + int val = c - 'a'; // Convert character to corresponding index (0-25) + if ((checker & (1 << val)) > 0) { + // If the bit corresponding to the character is already set, it means the character has occurred before + return false; + } + checker |= (1 << val); // Set the bit corresponding to the character to mark its occurrence + } + return true; +} + +int main() { + std::string s = "ABCDD"; + std::string t = "ABCD"; + std::string u = "AAAAAABCD"; + bool msg = isUniqueUsingBitVector(s); + std::cout << std::boolalpha << msg << std::endl; + msg = isUniqueUsingBitVector(t); + std::cout << std::boolalpha << msg << std::endl; + msg = isUniqueUsingBitVector(u); + std::cout << std::boolalpha << msg << std::endl; + + msg = isUniqueUsingBitVector("aa"); + std::cout << std::boolalpha << msg << std::endl; + + return 0; +} diff --git a/CCTI/arrays_strings_is_unique.go b/Strings/is_unique.go similarity index 100% rename from CCTI/arrays_strings_is_unique.go rename to Strings/is_unique.go diff --git a/Strings/is_unique.java b/Strings/is_unique.java new file mode 100644 index 00000000..66da5e2e --- /dev/null +++ b/Strings/is_unique.java @@ -0,0 +1,43 @@ +/* + Implement an algorithm to determine if a string has all unique characters. + what if you cannot use additional data structures? + + Explanation: + - The `isUniqueUsingBitVector` function takes a string `s` as input and returns a boolean value indicating whether the string has all unique characters. + - The variable `checker` is initialized as an integer, representing the bit vector to keep track of character occurrences. + - The function iterates over each character `c` in the string using a foreach loop. + - For each character, the variable `val` is computed by subtracting the ASCII value of `'a'` from the ASCII value of the character. This gives the corresponding index (0-25) for lowercase alphabetic characters. + - The program checks if the bit at position `val` in `checker` is already set. If it is, it means the character has occurred before, and the function returns `false`. + - If the character is unique, the bit at position `val` in `checker` is set by performing a bitwise OR operation with `(1 << val)`. This marks the occurrence of the character in the bit vector. + - After iterating through all the characters, if no duplicate characters are found, the function returns `true`. + - In the `main` function, a few test cases are provided, and the result of calling `isUniqueUsingBitVector` with each test case is printed. +*/ +public class Main { + public static boolean isUniqueUsingBitVector(String s) { + int checker = 0; // Bit vector to keep track of character occurrences + for (char c : s.toCharArray()) { + int val = c - 'a'; // Convert character to corresponding index (0-25) + if ((checker & (1 << val)) > 0) { + // If the bit corresponding to the character is already set, it means the character has occurred before + return false; + } + checker |= (1 << val); // Set the bit corresponding to the character to mark its occurrence + } + return true; + } + + public static void main(String[] args) { + String s = "ABCDD"; + String t = "ABCD"; + String u = "AAAAAABCD"; + boolean msg = isUniqueUsingBitVector(s); + System.out.println(msg); + msg = isUniqueUsingBitVector(t); + System.out.println(msg); + msg = isUniqueUsingBitVector(u); + System.out.println(msg); + + msg = isUniqueUsingBitVector("aa"); + System.out.println(msg); + } +} diff --git a/Strings/is_unique.js b/Strings/is_unique.js new file mode 100644 index 00000000..a66c8bb1 --- /dev/null +++ b/Strings/is_unique.js @@ -0,0 +1,43 @@ +/* + Implement an algorithm to determine if a string has all unique characters. + what if you cannot use additional data structures? + + Explanation: + + 1. The `isUniqueUsingBitVector` function takes a string `s` as input and returns a boolean value indicating whether the string has all unique characters. + 2. The variable `checker` is initialized as 0, representing the bit vector to keep track of character occurrences. + 3. The function iterates over each character in the string using a for loop. + 4. For each character, the variable `val` is computed by subtracting the ASCII value of `'a'` from the ASCII value of the character. This calculates the corresponding index (0-25) for lowercase alphabetic characters. + 5. The program checks if the bit at position `val` in the `checker` is already set. If it is, it means the character has occurred before, and the function returns `false`. + 6. If the character is unique, the bit at position `val` in the `checker` is set by performing a bitwise OR operation with `(1 << val)`. This marks the occurrence of the character in the bit vector. + 7. After iterating through all the characters, if no duplicate characters are found, the function returns `true`. + 8. In the test cases section, a few sample strings (`s`, `t`, `u`, and `"aa"`) are provided, and the result of calling `isUniqueUsingBitVector` with each test case is printed using `console.log()`. +*/ + +function isUniqueUsingBitVector(s) { + let checker = 0; // Bit vector to keep track of character occurrences + + for (let i = 0; i < s.length; i++) { + const val = s.charCodeAt(i) - "a".charCodeAt(0); // Convert character to corresponding index (0-25) + + if ((checker & (1 << val)) > 0) { + // If the bit corresponding to the character is already set, it means the character has occurred before + return false; + } + + checker |= 1 << val; // Set the bit corresponding to the character to mark its occurrence + } + + return true; +} + +// Test cases +const s = "ABCDD"; +const t = "ABCD"; +const u = "AAAAAABCD"; + +console.log(isUniqueUsingBitVector(s)); // false, 'D' appears more than once +console.log(isUniqueUsingBitVector(t)); // true, all characters are unique +console.log(isUniqueUsingBitVector(u)); // false, 'A' appears more than once + +console.log(isUniqueUsingBitVector("aa")); // false, 'a' appears more than once diff --git a/Strings/is_unique.py b/Strings/is_unique.py new file mode 100644 index 00000000..750b5feb --- /dev/null +++ b/Strings/is_unique.py @@ -0,0 +1,34 @@ +''' + Implement an algorithm to determine if a string has all unique characters. + what if you cannot use additional data structures? + + Explanation: + - The `is_unique_using_bit_vector` function takes a string `s` as input and returns a boolean value indicating whether the string has all unique characters. + - The variable `checker` is initialized as an integer, representing the bit vector to keep track of character occurrences. + - The function iterates over each character `c` in the string using a for loop. + - For each character, the variable `val` is computed by subtracting the ASCII value of `'a'` from the ASCII value of the character. This gives the corresponding index (0-25) for lowercase alphabetic characters. + - The program checks if the bit at position `val` in `checker` is already set. If it is, it means the character has occurred before, and the function returns `False`. + - If the character is unique, the bit at position `val` in +''' +def is_unique_using_bit_vector(s): + checker = 0 # Bit vector to keep track of character occurrences + for c in s: + val = ord(c) - ord('a') # Convert character to corresponding index (0-25) + if (checker & (1 << val)) > 0: + # If the bit corresponding to the character is already set, it means the character has occurred before + return False + checker |= (1 << val) # Set the bit corresponding to the character to mark its occurrence + return True + +s = "ABCDD" +t = "ABCD" +u = "AAAAAABCD" +msg = is_unique_using_bit_vector(s) +print(msg) +msg = is_unique_using_bit_vector(t) +print(msg) +msg = is_unique_using_bit_vector(u) +print(msg) + +msg = is_unique_using_bit_vector("aa") +print(msg) diff --git a/Strings/length_of_longest_substring.java b/Strings/length_of_longest_substring.java new file mode 100644 index 00000000..446cef52 --- /dev/null +++ b/Strings/length_of_longest_substring.java @@ -0,0 +1,49 @@ +/* Implement lengthOfLongestSubstring(s), which calculates the length of the longest possible substring that does not contain repeating characters. + +Example 1: +Input: "abcabcbaba" +Output: 3 + +Example 2: +Input: "dddddddd" +Output: 1 + +Example 3: +Input: "pwwkewo" +Output: 4 + +Constraints: +0 <= s.length <= 5 * 10^4 + +Time complexity: O(n^2) +Space complexity: O(1) + +*/ + +class Solution { + public int lengthOfLongestSubstring(String s) { + int length = s.length(); + if (length==0) { + return 0; + } + + int max = 1; + // Aiming to find substring of s that starts at index i and ends at j-1. + int i = 0; + int j = i+1; + + while (j max) { + max = j-i; + } + + i++; + } + + return max; + } +} diff --git a/Strings/longest palindromic substring.java b/Strings/longest palindromic substring.java new file mode 100644 index 00000000..82e0a1bc --- /dev/null +++ b/Strings/longest palindromic substring.java @@ -0,0 +1,73 @@ +/* +Issue#415 +//Input: +Given a string s, return the longest palindromic substring in s. + +//Palindrome:A palindrome is a word, phrase, or sequence that reads the same forward and backward, like "level" or "A man, a plan, a canal, Panama." + +//Example 1: +Input: s = "babad" +Output: "bab" +Explanation: "aba" is also a valid answer. +//Example 2: +Input: s = "cbbd" +Output: "bb" + +//Time complexity: +>>Time Complexity: O(n2) +as there is two recursion calls which are applied as two pointers so here Complexity would be O(n2). +>>Space Complexity: O(n) which is nothing but the storage consumed in this process. + +//Explanation: +>>The function first converts the input string into a character array. +>>If the length of the string is less than 2, it means the string itself is a palindrome, so it returns the string as is. +>>The function then iterates through each character of the string. +>>For each character, it expands outwards from that character and checks if it forms a palindrome. +>>It does this by calling the expandPalindrome function twice: once for odd-length palindromes with the current character as the center, +and once for even-length palindromes with the current character and the next character as centers. +>>The expandPalindrome function checks if the characters at positions j and k are equal, and if so, it expands the palindrome by decrementing j and incrementing k. +>>It continues this process until the characters at positions j and k are no longer equal or reach the boundaries of the string. +>>If a longer palindrome is found, the maxLen and lo variables are updated accordingly. +maxLen stores the length of the longest palindrome found so far, and lo stores the starting position of the longest palindrome. +>>Finally, the function returns the substring of the original string from the starting position lo to lo + maxLen, which represents the longest palindrome found. + +*/ +class Solution { + int maxLen = 0; // Length of the longest palindrome + int lo = 0; // Starting position of the longest palindrome + + public String longestPalindrome(String s) { + char[] input = s.toCharArray(); // Convert the input word to individual characters + + if (s.length() < 2) { + return s; // If word has less than 2 letters, it is already a palindrome + } + + for (int i = 0; i < input.length; i++) { + expandPalindrome(input, i, i); // Check odd-length palindromes with current letter as center + expandPalindrome(input, i, i + 1); // Check even-length palindromes with current and next letters as center + } + return s.substring(lo, lo + maxLen); // Return the longest palindrome found + } + + public void expandPalindrome(char[] s, int j, int k) { + while (j >= 0 && k < s.length && s[j] == s[k]) { + j--; // Move left to expand potential palindrome + k++; // Move right to expand potential palindrome + } + if (maxLen < k - j - 1) { + maxLen = k - j - 1; // Update length of longest palindrome if longer one is found + lo = j + 1; // Update starting position of longest palindrome + } + } + + public static void main(String[] args) { + Solution solution = new Solution(); + String input = "babad"; + String longestPalindrome = solution.longestPalindrome(input); + System.out.println("Longest Palindrome: " + longestPalindrome); + } +} + + + diff --git a/Strings/longest_common_prefix.cpp b/Strings/longest_common_prefix.cpp new file mode 100644 index 00000000..8dea0140 --- /dev/null +++ b/Strings/longest_common_prefix.cpp @@ -0,0 +1,47 @@ +/* +Write a function to find the longest common prefix string amongst an array of strings. + +If there is no common prefix, return an empty string "". + + + +Example 1: + +Input: strs = ["flower","flow","flight"] +Output: "fl" +Example 2: + +Input: strs = ["dog","racecar","car"] +Output: "" +Explanation: There is no common prefix among the input strings. + + +Constraints: + +1 <= strs.length <= 200 +0 <= strs[i].length <= 200 +strs[i] consists of only lowercase English letters. + +Time complexity : O(strs) +*/ + +#include + +class Solution { +public: + string longestCommonPrefix(vector& strs) { + if(strs.size() == 0) return ""; + string ans = ""; + // fix one string and check the common prefix of this string with other strings + // s is the smallest string, so longest cant be greater than smallest string in array + string s = *min_element(strs.begin(), strs.end()); + for(int i = 0; i < s.size(); i++){ + for(int j = 0; j < strs.size(); j++){ + if(s[i] != strs[j][i]) + return ans; + } + ans.push_back(s[i]); + } + return ans; + } +}; \ No newline at end of file diff --git a/Strings/longest_string.cpp b/Strings/longest_string.cpp new file mode 100644 index 00000000..74710716 --- /dev/null +++ b/Strings/longest_string.cpp @@ -0,0 +1,62 @@ +/*The code implements the dynamic programming approach to find the longest palindrome substring in a given string. It uses a two-dimensional table dp to store the results of subproblems, where dp[i][j] represents whether the substring from index i to index j is a palindrome. + +The algorithm first checks for all substrings of length 1 and marks them as palindromes. Then it checks for substrings of length 2 and updates the start and maxLen variables if a palindrome of length 2 is found. + +After that, it checks for substrings of length greater than 2 by iterating over all possible lengths and positions. If a palindrome is found at a particular position, it updates the start and maxLen variables accordingly. + +*/ + +#include +#include + +class Solution { +public: + /** + * Finds the longest palindrome substring within a given string. + * + * @param s The input string. + * @return The longest palindrome substring. + */ + std::string longestPalindrome(std::string s) { + int n = s.length(); + if (n < 2) { + return s; + } + + int start = 0; // start index of the longest palindrome + int maxLen = 1; // length of the longest palindrome + + // Initialize a table to store the results of subproblems + std::vector> dp(n, std::vector(n, false)); + + // All substrings of length 1 are palindromes + for (int i = 0; i < n; i++) { + dp[i][i] = true; + } + + // Check for substrings of length 2 + for (int i = 0; i < n - 1; i++) { + if (s[i] == s[i + 1]) { + dp[i][i + 1] = true; + start = i; + maxLen = 2; + } + } + + // Check for substrings of length greater than 2 + for (int len = 3; len <= n; len++) { + for (int i = 0; i < n - len + 1; i++) { + int j = i + len - 1; + if (s[i] == s[j] && dp[i + 1][j - 1]) { + dp[i][j] = true; + start = i; + maxLen = len; + } + } + } + + // Return the longest palindrome substring + return s.substr(start, maxLen); + } +}; + diff --git a/Strings/one_edit.cpp b/Strings/one_edit.cpp new file mode 100644 index 00000000..7f3a6122 --- /dev/null +++ b/Strings/one_edit.cpp @@ -0,0 +1,88 @@ + /* + You're given two strings stringone and stringtwo. Write a function that determines if these + two strings can be made equal using only one edit. + + + There are 3 possible edits: + Replace: One character in one string is swapped for a different character. + Add:: One character is added at any index in one string. + Remove: One character is removed at any index in one string. + + Sample Input: StringOne: alaska StringTwo: aloska + Output: True + + + Explanation: + The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given + strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. + + The `OneEdit` function takes two strings as input and returns a boolean indicating whether + they are one edit away. Here's the breakdown of the algorithm: + + 1. Calculate the lengths of the two strings. + 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's + not possible to make one edit to make the strings equal. + 3. Traverse both strings until the shorter one is fully traversed or an unequal character is + found. + 4. If an unequal character is found, check the remaining portion of the strings to determine + if they are still one edit away. + 5. Check the remaining characters in the longer string compared to the remaining characters + in the shorter string. + 6. Return `true` if the remaining portions match, indicating they are one edit away. + 7. If the loop completes without finding any unequal characters, the strings are either + identical or differ only in length by 1, which means they are one edit away. + 8. The `abs` and `min` functions are utility functions used to calculate the absolute value + and minimum of two integers, respectively. + + The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. + + O(n) time | O(1) space - where n is the length of the shorter string + + +*/ +#include +#include +using namespace std; + +bool OneEdit(string stringOne, string stringTwo) { + int lengthOne = stringOne.length(); + int lengthTwo = stringTwo.length(); + + // Check the difference in lengths between the two strings. + // If the difference is greater than 1, it is not possible to make one edit to make them equal. + if (abs(lengthOne - lengthTwo) > 1) { + return false; + } + + // Traverse the strings until the shorter one is fully traversed or an unequal character is found. + for (int i = 0; i < min(lengthOne, lengthTwo); i++) { + // If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. + if (stringOne[i] != stringTwo[i]) { + // Check the remaining characters in the longer string compared to the remaining characters in the shorter string. + // Return true if they match, indicating they are one edit away. + if (lengthOne > lengthTwo) { + return stringOne.substr(i + 1) == stringTwo.substr(i); + } else if (lengthTwo > lengthOne) { + return stringTwo.substr(i + 1) == stringOne.substr(i); + } else { + return stringOne.substr(i + 1) == stringTwo.substr(i + 1); + } + } + } + + // If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. + return true; +} + +int main() { + string stringOne = "pale"; + string stringTwo = "ple"; + + // Check if the strings are one edit away + bool isOneEdit = OneEdit(stringOne, stringTwo); + + // Print the result + cout << "The strings are" << (isOneEdit ? "" : " not") << " one edit away." << endl; + + return 0; +} diff --git a/Strings/one_edit.go b/Strings/one_edit.go new file mode 100644 index 00000000..59505eef --- /dev/null +++ b/Strings/one_edit.go @@ -0,0 +1,87 @@ +/* + You're given two strings stringone and stringtwo. Write a function that determines if these + two strings can be made equal using only one edit. + + + There are 3 possible edits: + Replace: One character in one string is swapped for a different character. + Add:: One character is added at any index in one string. + Remove: One character is removed at any index in one string. + + Sample Input: StringOne: alaska StringTwo: aloska + Output: True + + + Explanation: + The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given + strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. + + The `OneEdit` function takes two strings as input and returns a boolean indicating whether + they are one edit away. Here's the breakdown of the algorithm: + + 1. Calculate the lengths of the two strings. + 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's + not possible to make one edit to make the strings equal. + 3. Traverse both strings until the shorter one is fully traversed or an unequal character is + found. + 4. If an unequal character is found, check the remaining portion of the strings to determine + if they are still one edit away. + 5. Check the remaining characters in the longer string compared to the remaining characters + in the shorter string. + 6. Return `true` if the remaining portions match, indicating they are one edit away. + 7. If the loop completes without finding any unequal characters, the strings are either + identical or differ only in length by 1, which means they are one edit away. + 8. The `abs` and `min` functions are utility functions used to calculate the absolute value + and minimum of two integers, respectively. + + The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. + + O(n) time | O(1) space - where n is the length of the shorter string + + +*/ +package main + +func OneEdit(stringOne string, stringTwo string) bool { + lengthOne := len(stringOne) + lengthTwo := len(stringTwo) + + // Check the difference in lengths between the two strings. + // If the difference is greater than 1, it is not possible to make one edit to make them equal. + if abs(lengthOne - lengthTwo) > 1 { + return false + } + + // Traverse the strings until the shorter one is fully traversed or an unequal character is found. + for i := 0; i < min(lengthOne, lengthTwo); i++ { + // If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. + if stringOne[i] != stringTwo[i] { + // Check the remaining characters in the longer string compared to the remaining characters in the shorter string. + // Return true if they match, indicating they are one edit away. + if lengthOne > lengthTwo { + return stringOne[i+1:] == stringTwo[i:] + } else if lengthTwo > lengthOne { + return stringTwo[i+1:] == stringOne[i:] + } else { + return stringOne[i+1:] == stringTwo[i+1:] + } + } + } + + // If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. + return true +} + +func abs(a int) int { + if a < 0 { + return -a + } + return a +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/Strings/one_edit.java b/Strings/one_edit.java new file mode 100644 index 00000000..cafcd67d --- /dev/null +++ b/Strings/one_edit.java @@ -0,0 +1,84 @@ +/* + You're given two strings stringone and stringtwo. Write a function that determines if these + two strings can be made equal using only one edit. + + + There are 3 possible edits: + Replace: One character in one string is swapped for a different character. + Add:: One character is added at any index in one string. + Remove: One character is removed at any index in one string. + + Sample Input: StringOne: alaska StringTwo: aloska + Output: True + + + Explanation: + The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given + strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. + + The `OneEdit` function takes two strings as input and returns a boolean indicating whether + they are one edit away. Here's the breakdown of the algorithm: + + 1. Calculate the lengths of the two strings. + 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's + not possible to make one edit to make the strings equal. + 3. Traverse both strings until the shorter one is fully traversed or an unequal character is + found. + 4. If an unequal character is found, check the remaining portion of the strings to determine + if they are still one edit away. + 5. Check the remaining characters in the longer string compared to the remaining characters + in the shorter string. + 6. Return `true` if the remaining portions match, indicating they are one edit away. + 7. If the loop completes without finding any unequal characters, the strings are either + identical or differ only in length by 1, which means they are one edit away. + 8. The `abs` and `min` functions are utility functions used to calculate the absolute value + and minimum of two integers, respectively. + + The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. + + O(n) time | O(1) space - where n is the length of the shorter string + + +*/ +public class Main { + public static void main(String[] args) { + String stringOne = "pale"; + String stringTwo = "ple"; + + // Check if the strings are one edit away + boolean isOneEdit = isOneEdit(stringOne, stringTwo); + + // Print the result + System.out.println("The strings are" + (isOneEdit ? "" : " not") + " one edit away."); + } + + public static boolean isOneEdit(String stringOne, String stringTwo) { + int lengthOne = stringOne.length(); + int lengthTwo = stringTwo.length(); + + // Check the difference in lengths between the two strings. + // If the difference is greater than 1, it is not possible to make one edit to make them equal. + if (Math.abs(lengthOne - lengthTwo) > 1) { + return false; + } + + // Traverse the strings until the shorter one is fully traversed or an unequal character is found. + for (int i = 0; i < Math.min(lengthOne, lengthTwo); i++) { + // If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. + if (stringOne.charAt(i) != stringTwo.charAt(i)) { + // Check the remaining characters in the longer string compared to the remaining characters in the shorter string. + // Return true if they match, indicating they are one edit away. + if (lengthOne > lengthTwo) { + return stringOne.substring(i + 1).equals(stringTwo.substring(i)); + } else if (lengthTwo > lengthOne) { + return stringTwo.substring(i + 1).equals(stringOne.substring(i)); + } else { + return stringOne.substring(i + 1).equals(stringTwo.substring(i + 1)); + } + } + } + + // If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. + return true; + } +} diff --git a/Strings/one_edit.js b/Strings/one_edit.js new file mode 100644 index 00000000..460373b2 --- /dev/null +++ b/Strings/one_edit.js @@ -0,0 +1,80 @@ +/* + You're given two strings stringone and stringtwo. Write a function that determines if these + two strings can be made equal using only one edit. + + + There are 3 possible edits: + Replace: One character in one string is swapped for a different character. + Add:: One character is added at any index in one string. + Remove: One character is removed at any index in one string. + + Sample Input: StringOne: alaska StringTwo: aloska + Output: True + + + Explanation: + The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given + strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. + + The `OneEdit` function takes two strings as input and returns a boolean indicating whether + they are one edit away. Here's the breakdown of the algorithm: + + 1. Calculate the lengths of the two strings. + 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's + not possible to make one edit to make the strings equal. + 3. Traverse both strings until the shorter one is fully traversed or an unequal character is + found. + 4. If an unequal character is found, check the remaining portion of the strings to determine + if they are still one edit away. + 5. Check the remaining characters in the longer string compared to the remaining characters + in the shorter string. + 6. Return `true` if the remaining portions match, indicating they are one edit away. + 7. If the loop completes without finding any unequal characters, the strings are either + identical or differ only in length by 1, which means they are one edit away. + 8. The `abs` and `min` functions are utility functions used to calculate the absolute value + and minimum of two integers, respectively. + + The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. + + O(n) time | O(1) space - where n is the length of the shorter string + + +*/ +function isOneEdit(stringOne, stringTwo) { + const lengthOne = stringOne.length; + const lengthTwo = stringTwo.length; + + // Check the difference in lengths between the two strings. + // If the difference is greater than 1, it is not possible to make one edit to make them equal. + if (Math.abs(lengthOne - lengthTwo) > 1) { + return false; + } + + // Traverse the strings until the shorter one is fully traversed or an unequal character is found. + for (let i = 0; i < Math.min(lengthOne, lengthTwo); i++) { + // If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. + if (stringOne[i] !== stringTwo[i]) { + // Check the remaining characters in the longer string compared to the remaining characters in the shorter string. + // Return true if they match, indicating they are one edit away. + if (lengthOne > lengthTwo) { + return stringOne.slice(i + 1) === stringTwo.slice(i); + } else if (lengthTwo > lengthOne) { + return stringTwo.slice(i + 1) === stringOne.slice(i); + } else { + return stringOne.slice(i + 1) === stringTwo.slice(i + 1); + } + } + } + + // If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. + return true; +} + +const stringOne = "pale"; +const stringTwo = "ple"; + +// Check if the strings are one edit away +const isOneEdit = isOneEdit(stringOne, stringTwo); + +// Print the result +console.log(`The strings are${isOneEdit ? "" : " not"} one edit away.`); diff --git a/Strings/one_edit.py b/Strings/one_edit.py new file mode 100644 index 00000000..9215d1cb --- /dev/null +++ b/Strings/one_edit.py @@ -0,0 +1,75 @@ +''' + You're given two strings stringone and stringtwo. Write a function that determines if these + two strings can be made equal using only one edit. + + + There are 3 possible edits: + Replace: One character in one string is swapped for a different character. + Add:: One character is added at any index in one string. + Remove: One character is removed at any index in one string. + + Sample Input: StringOne: alaska StringTwo: aloska + Output: True + + + Explanation: + The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given + strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. + + The `OneEdit` function takes two strings as input and returns a boolean indicating whether + they are one edit away. Here's the breakdown of the algorithm: + + 1. Calculate the lengths of the two strings. + 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's + not possible to make one edit to make the strings equal. + 3. Traverse both strings until the shorter one is fully traversed or an unequal character is + found. + 4. If an unequal character is found, check the remaining portion of the strings to determine + if they are still one edit away. + 5. Check the remaining characters in the longer string compared to the remaining characters + in the shorter string. + 6. Return `true` if the remaining portions match, indicating they are one edit away. + 7. If the loop completes without finding any unequal characters, the strings are either + identical or differ only in length by 1, which means they are one edit away. + 8. The `abs` and `min` functions are utility functions used to calculate the absolute value + and minimum of two integers, respectively. + + The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. + + O(n) time | O(1) space - where n is the length of the shorter string + +''' +def OneEdit(stringOne, stringTwo): + lengthOne = len(stringOne) + lengthTwo = len(stringTwo) + + # Check the difference in lengths between the two strings. + # If the difference is greater than 1, it is not possible to make one edit to make them equal. + if abs(lengthOne - lengthTwo) > 1: + return False + + # Traverse the strings until the shorter one is fully traversed or an unequal character is found. + for i in range(min(lengthOne, lengthTwo)): + # If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. + if stringOne[i] != stringTwo[i]: + # Check the remaining characters in the longer string compared to the remaining characters in the shorter string. + # Return True if they match, indicating they are one edit away. + if lengthOne > lengthTwo: + return stringOne[i+1:] == stringTwo[i:] + elif lengthTwo > lengthOne: + return stringTwo[i+1:] == stringOne[i:] + else: + return stringOne[i+1:] == stringTwo[i+1:] + + # If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. + return True + +def abs(a): + if a < 0: + return -a + return a + +def min(a, b): + if a < b: + return a + return b diff --git a/Strings/plaindrome_str.cpp b/Strings/plaindrome_str.cpp new file mode 100644 index 00000000..874d80b3 --- /dev/null +++ b/Strings/plaindrome_str.cpp @@ -0,0 +1,53 @@ +#include +#include + +using namespace std; + +bool isPalindrome(string s, int start, int end) { + while (start < end) { + if (s[start] != s[end]) { + return false; + } + start++; + end--; + } + return true; +} + +vector> partitionPalindrome(string s) { + vector> result; + vector current; + + backtrack(s, 0, current, result); + + return result; +} + +void backtrack(string s, int start, vector& current, vector>& result) { + if (start == s.length()) { + result.push_back(current); + return; + } + + for (int end = start; end < s.length(); end++) { + if (isPalindrome(s, start, end)) { + current.push_back(s.substr(start, end - start + 1)); + backtrack(s, end + 1, current, result); + current.pop_back(); + } + } +} + +int main() { + string s = "aab"; + vector> partitions = partitionPalindrome(s); + + for (auto partition : partitions) { + for (string palindrome : partition) { + cout << palindrome << " "; + } + cout << endl; + } + + return 0; +} diff --git a/Random_Problems/rev_string.go b/Strings/reverse_string.go similarity index 71% rename from Random_Problems/rev_string.go rename to Strings/reverse_string.go index 8f1af1d9..514d4c44 100644 --- a/Random_Problems/rev_string.go +++ b/Strings/reverse_string.go @@ -1,18 +1,22 @@ -// Reverse a string -// Program Author : Abhisek Kumar Gupta - -package main -import "fmt" - -func reverse(s string) string { - chars := []rune(s) - for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 { - chars[i], chars[j] = chars[j], chars[i] - } - return string(chars) -} - -func main() { - fmt.Printf("%v\n", reverse("abcdefg")) -} - +// Reverse a string +// Program Author : Abhisek Kumar Gupta + +package main + +import "fmt" + +func reverse(s string) string { + // since strings are immutable in go, we make array of runes + chars := []rune(s) + // swap start and end position characters + for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 { + chars[i], chars[j] = chars[j], chars[i] + } + // cast chars to string + return string(chars) +} + +func main() { + fmt.Printf("%v\n", reverse("abcdefg")) +} + diff --git a/Strings/reverse_words_in_a_string.cpp b/Strings/reverse_words_in_a_string.cpp new file mode 100644 index 00000000..5520d9f9 --- /dev/null +++ b/Strings/reverse_words_in_a_string.cpp @@ -0,0 +1,44 @@ +/* + Given an input string s, reverse the order of the words. + A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. + Return a string of the words in reverse order concatenated by a single space. + + [Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces.] + + + Input: s = "the sky is blue" + Output: "blue is sky the" + + Input: s = " hello world " + Output: "world hello" + + Input: s = "a good example" + Output: "example good a" + + Constraints: + + > 1 <= s.length <= 104 + > s contains English letters (upper-case and lower-case), digits, and spaces ' '. + > There is at least one word in s. + +*/ +class Solution { +public: + string reverseWords(string s) { + //initialize the variables + string ans; + int i=0; + + while(i=s.length()) break; // if pointer exceeds the length of the string break! + int j=i+1; // initialize another pointer just ahead of i + while(j "dlroW olleH". + + // Now, let's iterate the sentence and reverse each word in place. + // "dlroW olleH" -> "World Hello" + start, end := 0, 0 + + for { + // find the start index of each word by detecting spaces. + for start < len(sentenceBytes) && sentenceBytes[start] == ' ' { + start += 1 + } + if start == strLen { + break + } + + // Find the end index of the word. + end = start + 1 + for end < strLen && sentenceBytes[end] != ' ' { + end += 1 + } + + // Let's call our helper function to reverse the word in-place. + sentenceBytes = strRev(sentenceBytes, start, end-1) + start = end + } + return string(sentenceBytes) +} + +// strRev function that reverses a whole sentence character by character +func strRev(str []byte, startRev int, endRev int) []byte { + // Starting from the two ends of the list, and moving + // in towards the centre of the string, swap the characters + for startRev < endRev { + temp := str[startRev] // temp store for swapping + str[startRev] = str[endRev] // swap step 1 + str[endRev] = temp // swap step 2 + + startRev += 1 // move forwards towards the middle + endRev -= 1 // move backwards towards the middle + } + + // Removing multiple spaces + // Removing multiple spaces + i := 0 + j := len(str) - 1 + strRes := make([]byte, 0) + for { + if str[i] != ' ' { + break + } + i++ + } + for { + if str[j] != ' ' { + break + } + j-- + } + for i <= j { + if str[i] == ' ' && strRes[len(strRes)-1] != ' ' { + strRes = append(strRes, str[i]) + } else if str[i] != ' ' { + strRes = append(strRes, str[i]) + } + i++ + } + + // Returning the reversed sentence + return strRes +} + +// Driver code +func main() { + stringToReverse := []string{"Hello World!", "We love Python.", "The quick brown fox jumped over the lazy dog.", "Hey!", "To be, or not to be", "AAAAA", "Hello World"} + + for i, str := range stringToReverse { + fmt.Printf("%d.\tActual string: \"%s\"\n", i+1, str) + fmt.Printf("\tReversed string: \"%s\"\n", reverseWords(str)) + fmt.Printf("%s\n", strings.Repeat("-", 100)) + } + fmt.Printf("%s\n", strings.Repeat("*", 100)) + for i, str := range stringToReverse { + fmt.Printf("%d.\tActual string: \"%s\"\n", i+1, str) + fmt.Printf("\tReversed string: \"%s\"\n", ReverseWordsNew(str)) + fmt.Printf("%s\n", strings.Repeat("-", 100)) + } +} + +// Using regexp and strings library + +func ReverseWordsNew(s string) string { + // trim leading and multiple spaces + trimmedString := trimHelper(s) + // convert string into array bytes since strings are immutable in go + sBytes := []byte(trimmedString) + // reverse the entire sBytes first + strlen := len(sBytes) + reversedString := revHelper(sBytes, 0, strlen - 1) + + // this way words will be in desired position + + start, end := 0, 0 + for { + for start < strlen && reversedString[start] == ' ' { + start += 1; + } + if start == strlen { + break + } + end = start + 1 + for end < strlen && reversedString[end] != ' ' { + end += 1 + } + reversedString = revHelper(reversedString, start, end - 1) + start = end + } + return string(reversedString) +} + +func trimHelper(s string) string { + result := strings.TrimSpace(s) + regex := regexp.MustCompile("\\s+") + result = regex.ReplaceAllString(result, " ") + return result +} + +func revHelper(s []byte, start int, end int) []byte { + for start < end { + s[start], s[end] = s[end], s[start] + start += 1 + end -= 1 + } + return s +} \ No newline at end of file diff --git a/Strings/reverse_words_in_a_string.js b/Strings/reverse_words_in_a_string.js new file mode 100644 index 00000000..40d7a17a --- /dev/null +++ b/Strings/reverse_words_in_a_string.js @@ -0,0 +1,36 @@ +/// Problem Statement: +/// You are given a string of length N. You need to reverse the string word by word. There can be multiple spaces between two words and there can be leading or trailing spaces but, in the output, reversed string you need to put a single space between two words, and your reversed string should not contain leading or trailing spaces. + +/// Sample Input: You need to reverse the string word by word +/// Expected Output: word by word string the reverse to need You + +/// Decalaration of function which will receive string to reverese +function reverseString(inputString) { + /// Trim function removes all the leading and trailing spaces, Split method store them in the array inputStringArray + var trimmedString = inputString.trim(); + var inputStringArray = trimmedString.split(" "); + + /// Variable declaration to store the output result + var outputString = ""; + + /// Loop through the array in reverse order to reverse the string, subtracting -1 from length because length starts from 1 but index strats from 0 + for (i = inputStringArray.length - 1; i >= 0; i--) { + /// If its a first iteration then store the word in variable otherwise concatenate it with the current string + if (inputStringArray[i] == "") { + continue; + } else { + outputString += inputStringArray[i] + " "; + } + } + return outputString.trim(); +} + +// console.log("You are given a string of length N"); +// console.log(reverseString("You are given a string of length N")); + +console.log(" You need to reverse the string word by word "); +console.log( + reverseString( + " You need to reverse the string word by word " + ) +); diff --git a/Strings/reverse_words_in_string.java b/Strings/reverse_words_in_string.java new file mode 100644 index 00000000..496d2bde --- /dev/null +++ b/Strings/reverse_words_in_string.java @@ -0,0 +1,97 @@ +/** + * Given an input string s, reverse the order of the words. + * + * A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. + * + * Return a string of the words in reverse order concatenated by a single space. + * + * Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces. + * + * + * + * Example 1: + * + * Input: s = "the sky is blue" + * Output: "blue is sky the" + * Example 2: + * + * Input: s = " hello world " + * Output: "world hello" + * Explanation: Your reversed string should not contain leading or trailing spaces. + */ + +package Strings; + +public class ReverseWordsInString { + public static void main(String[] args) { + String string = "the sky is blue"; + + String res = solve(string); + System.out.println(res); + } + public static String solve(String string) { + // O(N) time | O(N) - where N is the length of the array. + + //Convert String to String Builder + //and trim spaces at the same time + StringBuilder stringBuilder = trimSpaces(string); + + System.out.println(string); + + //reverse the whole word + reverse(stringBuilder, 0, stringBuilder.length() - 1); + System.out.println(stringBuilder); + + //reverse each word + reverseEachWord(stringBuilder); + System.out.println(stringBuilder); + + return stringBuilder.toString(); + } + + public static StringBuilder trimSpaces(String string) { + int leftIdx = 0, rightIdx = string.length() - 1; + + // remove leading spaces + while (leftIdx <= rightIdx && string.charAt(leftIdx) == ' ') ++leftIdx; + + // remove trailing spaces + while (leftIdx <= rightIdx && string.charAt(rightIdx) == ' ') --rightIdx; + + // reduce multiple spaces to single one + StringBuilder stringBuilder = new StringBuilder(); + while (leftIdx <= rightIdx) { + char currentChar = string.charAt(leftIdx); + + if (currentChar != ' ') stringBuilder.append(currentChar); + else if (stringBuilder.charAt(stringBuilder.length() - 1) != ' ') stringBuilder.append(currentChar); + + ++leftIdx; + } + return stringBuilder; + } + + public static void reverse(StringBuilder stringBuilder, int leftIdx, int rightIdx) { + while (leftIdx < rightIdx) { + char temp = stringBuilder.charAt(leftIdx); + stringBuilder.setCharAt(leftIdx++, stringBuilder.charAt(rightIdx)); + stringBuilder.setCharAt(rightIdx--, temp); + } + } + public static void reverseEachWord(StringBuilder stringBuilder) { + int len = stringBuilder.length(); + int startIdx = 0, endIdx = 0; + + while (startIdx < len) { + // go to the end of the word + while (endIdx < len && stringBuilder.charAt(endIdx) != ' ') ++endIdx; + // reverse the word + reverse(stringBuilder, startIdx, endIdx - 1); + // move to the next word + startIdx = endIdx + 1; + ++endIdx; + } + } + +} + diff --git a/Strings/valid_palindrome.js b/Strings/valid_palindrome.js new file mode 100644 index 00000000..ab39678d --- /dev/null +++ b/Strings/valid_palindrome.js @@ -0,0 +1,54 @@ +/*Name : Abhinav kumar +Github username : Abhinavcode13 +Repository name : data-structures-and-algorithms +Problem :Two Pointers: Check whether a given string is a Valid Palindrome in Javascript +Issue Number : #649 +Problem statement : + +Explanation of the below Java code : + +we first define the isPalindrome() function, which converts the input string to lowercase and removes all non-alphanumeric characters using a regular expression, and then uses two pointers to loop through the string and check whether it is a valid palindrome. + +We then use the prompt() function to display a popup dialog box and prompt the user to enter a string input. The input value is then stored in the str variable. + +We then call the isPalindrome() function with the input string as an argument to check whether it is a palindrome. If it is, we use the console.log() function to display a message saying that the input string is a palindrome. If it is not, we display a message saying that the input string is not a palindrome. + + + +*/ + +-------------------------------------------------------------------------------------------------------//Java code begins here------------------------------------------------------------------------------------------------------------------------------- + +function isPalindrome(str) { + // Convert the input string to lowercase and remove non-alphanumeric characters + str = str.toLowerCase().replace(/[^a-z0-9]/g, ''); + + // Initialize two pointers, one starting from the beginning of the string, and the other from the end + let left = 0; + let right = str.length - 1; + + // Loop through the string while the pointers haven't crossed each other + while (left < right) { + // If the characters at the left and right pointers don't match, the string is not a palindrome + if (str[left] !== str[right]) { + return false; + } + + // Move the pointers towards each other + left++; + right--; + } + + // If the loop finishes, the string is a palindrome + return true; +} + +// Prompt the user for a string input +let str = prompt("Enter a string:"); + +// Check whether the input string is a palindrome +if (isPalindrome(str)) { + console.log(`${str} is a palindrome!`); +} else { + console.log(`${str} is not a palindrome.`); +} diff --git a/Strings/valid_pallindrome2.cpp b/Strings/valid_pallindrome2.cpp new file mode 100644 index 00000000..77670063 --- /dev/null +++ b/Strings/valid_pallindrome2.cpp @@ -0,0 +1,47 @@ +// Check whether a string can be a valid palindrome by removing at most one character from it +/* + Write a function that takes a string as input and checks whether it can be a valid palindrome by removing at most one character from it. + + Constraints: string.length The string only consists of English letters + + Sample Input : "madame" + Output : True + + Sample Input : "masdasd" + Output : False +*/ +class Solution { +public: + bool validate(string s, int start, int end){ + int len = s.size(); + while(start < end){ + if(s[start] == s[end]){ + start++; + end--; + } + // doesn't match then return false + else{ + return false; + } + } + return true; + } + bool validPalindrome(string s) { + int len = s.size(); + // initialize two pointers at opposite end of string + int start = 0, end = len - 1; + while(start < end){ + // if value at left and right index match then move them closer + if(s[start] == s[end]){ + start++; + end--; + } + else{ // if mismatch occurs then skip one and check rest + bool r1 = validate(s, start + 1, end); // skip one from left and check remaining + bool r2 = validate(s, start, end -1); // skip one from right and check remaining + return r1 || r2; // if either is true return true + } + } + return true; + } +}; \ No newline at end of file diff --git a/Strings/valid_pallindrome2.go b/Strings/valid_pallindrome2.go new file mode 100644 index 00000000..5a3d94d6 --- /dev/null +++ b/Strings/valid_pallindrome2.go @@ -0,0 +1,61 @@ +// Check whether a string can be a valid palindrome by removing at most one character from it +/* + The given Go program takes a string as input and checks if it can be a valid palindrome by removing at most one + character from it. The program starts by defining two pointers, left and right, that move towards each other from + opposite ends of the input string. At each step, if the characters at the left and right pointers are equal, + then they move towards the center of the string. If the characters are not equal, then we check whether removing + the character at the left or right pointer would make the rest of the string a palindrome. If both removals fail, + then the string cannot be a valid palindrome by removing at most one character. The program returns true + if the string can be made a valid palindrome by removing at most one character, false otherwise. + + The time complexity of this algorithm is O(n), where n is the length of the input string. + The space complexity is O(1), as we only use constant extra space to store a few variables. + +*/ +package main + +// isPalindromeWithRemoval checks if a string can be converted into a palindrome +// by removing at most one character +func isPalindromeWithRemoval(s string) bool { + // Define two pointers to check the string from both ends + i, j := 0, len(s)-1 + + for i < j { + // If characters at the pointers are not equal + if s[i] != s[j] { + // Check if removing a character from the left pointer makes it a palindrome + leftStr := s[:i] + s[i+1:j+1] + if isPalindrome(leftStr) { + return true + } + // Check if removing a character from the right pointer makes it a palindrome + rightStr := s[:i] + s[i:j] + if isPalindrome(rightStr) { + return true + } + // If neither option works, it cannot be converted into a palindrome by removing at most one character + return false + } + // Move the pointers towards the middle of the string + i++ + j-- + } + return true +} + +// isPalindrome checks if a string is a palindrome +func isPalindrome(s string) bool { + // Define two pointers to check the string from both ends + i, j := 0, len(s)-1 + + for i < j { + // If characters at the pointers are not equal, it is not a palindrome + if s[i] != s[j] { + return false + } + // Move the pointers towards the middle of the string + i++ + j-- + } + return true +} diff --git a/Strings/valid_pallindrome2.java b/Strings/valid_pallindrome2.java new file mode 100644 index 00000000..0c864231 --- /dev/null +++ b/Strings/valid_pallindrome2.java @@ -0,0 +1,56 @@ +// Check whether a string can be a valid palindrome by removing at most one character from it +/* + The given Go program takes a string as input and checks if it can be a valid palindrome by removing at most one + character from it. The program starts by defining two pointers, left and right, that move towards each other from + opposite ends of the input string. At each step, if the characters at the left and right pointers are equal, + then they move towards the center of the string. If the characters are not equal, then we check whether removing + the character at the left or right pointer would make the rest of the string a palindrome. If both removals fail, + then the string cannot be a valid palindrome by removing at most one character. The program returns true + if the string can be made a valid palindrome by removing at most one character, false otherwise. + + The time complexity of this algorithm is O(n), where n is the length of the input string. + The space complexity is O(1), as we only use constant extra space to store a few variables. + +*/ +public static boolean isPalindromeWithRemoval(String s) { + // Define two pointers to check the string from both ends + int i = 0, j = s.length() - 1; + + while (i < j) { + // If characters at the pointers are not equal + if (s.charAt(i) != s.charAt(j)) { + // Check if removing a character from the left pointer makes it a palindrome + String leftStr = s.substring(0, i) + s.substring(i + 1); + if (isPalindrome(leftStr)) { + return true; + } + // Check if removing a character from the right pointer makes it a palindrome + String rightStr = s.substring(0, j) + s.substring(j + 1); + if (isPalindrome(rightStr)) { + return true; + } + // If neither option works, it cannot be converted into a palindrome by removing at most one character + return false; + } + // Move the pointers towards the middle of the string + i++; + j--; + } + return true; +} + +public static boolean isPalindrome(String s) { + // Define two pointers to check the string from both ends + int i = 0, j = s.length() - 1; + + while (i < j) { + // If characters at the pointers are not equal, it is not a palindrome + if (s.charAt(i) != s.charAt(j)) { + return false; + } + // Move the pointers towards the middle of the string + i++; + j--; + } + return true; +} diff --git a/Strings/valid_pallindrome2.js b/Strings/valid_pallindrome2.js new file mode 100644 index 00000000..1c04181c --- /dev/null +++ b/Strings/valid_pallindrome2.js @@ -0,0 +1,75 @@ +// Check whether a string can be a valid palindrome by removing at most one character from it +/* + The validPalindrome function takes a string s as input and returns true if the string can be a valid palindrome by removing + at most one character, and false otherwise. It works by initializing two pointers left and right to the left and right ends + of the string, respectively. It then moves the pointers inward while checking if the characters at the left and right indices + are equal. If they are not equal, it removes the left or right character, creates a new string, and checks if the new string + is a palindrome using the isPalindrome helper function. If the new string is a palindrome, it returns true. If not, it + continues moving the pointers inward and repeating the process. If the pointers meet, the string is already a palindrome, + or it can be made into one by removing at most one character, so it returns true. + + The isPalindrome function takes a string s as input and returns true if the string is a palindrome, and false otherwise. + It works by initializing two pointers left and right to the left and right ends of the string, respectively. It then moves + the pointers inward while checking if the characters at the + + Time complexity: O(n) : We iterate over the input string once with two pointers, which takes O(n) time. + Space complexity: O(1) : We only use a few constant amount of variables to keep track of the pointers, left and right. + Therefore, the space complexity is O(1). +*/ +/** + * Checks whether a string can be a valid palindrome by removing at most one character. + * @param {string} s - The input string to check. + * @return {boolean} - Returns true if the string can be a valid palindrome, false otherwise. + */ +function validPalindrome(s) { + let left = 0; // Pointer to the left end of the string. + let right = s.length - 1; // Pointer to the right end of the string. + + while (left < right) { + if (s[left] !== s[right]) { + // If characters at left and right indices are not equal. + // Check if string without left index is a palindrome. + let str1 = s.slice(0, left) + s.slice(left + 1); + if (isPalindrome(str1)) { + return true; + } + // Check if string without right index is a palindrome. + let str2 = s.slice(0, right) + s.slice(right + 1); + if (isPalindrome(str2)) { + return true; + } + // If string without left or right index is not a palindrome, return false. + return false; + } + // Move pointers inward if characters at left and right indices are equal. + left++; + right--; + } + + // If we get here, the string is already a palindrome, or it can be made into one + // by removing at most one character, so return true. + return true; +} + +/** + * Checks whether a string is a palindrome. + * @param {string} s - The input string to check. + * @return {boolean} - Returns true if the string is a palindrome, false otherwise. + */ +function isPalindrome(s) { + let left = 0; // Pointer to the left end of the string. + let right = s.length - 1; // Pointer to the right end of the string. + + while (left < right) { + if (s[left] !== s[right]) { + // If characters at left and right indices are not equal, return false. + return false; + } + // Move pointers inward if characters at left and right indices are equal. + left++; + right--; + } + + // If we get here, the string is a palindrome, so return true. + return true; +} diff --git a/Strings/valid_pallindrome2.py b/Strings/valid_pallindrome2.py new file mode 100644 index 00000000..b90d9ed9 --- /dev/null +++ b/Strings/valid_pallindrome2.py @@ -0,0 +1,44 @@ +// Check whether a string can be a valid palindrome by removing at most one character from it +''' + Explanation: + The function valid_palindrome takes a string s as input and returns a boolean indicating whether the + string can be a valid palindrome by removing at most one character from it. + + To check if the string can be a valid palindrome, we start by iterating over the string from both the + left and right ends at the same time. If we encounter two characters that are not the same, we need to + check if removing one of them would make the string a palindrome. We try removing the left character + first and create two new substrings - one with the left character removed and one with the right character removed. + We then check if either of the substrings is a palindrome. If either of the substrings is a palindrome, + then the original string can be a palindrome by removing the corresponding character. If neither of + the substrings is a palindrome, then the original string cannot be a palindrome even by removing a character. + + If we have checked the whole string and haven't returned yet, it means that the original string is a palindrome. + + The time complexity of the given function is O(n), where n is the length of the input string. + This is because we are iterating through the string only once and performing constant-time operations. + + The space complexity of the function is also O(n), where n is the length of the input string. + This is because we are using a constant amount of extra space to keep track of the counts of characters. + The size of this extra space does not depend on the length of the input string. +''' +# using two-pointer Technique +def valid_palindrome(s: str) -> bool: + left, right = 0, len(s) - 1 + + # iterate over the string from left and right at the same time + while left < right: + # if the characters at the left and right indices are not the same + if s[left] != s[right]: + # we need to check if removing one of them makes the string a palindrome + # we try removing the left character first + left_removed = s[left:right] + right_removed = s[left+1:right+1] + # if either of the substrings after removal is a palindrome, then the original string is also a palindrome + return left_removed == left_removed[::-1] or right_removed == right_removed[::-1] + + # move to the next pair of characters + left += 1 + right -= 1 + + # if we have checked the whole string and haven't returned yet, it's a palindrome + return True diff --git a/Strings/well_formed_parentheses.cpp b/Strings/well_formed_parentheses.cpp new file mode 100644 index 00000000..f8eca0a6 --- /dev/null +++ b/Strings/well_formed_parentheses.cpp @@ -0,0 +1,34 @@ +/* + Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. + + Example 1: + Input: n = 3 + Output: ["((()))","(()())","(())()","()(())","()()()"] + + Example 2: + Input: n = 1 + Output: ["()"] + + Constraints: 1 <= n <= 8 +*/ +#include +class Solution { +public: +vector ans; +int N; + void solve(string s,int count) + { + if(s.size()==2*N) + ans.push_back(s); + + if(count generateParenthesis(int n) { + string st; + solve(st,0); + return ans; + } +}; \ No newline at end of file diff --git a/Strings/well_formed_parentheses.java b/Strings/well_formed_parentheses.java new file mode 100644 index 00000000..4bfe3d7a --- /dev/null +++ b/Strings/well_formed_parentheses.java @@ -0,0 +1,117 @@ +/** + + Time Complexity: O(2^n * n), Space Complexity: O(n). + + Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. + + Example 1: + Input: n = 3 + Output: ["((()))","(()())","(())()","()(())","()()()"] + + + Example 2: + Input: n = 1 + Output: ["()"] + + + Constraints: + + 1 <= n <= 8 + + +**/ + + +class Solution { + + + public List generateParenthesis(int n) { + + ArrayList validParenthesis = new ArrayList(); + /** + We need to generate every n pair valid parenthses. + + What we are going to do is try every possible combination + if we have a 2 pair parenthes p: ???? + each question mark could be '(' or ')'. + ex: ()() or (()) or ((() + so we have a total of (2 ^ (2 * n)) combination. + + if we say that an open bracket '(' can be expressed as 0 + and a closed bracket ')' can be expressed 1. + + 0000 -> (((( + 0001 -> ((() + 0010 -> (()( + 0011 -> (()) + 0100 -> ()(( + 0101 -> ()() + . + . + . + . + . + 1111 -> )))) + + **/ + for (int i = 0; i < (1 << (2 * n)); ++i) + { + String aParenthesis = ""; + for(int j = 0; j < 2 * n; ++j) + { + if(((i >> j) & 1) == 1) + { + aParenthesis += '('; + } + else + aParenthesis += ')'; + } + if(isValid(aParenthesis)) + validParenthesis.add(aParenthesis); + } + + return validParenthesis; + } + + + /** + Function: isValid check if parenthesis is balanced or not. + + + For each open bracket we find we pushed it to the stack. + + If We find a closed bracket we check the top of the stack + if it is an open bracket that's mean we find a corrsponding closing bracket so, we removed it. + + else the top of the stack is a closed bracket that means there is no open bracket that matches that closed bracket, + that's means the parenthesis is NOT valid or balanced so, we return false. + + + At the end if we find that the stack is empty that's mean that each open breack has a corrsponding closed bracket, + so, we return true otherwise we return false. + **/ + + public boolean isValid(String s) { + + int n = s.length(); + Stack st = new Stack(); + + for(int i = 0; i < n; ++i) + { + char ch = s.charAt(i); + if( ch == '(') + { + st.push(ch); + } + else + { + if(!st.empty() && st.peek() == '(') + st.pop(); + else + return false; + } + } + + return st.empty(); + } +} diff --git a/Strings/zigzag_conversion.cpp b/Strings/zigzag_conversion.cpp new file mode 100644 index 00000000..ec9a6ffc --- /dev/null +++ b/Strings/zigzag_conversion.cpp @@ -0,0 +1,56 @@ +/* + The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility) + P A H N + A P L S I I G + Y I R + And then read line by line: "PAHNAPLSIIGYIR" + + Write the code that will take a string and make this conversion given a number of rows: + string convert(string s, int numRows); + + Example 1: + Input: s = "PAYPALISHIRING", numRows = 3 + Output: "PAHNAPLSIIGYIR" + + Example 2: + Input: s = "PAYPALISHIRING", numRows = 4 + Output: "PINALSIGYAHRPI" + Explanation: + P I N + A L S I G + Y A H R + P I + + Example 3: + Input: s = "A", numRows = 1 + Output: "A" + + Constraints:\ + 1 <= s.length <= 1000 + s consists of English letters (lower-case and upper-case), ',' and '.'. + 1 <= numRows <= 1000 +*/ + +#include +class Solution { +public: + string convert(string s, int numRows) { + if(numRows <= 1) return s; + string *A = new string[numRows]; + int n = s.size(); + int row = 0; + int step = 1; + for(int i = 0; i < n; i++){ + A[row].push_back(s[i]); + if(row == 0) step = 1; + else if(row == numRows - 1) step = -1; + + row = row + step; + } + string ans = ""; + for(int i = 0; i < numRows; i++){ + ans += A[i]; + } + return ans; + } +}; \ No newline at end of file diff --git a/Test/hello.js b/Test/hello.js deleted file mode 100644 index ad86b2ec..00000000 --- a/Test/hello.js +++ /dev/null @@ -1 +0,0 @@ -console.log("hello"); \ No newline at end of file diff --git a/Test/hello.py b/Test/hello.py deleted file mode 100644 index 8405850b..00000000 --- a/Test/hello.py +++ /dev/null @@ -1 +0,0 @@ -print("Testing!") diff --git a/Trees/Binary Search Trees/Kth_Largest_Value_In_BST.py b/Trees/Binary Search Trees/Kth_Largest_Value_In_BST.py new file mode 100644 index 00000000..d5254bad --- /dev/null +++ b/Trees/Binary Search Trees/Kth_Largest_Value_In_BST.py @@ -0,0 +1,88 @@ +# Name : Manmay Ghosh +# Github username : ManmayGhosh +# Repository name : data-structures-and-algorithms +# Problem : Kth Largest Value In BST in Python +# Issue Number : #1225 + +# Explanation of the below Python code : + +# 1. The idea is to do reverse inorder traversal of BST. Keep a count of nodes visited. +# 2. The reverse inorder traversal traverses all nodes in decreasing order, i.e, visit the right node then centre then left and continue traversing the nodes recursively. +# 3. While doing the traversal, keep track of the count of nodes visited so far. +# 4. When the count becomes equal to k, stop the traversal and print the key. + +# -------------------------------------------------------------------------//Python code begins here------------------------------------------------------------------------ + +class Node: + # Constructor to create a new node + def __init__(self, val): + self.key = val + self.left = None + self.right = None + +# This function will find k'th largest element in a tree. +def kthLargestUtil(root, k, c): + + # Base cases, c[0] >= k is important to avoid unnecessary recursive calls + if root == None or c[0] >= k: + return + + # Follow reverse inorder traversal so that the largest element is visited first + kthLargestUtil(root.right, k, c) + + # Increment count of visited nodes + c[0] += 1 + + # If c becomes k now, then this is the k'th largest + if c[0] == k: + print("K'th largest element is",root.key) + return + + # Recurssion for left subtree + kthLargestUtil(root.left, k, c) + + +# Function to find k'th largest element +def kthLargest(root, k): + + # Initialize count of nodes visited as 0 + c = [0] + + # Note that c is passed by reference + kthLargestUtil(root, k, c) + + +# Function to insert a new node in BST */ +def insert(node, key): + + if node == None: + return Node(key) + + if key < node.key: + node.left = insert(node.left, key) + elif key > node.key: + node.right = insert(node.right, key) + + return node + +# Driver Code +if __name__ == '__main__': + root = None + root = insert(root, 50) + insert(root, 30) + insert(root, 20) + insert(root, 40) + insert(root, 70) + insert(root, 60) + insert(root, 80) + k = int(input("Enter the Kth element(should be greater than 1)")) + kthLargest(root, k) + + + + +# Complexity Analysis: +# Time Complexity: O(n). +# In worst case the code can traverse each and every node of the tree if the k given is equal to n (total number of nodes in the tree). Therefore overall time complexity is O(n). +# Auxiliary Space: O(h). +# Max recursion stack of height h at a given time. diff --git a/Trees/Binary Search Trees/Kth_largest_BST.cpp b/Trees/Binary Search Trees/Kth_largest_BST.cpp new file mode 100644 index 00000000..eb9b8bc2 --- /dev/null +++ b/Trees/Binary Search Trees/Kth_largest_BST.cpp @@ -0,0 +1,167 @@ +/*TASK + Binary Search Tree : Get kth largest element in a BST + A BST is a tree structure that is sorted by the nodes' data. + Following rules are true for every picked node in a BST: + -> all nodes to the left have data that is less than the data of the picked node + -> all nodes to the right have data that is greater than the the data of the picked node + -> all nodes have 2 children (left and right), but they can be empty (null) too. +*/ + +/*SAMPLE DATA + Take a look on the following BST + 3 + 2 7 + 1 5 9 + 4 + + For k = 3, we should get 5 as an output, because 5 is the 3. largest element in the BST +*/ + +/*APPROACH + an recursive algorithm that is going to get to the nodes in descending order. + the visited nodes get appended to an array and if the kth node is visited the recursion will be left + 1. start with the root node of a BST + 2. for each node that is reached, do the first that is possible from the following: + "if possible" means that the node has a valid child in that direction that is not already visited + 1. go to the right if possible to get to the far right node which also has the largest data + 2. go to the left if possible to get larger data than going back + (If the algorithm goes back from the current node, all right nodes will be already visted, that means that we will land on a lower data node) + 3. go back + + If we cannot do 1. we save this node as visited. + The kth visited node is also the kth largest. + if this node is reached it is getting saved in a class member variable. + In each recursion call this variable gets checked. If the node is set (we already found the kth largest node), + the recursion step returns before doing anything. + that is how the programm will get out of the recursion fast after finding the target node. +3. if the member variable is set, that is the kth largest node, if its empty there is no kth largest node (out of range) + +SAMPLE + 1. first we start with the root 3 + 2. we can go right, so we will do so (node 7) + 3. we can go right (node 9) + 4. we cannot go right, we save this in a visited node list (node 9, list 9) + 5. we cannot go left, we go back (node 7) + 6. we cannot go right, because 9 is already visited, we save this in visited nodes (node 7, list 9,7) + 7. we can go left (node 5) + 8. we cannot go right, we save this in visited nodes (node 5, list 9,7,5) + 9. the list's size is equal to k, we will set the member variable to the last saved node 5 (node 5, list 9,7,5, member 5) + 10. we have a member set, we go back (node 7) + 11. we have a member set, we go back (node 3) + 12. we have a member set, we go back (leaving recursion) + 13. member variable holds 5, which is the 3. largest number in the BST +*/ + +/*COMPLEXITY + the algorithm has to go to the far right in h steps, where h is the height of the tree + and additionally k steps to get to the kth largest value. to break out the programm needs additionally h steps + Time complexity is O(2*h+k). The complexity class is O(n), because h is linear in a worst case (skewed to one side) and k is also linear. + Space complexity is O(h), because the algorithm needs to store a maximum of h recursion calls. + The complexity class is O(n), because h is linear in a worst case (skewed to one side). + But in a balanced tree it is only O(log(n)), because h is log(n), where n is the number of nodes in the balanced tree. +*/ +#include +#include +#include +class Node{ + //Node class holds one node of the tree + //every node holds some data (int) and has two children + //an empty children gets represented with nullptr +public: + int data; + Node* left; + Node* right; + +public: + Node(int item){ + //init the node with no children + data = item; + left = nullptr; + right = nullptr; + } + virtual ~Node(){ + /* + IMPORTANT to avoid memory leaks + a virtual constructor is required to correctly delete all children (and children of children) + checking if the child is not empty than calling the children's destructor + */ + if(left != nullptr){delete left;} + if(right != nullptr){delete right;} + }; +}; + +class BinaryTree{ + //holds the full tree (and tree functionalities) +private: + Node* kth_largest; //that is the member variable that our algorithm uses to save the target node + std::vector visited_nodes; //the list of visited nodes +public: + Node* root; //pointer to the root node of the BST + +private: + void traverseRL(Node* node, int k){ + //this method is traversing the BST from the largest to the smallest element + //this algorithm is taking the root node and the k + if(this->kth_largest != nullptr){ //are we ready? + return; + } + if(node->right != nullptr){ //can we go to the right? + traverseRL(node->right, k); //go to the right + } + this->visited_nodes.push_back(node); //cannot go more right, add to visited nodes + if(this->visited_nodes.size() == k){ //if the visited nodes list is long enough to hold the target node + this->kth_largest = node; //save this node in the member variable + return; //start to break down the recursion + } + if(node->left != nullptr){ //can we go to the left? + traverseRL(node->left, k); //go to the left + } + return; //this node is already dealt with + } + +public: + BinaryTree(int root_data){ + //creates a binary tree with one node (root) that gets some given data + kth_largest = nullptr; + root = new Node(root_data); + } + Node* insertNode(Node* node, int data){ //insert a new node on the right position (remember the BST rules) + if(node == nullptr){ //if we reach an empty spot we set the new node there + return new Node(data); + } + if(data <= node->data){ //data is smaller than the data of the current node + node->left = insertNode(node->left, data); //we have to got to the left + } + else{ //data is greater than the data of the current node + node->right = insertNode(node->right, data); //we have to got to the right + } + return node; //get the node back to the previous recursion layer + } + + Node* getKthLargest(int k){ //get the kth largest data node + this->kth_largest = nullptr; //init the member variables that are needed for the algorithm + this->visited_nodes.clear(); + this->traverseRL(this->root, k); //perform the search + if(this->kth_largest == nullptr){ //kth largest element cannot be found (out of range) + std::cout << "There is no " << k << ". largest element in this tree" << std::endl; + std::cout << "Valid k's for a tree are numbers between 1 and the number of nodes inclusive"; + return nullptr; + }else{ + return this->kth_largest; //returrn the kth largest node + } + } +}; + +int main() { + //sample + int k = 3; + BinaryTree tree = BinaryTree(3); //building up the sample BST + tree.insertNode(tree.root, 7); + tree.insertNode(tree.root, 2); + tree.insertNode(tree.root, 5); + tree.insertNode(tree.root, 9); + tree.insertNode(tree.root, 1); + tree.insertNode(tree.root, 4); + std::cout << tree.getKthLargest(k)->data << std::endl; //print the result + return 0; +} \ No newline at end of file diff --git a/Trees/Binary Search Trees/Kth_largest_BST.java b/Trees/Binary Search Trees/Kth_largest_BST.java new file mode 100644 index 00000000..0f268db8 --- /dev/null +++ b/Trees/Binary Search Trees/Kth_largest_BST.java @@ -0,0 +1,101 @@ +/** + * A class to represent a node in the BST. + */ +class Node { + int data; + Node left, right; + + Node(int data) { + this.data = data; + left = right = null; + } +} + +/** + * A class to represent a binary search tree. + */ +class BST { + Node root; + + BST() { + root = null; + } + + /** + * Function to find the kth largest element in the BST. + Time complexity: O(n), where n is the number of nodes in the BST. This is because we need to visit every node in the worst case to find the kth largest element. + Space complexity: O(h), where h is the height of the BST. This is because we're recursively traversing the tree using a call stack, which can have at most h frames in it + + The kthLargest() function takes an integer k as input and returns the kth largest element in the BST. It first creates an empty result list, calls the kthLargestHelper() function to traverse the BST in reverse inorder, and collects the kth largest element in the result list. It then returns the last element of the result list, which is the kth largest element in the BST. + + Time complexity: O(k), since we're only collecting up to k elements in the result list. + pace complexity: O(h), where h is the height of the BST. This is because we're recursively traversing the tree using a call stack, which can have at most h frames in it + */ + public int kthLargest(int k) { + List result = new ArrayList<>(); + kthLargestHelper(root, result, k); + return result.get(result.size() - 1); + } + + + /**The kthLargestHelper() function is a helper function to traverse the BST in reverse inorder and collect the kth largest element in the result list. It first recursively calls itself on the right child of the current node, then adds the current node's `data */ + private void kthLargestHelper(Node node, List result, int k) { + if (node == null || result.size() == k) { + return; + } + kthLargestHelper(node.right, result, k); + if (result.size() < k) { + result.add(node.data); + } + kthLargestHelper(node.left, result, k); + } + + /** + * Function to insert a node in the BST. + Time complexity: O(h), where h is the height of the BST. This is because we're traversing the tree from the root to the correct position for the new node. + Space complexity: O(1), since we're only creating a new node and not using any extra memory. + */ + public void insert(int data) { + root = insertHelper(root, data); + } + + /** + * Helper function to insert a node in the BST. + Time complexity: O(h), where h is the height of the BST. This is because we're traversing the tree from the root to the correct position for the new node. +S pace complexity: O(1), since we're only creating a new node and not using any extra memory. + + */ + private Node insertHelper(Node node, int data) { + if (node == null) { + node = new Node(data); + return node; + } + if (data < node.data) { + node.left = insertHelper(node.left, data); + } else if (data > node.data) { + node.right = insertHelper(node.right, data); + } + return node; + } +} + +/** + * A class to test the BST implementation. + */ +public class KthLargestBST { + public static void main(String[] args) { + BST bst = new BST(); + bst.insert(50); + bst.insert(30); + bst.insert(20); + bst.insert(40); + bst.insert(70); + bst.insert(60); + bst.insert(80); + int k = 3; + int kthLargest = bst.kthLargest(k); + System.out.println(k + "th largest element in the BST is " + kthLargest); + } +} + +/**Overall, the time complexity of finding the kth largest element in a BST using this code is O(n), and the space complexity is O(h). */ diff --git a/Trees/Binary Search Trees/Kth_largest_BST.js b/Trees/Binary Search Trees/Kth_largest_BST.js new file mode 100644 index 00000000..c9726bad --- /dev/null +++ b/Trees/Binary Search Trees/Kth_largest_BST.js @@ -0,0 +1,138 @@ +/** + The Node class represents a single node in the BST. It has three properties: value to store the node's value, left to store the reference to the left child node, and right to store the reference to the right child node. + */ + class Node { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } + } + + /** + the BST class represents the Binary Search Tree. It has one property: root to store the reference to the root node of the tree. + */ + class BST { + constructor() { + this.root = null; + } + + /** + insert(value): This function inserts a new node with the given value into the BST. It uses the insertNode helper function for recursive insertion. The time complexity is O(log n) on average (O(n) in the worst case if the tree is heavily unbalanced), and the space complexity is O(1). + * @param {number} value - The value to be inserted + */ + insert(value) { + const newNode = new Node(value); + + if (this.root === null) { + this.root = newNode; + } else { + this.insertNode(this.root, newNode); + } + } + + /** + The insertNode(node, newNode) function is a helper function used by the insert(value) method in the BST class. It recursively inserts a new node (newNode). + + Here's a breakdown of how the insertNode function works: + + * It compares the value of the newNode with the value of the node to determine whether to go left or right in the BST. + * If the value of newNode is less than the value of node, it checks if the left child of node is null. If it is, the newNode becomes the left child; otherwise, the function recursively calls itself with the left child node of node. + * If the value of newNode is greater than or equal to the value of node, it checks if the right child of node is null. If it is, the newNode becomes the right child; otherwise, the function recursively calls itself with the right child node of node. + * This process continues until a suitable position is found for the newNode in the BST. + + The time complexity of the insertNode function depends on the structure of the BST. In the average case, when the tree is balanced, the time complexity is O(log n), where n is the number of nodes in the tree. This is because at each level of the tree, the function divides the remaining nodes to be searched by half. However, in the worst case scenario, when the tree is heavily unbalanced (e.g., resembles a linked list), the time complexity becomes O(n), where n is the number of nodes in the tree. This happens when all nodes are in a straight line from the root. + + The space complexity of the insertNode function is O(log n) in the average case and O(n) in the worst case. This is due to the recursive calls that consume memory on the call stack. In the average case, the maximum number of recursive calls is limited by the height of the balanced tree, which is logarithmic to the number of nodes. In the worst case, where the tree is unbalanced, the maximum number of recursive calls is equal to the number of nodes in the tree. + + * @param {Node} node - The current node being traversed + * @param {Node} newNode - The new node to be inserted + */ + insertNode(node, newNode) { + if (newNode.value < node.value) { + if (node.left === null) { + node.left = newNode; + } else { + this.insertNode(node.left, newNode); + } + } else { + if (node.right === null) { + node.right = newNode; + } else { + this.insertNode(node.right, newNode); + } + } + } + + /** + findKthLargest(k): This function finds the Kth largest value in the BST. It first performs an in-order traversal of the tree to retrieve a sorted array of values. If K is larger than the number of nodes in the tree, it returns null. Otherwise, it returns the Kth largest value from the sorted array. The time complexity of this function is O(n), where n is the number of nodes in the tree. The space complexity is O(n) since it stores all the values in the array during the traversal. + * @param {number} k - The Kth largest value to find + * @returns {number|null} - The Kth largest value, or null if it doesn't exist + */ + findKthLargest(k) { + if (k <= 0) { + throw new Error('k should be a positive integer'); + } + + const sortedValues = []; + this.inOrderTraversal(this.root, sortedValues); + + if (k > sortedValues.length) { + return null; + } + + return sortedValues[sortedValues.length - k]; + } + + /** + inOrderTraversal(node, values): This is a helper function that performs an in-order traversal of the BST and stores the sorted values in the given array. It recursively visits the left subtree, then the current node, and finally the right subtree. The time complexity of this function is O(n), where n is the number of nodes in the tree, as it needs to visit each node exactly once. The space complexity is O(n) since it uses the array to store the values. + * @param {Node} node - The current node being traversed + * @param {Array} values - The array to store the sorted values + */ + inOrderTraversal(node, values) { + if (node !== null) { + this.inOrderTraversal(node.left, values); + values.push(node.value); + this.inOrderTraversal(node.right, values); + } + } + } + // Sample input and imlplementation + /** + 5 + / \ + 3 7 + / \ / \ + 2 4 6 8 + + Now, let's find the 3rd largest value in the BST. + + The sorted order of the tree is [2, 3, 4, 5, 6, 7, 8]. + + The 3rd largest value is 6. + + Here's the updated tree with the 3rd largest value marked: + 5 + / \ + 3 7 + / \ / \ + 2 4 6* 8 + + As you can see, the 3rd largest value, 6, is marked with an asterisk (*). + + */ + // Create a new instance of BST +const bst = new BST(); + +// Insert nodes into the BST +bst.insert(5); +bst.insert(3); +bst.insert(7); +bst.insert(2); +bst.insert(4); +bst.insert(6); +bst.insert(8); + +// Find the 3rd largest value +const kthLargest = bst.findKthLargest(3); +console.log(kthLargest); // Output: 6 \ No newline at end of file diff --git a/Trees/Binary Search Trees/Validate_BST.cpp b/Trees/Binary Search Trees/Validate_BST.cpp new file mode 100644 index 00000000..4c5d33ab --- /dev/null +++ b/Trees/Binary Search Trees/Validate_BST.cpp @@ -0,0 +1,95 @@ +// VALIDATE BINARY SEARCH TREE --->> LEETCODE + + + +// Given the root of a binary tree, determine if it is a valid binary search tree (BST). + +// A valid BST is defined as follows: + +// The left +// subtree +// of a node contains only nodes with keys less than the node's key. +// The right subtree of a node contains only nodes with keys greater than the node's key. +// Both the left and right subtrees must also be binary search trees. + +// ALGORITHM--> +// Follow these steps while the current node is not null: +// Process the current node and go to its right child if it doesn't have a left child. +// Find the inorder predecessor of the current node—that is, the rightmost node in the left subtree—if the present node has a left child, and see if its value is smaller than the value of the current node. + +// If the predecessor's right child is null, go to the current node's left child and change the predecessor's right child to point to it. +// In order to restore the binary tree's original structure, reset the predecessor's right child to null, process the current node, and then move to its right child if it is already referring to the current node. + + + + + +// C++ program to check if a given tree is BST. +#include +using namespace std; + + + +struct Node { + // structure of a node of the tree. + int data; + struct Node *left, *right; + + Node(int data) + { + this->data = data; + left = right = NULL; + } +}; + + +bool validate(Node* root,long long int min , long long int max){ + if(!root) + return true; // if the root is null then it is a valid BST. it means that the tree is empty or we had reached the end of tree. + // initializing the ans variable to false (temporarily). + bool ans = false; + + // checking if the root's data is in the range of min and max. + if(root->datadata>min) + ans = true; + else + return ans; // if the root's data is not in the range of min and max then it is not a valid BST. hence returning false. + + // changing min and max for the left and right subtree. and checking for the left and right subtree with respesct to tree root and returning the ans. + return ans && validate(root->left,min,root->data) && + validate(root->right,root->data,max); +} + + + bool isValidBST(Node* root) { + if(!root) + return true; + + // calling validate function so that it can check for the left and right subtree .. while giving the range of the values of the nodes. + return validate(root ,-9223372036854775807,9223372036854775807 ); + } + +int main() +{ + + // Initializing the tree. + struct Node* root = new Node(3); + root->left = new Node(2); + root->right = new Node(5); + root->left->left = new Node(1); + root->left->right = new Node(4); + + // calling the function to check BST. + if (isValidBST(root)) + cout << "Is BST"; + else + cout << "Not a BST"; + + return 0; +} + + + + +// T.C. O(N) +// S.C. O(N) ---> for Auxillary stack \ No newline at end of file diff --git a/Trees/Binary Search Trees/Validate_BST.java b/Trees/Binary Search Trees/Validate_BST.java new file mode 100644 index 00000000..b81e23ed --- /dev/null +++ b/Trees/Binary Search Trees/Validate_BST.java @@ -0,0 +1,59 @@ +////// Explanation: +// To validate a Binary Search Tree (BST), we need to ensure that the values of nodes in the left subtree of any node are less than the value of the node, and the values of nodes in the right subtree are greater than the value of the node. +// Additionally, the left and right subtrees themselves must also be valid BSTs. + +////// Code: +class validateBST { + public static class Node { + public int data; + public Node left, right; + + public Node(int data) { + this.data = data; + left = null; + right = null; + } + }; + + static Node prev; + + static Boolean isBST(Node root) { + + // traverse the tree in inorder fashion and // keep track of prev node + if (root == null) + return true; + + //If Left Node is not BST then return false + if (!isBST(root.left)) + return false; + + //Check if current node is less than its parent node + if (prev != null && root.data <= prev.data) + return false; + + prev = root; + return isBST(root.right); + } + + public static void main(String[] args) { + + //Creating BST + Node root = new Node(1); + root.left = new Node(-2); + root.right = new Node(3); + root.left.left = new Node(-4); + root.left.right = new Node(-1); + root.right.left = new Node(2); + root.right.right = new Node(7); + + // Function call + if (isBST(root)) + System.out.println("Is Valid BST"); + else + System.out.println("Not a Valid BST"); + } +} + +////// Complexity: +// Time Complexity: O(n) where n is number of nodes +// Space Complexity: O(1) diff --git a/Trees/Binary Search Trees/Validate_BST.js b/Trees/Binary Search Trees/Validate_BST.js new file mode 100644 index 00000000..7d2ffc41 --- /dev/null +++ b/Trees/Binary Search Trees/Validate_BST.js @@ -0,0 +1,92 @@ +/* + Write a function that takes in a potentially invalid Binary Search Tree (BST) + and returns a boolean representing whether the BST is valid. + Sample Input : + 10 + / \ + 5 15 + / \ / \ + 2 5 13 22 + / \ +1 14 + Output : True + + Explanation: + This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can + point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating + whether the tree is a valid BST or not. + + The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree + is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum + and maximum values that the current node's value can take in order to be a valid BST. + + The validateBST() method first checks whether the current node's value is within the valid range determined + by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. + + If the current node's value is within the valid range, the method then recursively calls itself on the left + and right child nodes to check whether their values are within their valid ranges. The valid range for the + left child node is defined by the minimum value and the parent node's value, while the valid range for the + right child node is defined by the parent node's value and the maximum value. + + If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree + is a valid BST. + + O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST + +*/ +class Node { + constructor(data) { + this.data = data; + this.left = null; + this.right = null; + } + } + +// isBst is a method of BST that checks if the binary search tree is valid +function isBST(root) { + let prev = null; + + function inorderTraversal(node) { + + // Base case + if (node === null) { + return true; + } + + // recursively check the left subtree, making sure all values are less than the current node's value + if (!inorderTraversal(node.left)) { + return false; + } + + // if the current node's value is outside the allowed range, then the tree is invalid + if (prev !== null && node.data <= prev.data) { + return false; + } + + prev = node; + return inorderTraversal(node.right); + } + + return inorderTraversal(root); + } + + // Create the tree + const root = new Node(100); + root.left = new Node(-2); + root.right = new Node(3); + root.left.left = new Node(-4); + root.left.right = new Node(-1); + root.right.left = new Node(2); + root.right.right = new Node(7); + + // Function call + if (isBST(root)) { + console.log("Is Valid BST"); + } else { + console.log("Not a Valid BST"); + } + + +////// Complexity: +// Time Complexity: O(n) where n is number of nodes +// Space Complexity: O(1) diff --git a/Trees/Binary Search Trees/Validate_BST.py b/Trees/Binary Search Trees/Validate_BST.py new file mode 100644 index 00000000..559b09c4 --- /dev/null +++ b/Trees/Binary Search Trees/Validate_BST.py @@ -0,0 +1,90 @@ +''' + Write a function that takes in a potentially invalid Binary Search Tree (BST) + and returns a boolean representing whether the BST is valid. + Sample Input : + 10 + / \ + 5 15 + / \ / \ + 2 5 13 22 + / \ +1 14 + Output : True + + Explanation: + This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can + point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating + whether the tree is a valid BST or not. + + The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree + is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum + and maximum values that the current node's value can take in order to be a valid BST. + + The validateBST() method first checks whether the current node's value is within the valid range determined + by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. + + If the current node's value is within the valid range, the method then recursively calls itself on the left + and right child nodes to check whether their values are within their valid ranges. The valid range for the + left child node is defined by the minimum value and the parent node's value, while the valid range for the + right child node is defined by the parent node's value and the maximum value. + + If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree + is a valid BST. + + O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST + +''' +class Node: + def __init__(self, data): + self.data = data + self.left = None + self.right = None + + +# ValidateBst is a method of BST that checks if the binary search tree is valid +def is_bst(root): + global prev + prev = None + + #validateBST is a recursive helper function that checks if the binary search tree is valid + #min is the minimum value that a node in the subtree rooted at this node can have + #max is the maximum value that a node in the subtree rooted at this node can have + def inorder_traversal(node): + global prev + + # if the current node's value is outside the allowed range, then the tree is invalid + if node is None: + return True + + # If left subtree is not BST return false + if not inorder_traversal(node.left): + return False + + # recursively check the left subtree, making sure all values are less than the current node's value + if prev is not None and node.data <= prev.data: + return False + + prev = node + return inorder_traversal(node.right) + + return inorder_traversal(root) + + +# Create the tree +root = Node(1) +root.left = Node(-2) +root.right = Node(3) +root.left.left = Node(-4) +root.left.right = Node(-1) +root.right.left = Node(2) +root.right.right = Node(7) + +# Function call +if is_bst(root): + print("Is Valid BST") +else: + print("Not a Valid BST") + +## Complexity: +# Time Complexity: O(n) where n is number of nodes +# Space Complexity: O(1) diff --git a/Trees/Binary_Search_Trees/bst.go b/Trees/Binary Search Trees/bst.go similarity index 100% rename from Trees/Binary_Search_Trees/bst.go rename to Trees/Binary Search Trees/bst.go diff --git a/Trees/Binary Search Trees/find_closest_value.cpp b/Trees/Binary Search Trees/find_closest_value.cpp new file mode 100644 index 00000000..40c769d9 --- /dev/null +++ b/Trees/Binary Search Trees/find_closest_value.cpp @@ -0,0 +1,90 @@ +/* + Write a function that takes in a Binary Search Tree (BST) and a target integer + value and returns the closest value to that target value contained in the BST. + +Sample Input : 12 + + 10 + / \ + 5 15 + / \ / \ + 2 5 13 22 + / \ +1 14 +Output : 13 + + Explanation: + + The code defines a BST (Binary Search Tree) class with member functions to find the closest value to a given target value. + The findClosestValue function is the public interface that initializes the closest value with the root value and calls + the helper function. The findClosestValueHelper function recursively traverses the tree, updating the closest value based + on the absolute difference between the target and the current node value. It then continues the search in the appropriate + subtree based on the comparison with the target value. The absDiff function calculates the absolute difference between two + integers. + + Time and Space complexity: + + Average: O(log(n)) time | O(1) space - where n is the number of nodes in the BST + Worst: O(n) time | O(1) space - where n is the number of nodes in the BST +*/ +#include + +class BST { +public: + int value; + BST* left; + BST* right; + + BST(int val) { + value = val; + left = nullptr; + right = nullptr; + } + + int findClosestValue(int target) { + // Call the helper function with the initial closest value as the root value + return findClosestValueHelper(target, value); + } + +private: + int findClosestValueHelper(int target, int closest) { + // Compare the absolute difference between the target and the current closest value + // with the absolute difference between the target and the current node value + if (std::abs(target - closest) > std::abs(target - value)) { + closest = value; + } + + // Look for the target in the left subtree if the target is smaller than the current node value + if (target < value && left != nullptr) { + return left->findClosestValueHelper(target, closest); + } + // Look for the target in the right subtree if the target is greater than the current node value + else if (target > value && right != nullptr) { + return right->findClosestValueHelper(target, closest); + } + + return closest; + } +}; + +int main() { + // Create a BST instance + BST* bst = new BST(10); + bst->left = new BST(5); + bst->right = new BST(15); + bst->left->left = new BST(2); + bst->left->right = new BST(5); + bst->right->left = new BST(13); + bst->right->right = new BST(22); + bst->left->left->left = new BST(1); + bst->right->left->right = new BST(14); + + // Find the closest value to the target + int target = 12; + int closestValue = bst->findClosestValue(target); + + // Print the result + std::cout << "The closest value to " << target << " is " << closestValue << std::endl; + + return 0; +} diff --git a/Trees/Binary Search Trees/find_closest_value.go b/Trees/Binary Search Trees/find_closest_value.go new file mode 100644 index 00000000..f65a16be --- /dev/null +++ b/Trees/Binary Search Trees/find_closest_value.go @@ -0,0 +1,63 @@ +/* + Write a function that takes in a Binary Search Tree (BST) and a target integer + value and returns the closest value to that target value contained in the BST. + +Sample Input : 12 + + 10 + / \ + 5 15 + / \ / \ + 2 5 13 22 + / \ +1 14 +Output : 13 + + Explanation: + + The code defines a BST (Binary Search Tree) class with member functions to find the closest value to a given target value. + The findClosestValue function is the public interface that initializes the closest value with the root value and calls + the helper function. The findClosestValueHelper function recursively traverses the tree, updating the closest value based + on the absolute difference between the target and the current node value. It then continues the search in the appropriate + subtree based on the comparison with the target value. The absDiff function calculates the absolute difference between two + integers. + + Time and Space complexity: + + Average: O(log(n)) time | O(1) space - where n is the number of nodes in the BST + Worst: O(n) time | O(1) space - where n is the number of nodes in the BST +*/ +package main + +type BST struct { + Value int + + Left *BST + Right *BST +} + +func (tree *BST) FindClosestValue(target int) int { + // call helper function + return tree.findClosestValue(target, tree.Value) +} + +func (tree *BST) findClosestValue(target, closest int) int { + if absDiff(target, closest) > absDiff(target, tree.Value) { + closest = tree.Value + } + // look for target in left sub tree + if target < tree.Value && tree.Left != nil { + return tree.Left.findClosestValue(target, closest) + } else if target > tree.Value && tree.Right != nil { + // // look for target in right sub tree + return tree.Right.findClosestValue(target, closest) + } + return closest +} + +func absDiff(a, b int) int { + if a > b { + return a - b + } + return b - a +} diff --git a/Trees/Binary Search Trees/find_closest_value.js b/Trees/Binary Search Trees/find_closest_value.js new file mode 100644 index 00000000..8bbdf488 --- /dev/null +++ b/Trees/Binary Search Trees/find_closest_value.js @@ -0,0 +1,78 @@ +/* + Write a function that takes in a Binary Search Tree (BST) and a target integer + value and returns the closest value to that target value contained in the BST. + +Sample Input : 12 + + 10 + / \ + 5 15 + / \ / \ + 2 5 13 22 + / \ +1 14 +Output : 13 + + Explanation: + + The code defines a BST (Binary Search Tree) class with member functions to find the closest value to a given target value. + The findClosestValue function is the public interface that initializes the closest value with the root value and calls + the helper function. The findClosestValueHelper function recursively traverses the tree, updating the closest value based + on the absolute difference between the target and the current node value. It then continues the search in the appropriate + subtree based on the comparison with the target value. The absDiff function calculates the absolute difference between two + integers. + + Time and Space complexity: + + Average: O(log(n)) time | O(1) space - where n is the number of nodes in the BST + Worst: O(n) time | O(1) space - where n is the number of nodes in the BST +*/ +class BST { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } + + findClosestValue(target) { + // Call the helper function with the initial closest value as the root value + return this._findClosestValueHelper(target, this.value); + } + + _findClosestValueHelper(target, closest) { + // Compare the absolute difference between the target and the current closest value + // with the absolute difference between the target and the current node value + if (Math.abs(target - closest) > Math.abs(target - this.value)) { + closest = this.value; + } + + // Look for the target in the left subtree if the target is smaller than the current node value + if (target < this.value && this.left) { + return this.left._findClosestValueHelper(target, closest); + } + // Look for the target in the right subtree if the target is greater than the current node value + else if (target > this.value && this.right) { + return this.right._findClosestValueHelper(target, closest); + } + + return closest; + } +} + +// Create a BST instance +const bst = new BST(10); +bst.left = new BST(5); +bst.right = new BST(15); +bst.left.left = new BST(2); +bst.left.right = new BST(5); +bst.right.left = new BST(13); +bst.right.right = new BST(22); +bst.left.left.left = new BST(1); +bst.right.left.right = new BST(14); + +// Find the closest value to the target +const target = 12; +const closestValue = bst.findClosestValue(target); + +// Print the result +console.log(`The closest value to ${target} is ${closestValue}`); diff --git a/Trees/Binary Search Trees/find_closest_value.py b/Trees/Binary Search Trees/find_closest_value.py new file mode 100644 index 00000000..cc32a3e9 --- /dev/null +++ b/Trees/Binary Search Trees/find_closest_value.py @@ -0,0 +1,71 @@ +''' + Write a function that takes in a Binary Search Tree (BST) and a target integer + value and returns the closest value to that target value contained in the BST. + +Sample Input : 12 + + 10 + / \ + 5 15 + / \ / \ + 2 5 13 22 + / \ +1 14 +Output : 13 + + Explanation: + + The code defines a BST (Binary Search Tree) class with member functions to find the closest value to a given target value. + The findClosestValue function is the public interface that initializes the closest value with the root value and calls + the helper function. The findClosestValueHelper function recursively traverses the tree, updating the closest value based + on the absolute difference between the target and the current node value. It then continues the search in the appropriate + subtree based on the comparison with the target value. The absDiff function calculates the absolute difference between two + integers. + + Time and Space complexity: + + Average: O(log(n)) time | O(1) space - where n is the number of nodes in the BST + Worst: O(n) time | O(1) space - where n is the number of nodes in the BST +''' +class BST: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + + def find_closest_value(self, target): + # Call the helper function with the initial closest value as the root value + return self._find_closest_value_helper(target, self.value) + + def _find_closest_value_helper(self, target, closest): + # Compare the absolute difference between the target and the current closest value + # with the absolute difference between the target and the current node value + if abs(target - closest) > abs(target - self.value): + closest = self.value + + # Look for the target in the left subtree if the target is smaller than the current node value + if target < self.value and self.left: + return self.left._find_closest_value_helper(target, closest) + # Look for the target in the right subtree if the target is greater than the current node value + elif target > self.value and self.right: + return self.right._find_closest_value_helper(target, closest) + + return closest + +# Create a BST instance +bst = BST(10) +bst.left = BST(5) +bst.right = BST(15) +bst.left.left = BST(2) +bst.left.right = BST(5) +bst.right.left = BST(13) +bst.right.right = BST(22) +bst.left.left.left = BST(1) +bst.right.left.right = BST(14) + +# Find the closest value to the target +target = 12 +closest_value = bst.find_closest_value(target) + +# Print the result +print(f"The closest value to {target} is {closest_value}") diff --git a/Trees/Binary_Search_Trees/BST_insert_into_bst.cpp b/Trees/Binary Search Trees/insert_into_bst.cpp similarity index 100% rename from Trees/Binary_Search_Trees/BST_insert_into_bst.cpp rename to Trees/Binary Search Trees/insert_into_bst.cpp diff --git a/Trees/Binary Search Trees/kth_largest.go b/Trees/Binary Search Trees/kth_largest.go new file mode 100644 index 00000000..3fb1197b --- /dev/null +++ b/Trees/Binary Search Trees/kth_largest.go @@ -0,0 +1,108 @@ +// Kth largest in BST +package main + +// Approach 1: Using Brute Force +// Since Inorder traversal of BSt always comes in sorted order, we can directly access kth from end +type BST struct { + Value int + + Left *BST + Right *BST +} + +func FindKthLargestValueInBst(tree *BST, k int) int { + sortedValues := make([]int, 0) + inOrderTraverse(tree, &sortedValues) + return sortedValues[len(sortedValues) - k] +} + +func inOrderTraverse(tree *BST, sortedValues *[]int) { + if tree.Left != nil { + inOrderTraverse(tree.Left, sortedValues) + } + *sortedValues = append(*sortedValues, tree.Value) + if tree.Right != nil { + inOrderTraverse(tree.Right, sortedValues) + } +} + + +// Approach 2: Using Reverse InOrder Traversal +/* + The given code implements a function to find the Kth largest value in a Binary Search Tree (BST). Here's how it works: + + 1. The `BST` struct represents a node in the BST, with a `Value` field and pointers to the left and right child nodes. + + 2. The `treeInfo` struct is a helper struct used to store information during the traversal of the BST. It contains two fields: `numberOfNodesVisited` to keep track of the number of nodes visited, and `latestVisitedNodeValue` to store the value of the latest visited node. + + 3. The `FindKthLargestValueInBst` function takes the root of the BST and the value of K as input and returns the Kth largest value in the BST. + + 4. Inside the `FindKthLargestValueInBst` function, a `treeInfo` instance is created to track the traversal progress. + + 5. The `reverseInOrderTraverse` function is called to perform a reverse in-order traversal of the BST. It starts from the right child, then visits the current node, and finally visits the left child. + + 6. In the `reverseInOrderTraverse` function, the base case is checked: if the current node is `nil` or the desired Kth largest value has already been found (i.e., `numberOfNodesVisited` is equal to K), the function returns. + + 7. The `reverseInOrderTraverse` function is recursively called on the right child node, which traverses the BST in descending order. + + 8. After the right subtree is traversed, the function checks if `numberOfNodesVisited` is still less than K. If so, it increments `numberOfNodesVisited`, assigns the current node's value to `latestVisitedNodeValue`, and recursively calls the function on the left child node. + + 9. The reverse in-order traversal ensures that the largest values are visited first, so when `numberOfNodesVisited` reaches K, the value stored in `latestVisitedNodeValue` will be the Kth largest value in the BST. + + 10. Finally, the Kth largest value is returned by the `FindKthLargestValueInBst` function. + + The code efficiently finds the Kth largest value in a BST by performing a reverse in-order traversal, keeping track of the + number of visited nodes and the latest visited node value. + + The time complexity of the algorithm is O(H + K), where H is the height of the BST and K is the input value. + + The space complexity of the code is O(h), where h is the height of the BST. + During the execution of the FindKthLargestValueInBst function, the space usage is primarily determined by the recursive + calls to the reverseInOrderTraverse function. Each recursive call adds a new frame to the call stack, + consuming additional memory. + + In the worst case scenario, where the BST is skewed and has a height of h (resembling a linked list), the space + complexity will be O(h). This is because the function will make h recursive calls before reaching the base case. + + However, in a balanced BST where the height is log(N), where N is the number of nodes in the tree, the space + complexity will be O(log(N)). + + It's important to note that the space complexity only accounts for the additional space used by the recursive calls + and the call stack. The space required to store the BST itself is not considered in this analysis as it is part of + the input data. + +*/ +// Helper struct to store traversal information +type treeInfo struct { + numberOfNodesVisited int + latestVisitedNodeValue int +} + +// FindKthLargestValueInBst finds the Kth largest value in the BST +func FindKthLargestValueInBst2(tree *BST, k int) int { + // Create treeInfo to track traversal progress + treeInfo := treeInfo{0, -1} + reverseInOrderTraverse(tree, k, &treeInfo) + return treeInfo.latestVisitedNodeValue +} + +// reverseInOrderTraverse performs reverse in-order traversal of the BST +func reverseInOrderTraverse(tree *BST, k int, treeInfo *treeInfo) { + // Base case: if current node is nil or Kth largest value found, return + if tree == nil || treeInfo.numberOfNodesVisited == k { + return + } + + // Traverse the right subtree + reverseInOrderTraverse(tree.Right, k, treeInfo) + + // Check if Kth largest value has been found + if treeInfo.numberOfNodesVisited < k { + // Increment the count of visited nodes and update the latest visited node value + treeInfo.numberOfNodesVisited++ + treeInfo.latestVisitedNodeValue = tree.Value + + // Traverse the left subtree + reverseInOrderTraverse(tree.Left, k, treeInfo) + } +} \ No newline at end of file diff --git a/Trees/Binary Search Trees/min_height_BST.cpp b/Trees/Binary Search Trees/min_height_BST.cpp new file mode 100644 index 00000000..0bc84eb9 --- /dev/null +++ b/Trees/Binary Search Trees/min_height_BST.cpp @@ -0,0 +1,164 @@ +/* + + Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. + The function should minimize the height of the BST. + Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] + Output: + + 10 + / \ + 2 14 + / \ / \ + 1 5 13 15 + \ \ + 7 22 + + Explanation: + + The given code is used to construct a minimum height binary search tree (BST) from a sorted array. + The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. + + The MinHeightBST function serves as a wrapper function that initializes the construction process by calling + the constructMinHeightBST helper function. + + The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, + a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. + + The function follows these steps: + + Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. + + Calculate the mid index as the midpoint of the current range (start and end). + + Get the value from the array at the mid index. + + If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. + Otherwise, insert the value into the existing bst using the Insert method. + + Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. + This constructs the left subtree. + + Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. + This constructs the right subtree. + + Finally, return the bst which represents the constructed minimum height BST. + + The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and + Left and Right fields to point to the left and right child nodes, respectively. + + The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to + insert the value based on the comparison with the current node's value and updates the left or right child accordingly. + + Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the + array and inserting the mid-value into the BST, ensuring a balanced structure. + + Time Complexity: + + The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like + operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where + n is the number of elements in the array. + + The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, + as it involves traversing the height of the BST to find the appropriate position to insert the value. + Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), + where n is the number of elements in the array. + + Space Complexity: + + The space complexity is determined by the stack space used during recursive calls and the space required + to store the BST. + + The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of + the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of + elements (n), the space complexity becomes O(n). + + The space required to store the BST is also O(n) in the worst case since each element in the array is + inserted into the BST. + + Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. + In summary, the time complexity is O(n log n) and the space complexity is O(n). + + + +*/ +#include +#include + +struct TreeNode { + int value; + TreeNode* left; + TreeNode* right; + + TreeNode(int x) : value(x), left(nullptr), right(nullptr) {} +}; + +TreeNode* minHeightBST(std::vector& array) { + // Call helper method with start index, end index, and a nullptr root node + return constructMinHeightBST(array, nullptr, 0, array.size() - 1); +} + +TreeNode* constructMinHeightBST(std::vector& array, TreeNode* bst, int start, int end) { + // Base case + if (end < start) { + return nullptr; + } + int mid = (start + end) / 2; + int value = array[mid]; + // If the BST is empty, create a new root node + if (bst == nullptr) { + bst = new TreeNode(value); + } else { + // Insert the value into the existing BST + bst->insert(value); + } + // Recursively construct the left and right subtrees + bst->left = constructMinHeightBST(array, bst->left, start, mid - 1); + bst->right = constructMinHeightBST(array, bst->right, mid + 1, end); + return bst; +} + +class BST { +public: + int value; + BST* left; + BST* right; + + BST(int x) : value(x), left(nullptr), right(nullptr) {} + + void insert(int value) { + if (value < this->value) { + if (left == nullptr) { + left = new BST(value); + } else { + left->insert(value); + } + } else { + if (right == nullptr) { + right = new BST(value); + } else { + right->insert(value); + } + } + } +}; + +int main() { + std::vector array = {1, 2, 3, 4, 5, 6, 7}; + TreeNode* root = minHeightBST(array); + + // Printing the BST is left as an exercise + + // Clean up memory + deleteTree(root); + + return 0; +} + +void deleteTree(TreeNode* root) { + if (root == nullptr) { + return; + } + deleteTree(root->left); + deleteTree(root->right); + delete root; +} diff --git a/Trees/Binary Search Trees/min_height_BST.go b/Trees/Binary Search Trees/min_height_BST.go new file mode 100644 index 00000000..17a004af --- /dev/null +++ b/Trees/Binary Search Trees/min_height_BST.go @@ -0,0 +1,131 @@ +/* + + Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. + The function should minimize the height of the BST. + Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] + Output: + + 10 + / \ + 2 14 + / \ / \ + 1 5 13 15 + \ \ + 7 22 + + Explanation: + + The given code is used to construct a minimum height binary search tree (BST) from a sorted array. + The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. + + The MinHeightBST function serves as a wrapper function that initializes the construction process by calling + the constructMinHeightBST helper function. + + The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, + a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. + + The function follows these steps: + + Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. + + Calculate the mid index as the midpoint of the current range (start and end). + + Get the value from the array at the mid index. + + If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. + Otherwise, insert the value into the existing bst using the Insert method. + + Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. + This constructs the left subtree. + + Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. + This constructs the right subtree. + + Finally, return the bst which represents the constructed minimum height BST. + + The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and + Left and Right fields to point to the left and right child nodes, respectively. + + The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to + insert the value based on the comparison with the current node's value and updates the left or right child accordingly. + + Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the + array and inserting the mid-value into the BST, ensuring a balanced structure. + + Time Complexity: + + The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like + operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where + n is the number of elements in the array. + + The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, + as it involves traversing the height of the BST to find the appropriate position to insert the value. + Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), + where n is the number of elements in the array. + + Space Complexity: + + The space complexity is determined by the stack space used during recursive calls and the space required + to store the BST. + + The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of + the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of + elements (n), the space complexity becomes O(n). + + The space required to store the BST is also O(n) in the worst case since each element in the array is + inserted into the BST. + + Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. + In summary, the time complexity is O(n log n) and the space complexity is O(n). + + + +*/ +package main + +func MinHeightBST(array []int) *BST { + // call helper method with start index, end index array and nil node + return constructMinHeightBST(array, nil, 0, len(array)-1) +} + +func constructMinHeightBST(array []int, bst *BST, start int, end int) *BST { + // base case + if end < start { + return nil + } + mid := (start + end) / 2 + value := array[mid] + // there are no value in bst + if bst == nil { + bst = &BST{Value: value} + } else { + bst.Insert(value) + } + constructMinHeightBST(array, bst, start, mid-1) + constructMinHeightBST(array, bst, mid+1, end) + return bst +} + +type BST struct { + Value int + + Left *BST + Right *BST +} + +func (tree *BST) Insert(value int) *BST { + if value < tree.Value { + if tree.Left == nil { + tree.Left = &BST{Value: value} + } else { + tree.Left.Insert(value) + } + } else { + if tree.Right == nil { + tree.Right = &BST{Value: value} + } else { + tree.Right.Insert(value) + } + } + return tree +} diff --git a/Trees/Binary Search Trees/min_height_BST.java b/Trees/Binary Search Trees/min_height_BST.java new file mode 100644 index 00000000..89871488 --- /dev/null +++ b/Trees/Binary Search Trees/min_height_BST.java @@ -0,0 +1,128 @@ +/* + + Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. + The function should minimize the height of the BST. + Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] + Output: + + 10 + / \ + 2 14 + / \ / \ + 1 5 13 15 + \ \ + 7 22 + + Explanation: + + The given code is used to construct a minimum height binary search tree (BST) from a sorted array. + The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. + + The MinHeightBST function serves as a wrapper function that initializes the construction process by calling + the constructMinHeightBST helper function. + + The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, + a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. + + The function follows these steps: + + Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. + + Calculate the mid index as the midpoint of the current range (start and end). + + Get the value from the array at the mid index. + + If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. + Otherwise, insert the value into the existing bst using the Insert method. + + Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. + This constructs the left subtree. + + Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. + This constructs the right subtree. + + Finally, return the bst which represents the constructed minimum height BST. + + The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and + Left and Right fields to point to the left and right child nodes, respectively. + + The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to + insert the value based on the comparison with the current node's value and updates the left or right child accordingly. + + Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the + array and inserting the mid-value into the BST, ensuring a balanced structure. + + Time Complexity: + + The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like + operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where + n is the number of elements in the array. + + The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, + as it involves traversing the height of the BST to find the appropriate position to insert the value. + Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), + where n is the number of elements in the array. + + Space Complexity: + + The space complexity is determined by the stack space used during recursive calls and the space required + to store the BST. + + The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of + the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of + elements (n), the space complexity becomes O(n). + + The space required to store the BST is also O(n) in the worst case since each element in the array is + inserted into the BST. + + Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. + In summary, the time complexity is O(n log n) and the space complexity is O(n). + + + +*/ +public class JuiceBottling { + + public static int[] juiceBottling(int[] prices) { + int numSizes = prices.length; + int[] maxProfit = new int[numSizes]; // Array to store the maximum profit for each bottle size + int[] dividingPoints = new int[numSizes]; // Array to store the dividing points that maximize profit + + // Loop through each bottle size + for (int size = 0; size < numSizes; size++) { + // Loop through possible dividing points for the current size + for (int dividingPoint = 0; dividingPoint < size + 1; dividingPoint++) { + // Calculate the possible profit by combining the previous maximum profit + // with the price at the current dividing point + int possibleProfit = maxProfit[size - dividingPoint] + prices[dividingPoint]; + + // Update maxProfit and dividingPoints if the new possible profit is greater + if (possibleProfit > maxProfit[size]) { + maxProfit[size] = possibleProfit; + dividingPoints[size] = dividingPoint; + } + } + } + + int[] solution = new int[numSizes]; + int currentDividingPoint = numSizes - 1; + // Reconstruct the solution by tracing back from the end + // using the dividing points information + while (currentDividingPoint > 0) { + solution[currentDividingPoint] = dividingPoints[currentDividingPoint]; + currentDividingPoint -= dividingPoints[currentDividingPoint]; + } + return solution; + } + + public static void main(String[] args) { + int[] prices = {3, 5, 8, 9, 10, 17, 17, 20}; + int[] result = juiceBottling(prices); + + System.out.print("Dividing Points for Maximum Profit:"); + for (int point : result) { + System.out.print(" " + point); + } + System.out.println(); + } +} diff --git a/Trees/Binary Search Trees/min_height_BST.js b/Trees/Binary Search Trees/min_height_BST.js new file mode 100644 index 00000000..b7f6255c --- /dev/null +++ b/Trees/Binary Search Trees/min_height_BST.js @@ -0,0 +1,148 @@ +/* + + Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. + The function should minimize the height of the BST. + Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] + Output: + + 10 + / \ + 2 14 + / \ / \ + 1 5 13 15 + \ \ + 7 22 + + Explanation: + + The given code is used to construct a minimum height binary search tree (BST) from a sorted array. + The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. + + The MinHeightBST function serves as a wrapper function that initializes the construction process by calling + the constructMinHeightBST helper function. + + The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, + a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. + + The function follows these steps: + + Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. + + Calculate the mid index as the midpoint of the current range (start and end). + + Get the value from the array at the mid index. + + If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. + Otherwise, insert the value into the existing bst using the Insert method. + + Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. + This constructs the left subtree. + + Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. + This constructs the right subtree. + + Finally, return the bst which represents the constructed minimum height BST. + + The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and + Left and Right fields to point to the left and right child nodes, respectively. + + The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to + insert the value based on the comparison with the current node's value and updates the left or right child accordingly. + + Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the + array and inserting the mid-value into the BST, ensuring a balanced structure. + + Time Complexity: + + The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like + operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where + n is the number of elements in the array. + + The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, + as it involves traversing the height of the BST to find the appropriate position to insert the value. + Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), + where n is the number of elements in the array. + + Space Complexity: + + The space complexity is determined by the stack space used during recursive calls and the space required + to store the BST. + + The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of + the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of + elements (n), the space complexity becomes O(n). + + The space required to store the BST is also O(n) in the worst case since each element in the array is + inserted into the BST. + + Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. + In summary, the time complexity is O(n log n) and the space complexity is O(n). + + + +*/ +class TreeNode { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +// Function to construct a minimum-height BST +function minHeightBST(array) { + // Call the helper method with start index, end index, and a null root node + return constructMinHeightBST(array, null, 0, array.length - 1); +} + +// Recursive helper function to construct a minimum-height BST +function constructMinHeightBST(array, bst, start, end) { + // Base case + if (end < start) { + return null; + } + const mid = Math.floor((start + end) / 2); + const value = array[mid]; + // If the BST is empty, create a new root node + if (bst === null) { + bst = new TreeNode(value); + } else { + // Insert the value into the existing BST + bst.insert(value); + } + // Recursively construct the left and right subtrees + bst.left = constructMinHeightBST(array, bst.left, start, mid - 1); + bst.right = constructMinHeightBST(array, bst.right, mid + 1, end); + return bst; +} + +// Class for Binary Search Tree (BST) +class BST { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } + + insert(value) { + if (value < this.value) { + if (this.left === null) { + this.left = new BST(value); + } else { + this.left.insert(value); + } + } else { + if (this.right === null) { + this.right = new BST(value); + } else { + this.right.insert(value); + } + } + } +} + +// Example usage: +const array = [1, 2, 3, 4, 5, 6, 7]; +const root = minHeightBST(array); + +// Printing the BST is left as an exercise diff --git a/Trees/Binary Search Trees/min_height_BST.py b/Trees/Binary Search Trees/min_height_BST.py new file mode 100644 index 00000000..8c283db9 --- /dev/null +++ b/Trees/Binary Search Trees/min_height_BST.py @@ -0,0 +1,131 @@ +''' + Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. + The function should minimize the height of the BST. + Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] + Output: + + 10 + / \ + 2 14 + / \ / \ + 1 5 13 15 + \ \ + 7 22 + + Explanation: + + The given code is used to construct a minimum height binary search tree (BST) from a sorted array. + The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. + + The MinHeightBST function serves as a wrapper function that initializes the construction process by calling + the constructMinHeightBST helper function. + + The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, + a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. + + The function follows these steps: + + Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. + + Calculate the mid index as the midpoint of the current range (start and end). + + Get the value from the array at the mid index. + + If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. + Otherwise, insert the value into the existing bst using the Insert method. + + Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. + This constructs the left subtree. + + Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. + This constructs the right subtree. + + Finally, return the bst which represents the constructed minimum height BST. + + The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and + Left and Right fields to point to the left and right child nodes, respectively. + + The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to + insert the value based on the comparison with the current node's value and updates the left or right child accordingly. + + Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the + array and inserting the mid-value into the BST, ensuring a balanced structure. + + Time Complexity: + + The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like + operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where + n is the number of elements in the array. + + The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, + as it involves traversing the height of the BST to find the appropriate position to insert the value. + Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), + where n is the number of elements in the array. + + Space Complexity: + + The space complexity is determined by the stack space used during recursive calls and the space required + to store the BST. + + The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of + the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of + elements (n), the space complexity becomes O(n). + + The space required to store the BST is also O(n) in the worst case since each element in the array is + inserted into the BST. + + Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. + In summary, the time complexity is O(n log n) and the space complexity is O(n). + + +''' +class TreeNode: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + +def min_height_bst(array): + # Call helper method with start index, end index, and a None root node + return construct_min_height_bst(array, None, 0, len(array) - 1) + +def construct_min_height_bst(array, bst, start, end): + # Base case + if end < start: + return None + mid = (start + end) // 2 + value = array[mid] + # If the BST is empty, create a new root node + if bst is None: + bst = TreeNode(value) + else: + # Insert the value into the existing BST + bst.insert(value) + # Recursively construct the left and right subtrees + bst.left = construct_min_height_bst(array, bst.left, start, mid - 1) + bst.right = construct_min_height_bst(array, bst.right, mid + 1, end) + return bst + +class BST: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + + def insert(self, value): + if value < self.value: + if self.left is None: + self.left = BST(value) + else: + self.left.insert(value) + else: + if self.right is None: + self.right = BST(value) + else: + self.right.insert(value) + +# Example usage: +array = [1, 2, 3, 4, 5, 6, 7] +root = min_height_bst(array) + +# Printing the BST is left as an exercise diff --git a/Trees/Binary Search Trees/reconstruct_bst.cpp b/Trees/Binary Search Trees/reconstruct_bst.cpp new file mode 100644 index 00000000..e7821732 --- /dev/null +++ b/Trees/Binary Search Trees/reconstruct_bst.cpp @@ -0,0 +1,127 @@ +/* + Reconstruct BST + The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: + Current Node + Left Subtree + Right Subtree + + Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), + write a function that creates the relevant BST and returns its root node. + + The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. + + Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] + Sample Output: + 10 + / \ + 4 17 + / \ \ + 2 5 19 + / / +1 18 + + The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. + It reconstructs the BST using a range-based approach. Here's how the algorithm works: + + The ReconstructBst function initializes a treeInfo struct to keep track of the current root index. + + The ReconstructBst function calls the reconstructBSTFromRange helper function, passing the minimum and maximum integer values + as the initial range, the pre-order traversal values, and the treeInfo struct. + + The reconstructBSTFromRange function first checks if the current root index has reached the end of the pre-order traversal values. + If so, it returns nil indicating an empty subtree. + + It retrieves the value of the current root from the pre-order traversal values. + + It checks if the root value is outside the valid range defined by the lower and upper bounds. If so, it returns + + The time complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. + This is because the function processes each node exactly once. + + The space complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. + This is because the function creates BST nodes and recursively calls itself to construct the left and right subtrees. + The space complexity is determined by the height of the BST, which can be at most n in the worst case for a skewed BST. + +*/ +#include +#include +#include + +// Definition for a binary tree node. +struct TreeNode { + int val; + TreeNode* left; + TreeNode* right; + TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} +}; + +// A helper class to keep track of the current root index during reconstruction. +class TreeInfo { +public: + int rootIdx; + TreeInfo(int idx) : rootIdx(idx) {} +}; + +// Function to reconstruct the BST from a pre-order traversal. +TreeNode* reconstructBst(std::vector& preOrderTraversalValues) { + // Create a TreeInfo object to keep track of the current root index. + TreeInfo treeInfo(0); + + // Call the helper function to reconstruct the BST from the given range and return the result. + return reconstructBstFromRange(INT_MIN, INT_MAX, preOrderTraversalValues, treeInfo); +} + +// Recursive helper function to reconstruct the BST within a given range. +TreeNode* reconstructBstFromRange(int lowerBound, int upperBound, std::vector& preOrderTraversalValues, TreeInfo& currentSubtreeInfo) { + // Check if the root index has reached the end of the pre-order traversal values. If so, return nullptr indicating an empty subtree. + if (currentSubtreeInfo.rootIdx == preOrderTraversalValues.size()) { + return nullptr; + } + + // Get the value of the current root from the pre-order traversal values. + int rootValue = preOrderTraversalValues[currentSubtreeInfo.rootIdx]; + + // Check if the root value is out of the valid range defined by the lower and upper bounds. If so, return nullptr indicating an invalid subtree. + if (rootValue < lowerBound || rootValue >= upperBound) { + return nullptr; + } + + // Increment the root index to move to the next element in the pre-order traversal values. + currentSubtreeInfo.rootIdx++; + + // Recursively reconstruct the left subtree within the range (lowerBound, rootValue) using the updated root index. + TreeNode* leftSubtree = reconstructBstFromRange(lowerBound, rootValue, preOrderTraversalValues, currentSubtreeInfo); + + // Recursively reconstruct the right subtree within the range (rootValue, upperBound) using the updated root index. + TreeNode* rightSubtree = reconstructBstFromRange(rootValue, upperBound, preOrderTraversalValues, currentSubtreeInfo); + + // Create a new TreeNode with the current root value and the reconstructed left and right subtrees. + TreeNode* rootNode = new TreeNode(rootValue); + rootNode->left = leftSubtree; + rootNode->right = rightSubtree; + + return rootNode; +} + +// Function to delete the BST and free memory. +void deleteBst(TreeNode* root) { + if (root == nullptr) { + return; + } + deleteBst(root->left); + deleteBst(root->right); + delete root; +} + +int main() { + std::vector preOrderTraversalValues = {10, 5, 2, 7, 15, 12, 20}; + TreeInfo treeInfo(0); // Initialize treeInfo with root index 0 + TreeNode* root = reconstructBstFromRange(INT_MIN, INT_MAX, preOrderTraversalValues, treeInfo); + + // Printing the reconstructed BST is left as an exercise + + // Clean up memory + deleteBst(root); + + return 0; +} diff --git a/Trees/Binary Search Trees/reconstruct_bst.go b/Trees/Binary Search Trees/reconstruct_bst.go new file mode 100644 index 00000000..5a04820c --- /dev/null +++ b/Trees/Binary Search Trees/reconstruct_bst.go @@ -0,0 +1,161 @@ +/* + Reconstruct BST + The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: + Current Node + Left Subtree + Right Subtree + + Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), + write a function that creates the relevant BST and returns its root node. + + The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. + + Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] + Sample Output: + 10 + / \ + 4 17 + / \ \ + 2 5 19 + / / +1 18 + + Explanation: + + Approach 1: + + The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. + It reconstructs the BST using a recursive approach. Here's how the algorithm works: + + The base case is defined when the preOrderTraversalValues slice is empty, in which case it returns nil indicating an empty tree. + + The first element in the preOrderTraversalValues slice represents the current node value of the BST. + + The algorithm finds the index (rightSubTreeRootIdx) where the right subtree starts by iterating over the remaining elements in + the preOrderTraversalValues slice and finding the first value greater than or equal to the current value. + + It recursively calls ReconstructBst on the sub-array representing the left subtree (preOrderTraversalValues[1:rightSubTreeRootIdx]) + to reconstruct the left subtree. + + It recursively calls ReconstructBst on the sub-array representing the right subtree (preOrderTraversalValues[rightSubTreeRootIdx:]) + to reconstruct the right subtree. + + Finally, it creates a new BST node with the current value, the reconstructed left subtree, and the reconstructed right subtree, + and returns the node. + + The algorithm builds the BST in a top-down manner by dividing the pre-order traversal values into left and right subtrees. + It constructs the left subtree first and then the right subtree. + + The time complexity of the algorithm is O(n^2) in the worst case, where n is the number of nodes in the BST. + + + ****************************************************************************************** + + Approach 2: + + The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. + It reconstructs the BST using a range-based approach. Here's how the algorithm works: + + The ReconstructBst function initializes a treeInfo struct to keep track of the current root index. + + The ReconstructBst function calls the reconstructBSTFromRange helper function, passing the minimum and maximum integer values + as the initial range, the pre-order traversal values, and the treeInfo struct. + + The reconstructBSTFromRange function first checks if the current root index has reached the end of the pre-order traversal values. + If so, it returns nil indicating an empty subtree. + + It retrieves the value of the current root from the pre-order traversal values. + + It checks if the root value is outside the valid range defined by the lower and upper bounds. If so, it returns + + The time complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. + This is because the function processes each node exactly once. + + The space complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. + This is because the function creates BST nodes and recursively calls itself to construct the left and right subtrees. + The space complexity is determined by the height of the BST, which can be at most n in the worst case for a skewed BST. +*/ +package main + +import "math" + +// BST represents a binary search tree node. +type BST struct { + Value int + Left *BST + Right *BST +} + +// Approach 1: Time complexity O(n^2) Space O(n), where n is length of input array +// ReconstructBst takes a slice of integers representing the pre-order traversal of a BST and returns the reconstructed BST. +func ReconstructBst(preOrderTraversalValues []int) *BST { + // Base case: If the pre-order traversal is empty, return nil indicating an empty tree. + if len(preOrderTraversalValues) == 0 { + return nil + } + + // Get the current value from the pre-order traversal values. + currentVal := preOrderTraversalValues[0] + + // Find the index where the right subtree starts by searching for the first value greater than or equal to the current value. + rightSubTreeRootIdx := len(preOrderTraversalValues) + for i := 1; i < len(preOrderTraversalValues); i++ { + value := preOrderTraversalValues[i] + if value >= currentVal { + rightSubTreeRootIdx = i + break + } + } + + // Recursively reconstruct the left and right subtrees by calling the ReconstructBst function on the appropriate sub-arrays. + leftSubTree := ReconstructBst(preOrderTraversalValues[1:rightSubTreeRootIdx]) + rightSubTree := ReconstructBst(preOrderTraversalValues[rightSubTreeRootIdx:]) + + // Create a new BST node with the current value and the reconstructed left and right subtrees. + return &BST{Value: currentVal, Left: leftSubTree, Right: rightSubTree} +} + + +// Approach 2: Time complexity O(n) Space O(n), where n is length of input array + +// treeInfo is a helper struct to keep track of the current root index during the reconstruction process. +type treeInfo struct { + rootIdx int +} + +// ReconstructBst takes a slice of integers representing the pre-order traversal of a BST and returns the reconstructed BST. +func ReconstructBst2(preOrderTraversalValues []int) *BST { + // Create a treeInfo struct to keep track of the current root index. + treeInfo := &treeInfo{rootIdx: 0} + + // Call the helper function to reconstruct the BST from the given range and return the result. + return reconstructBSTFromRange(math.MinInt32, math.MaxInt32, preOrderTraversalValues, treeInfo) +} + +// reconstructBSTFromRange reconstructs the BST recursively within the given range using the pre-order traversal values. +func reconstructBSTFromRange(lowerBound, upperBound int, preOrderTraversalValues []int, currentSubtreeInfo *treeInfo) *BST { + // Check if the root index has reached the end of the pre-order traversal values. If so, return nil indicating an empty subtree. + if currentSubtreeInfo.rootIdx == len(preOrderTraversalValues) { + return nil + } + + // Get the value of the current root from the pre-order traversal values. + rootValue := preOrderTraversalValues[currentSubtreeInfo.rootIdx] + + // Check if the root value is out of the valid range defined by the lower and upper bounds. If so, return nil indicating an invalid subtree. + if rootValue < lowerBound || rootValue >= upperBound { + return nil + } + + // Increment the root index to move to the next element in the pre-order traversal values. + currentSubtreeInfo.rootIdx++ + + // Recursively reconstruct the left subtree within the range (lowerBound, rootValue) using the updated root index. + leftSubtree := reconstructBSTFromRange(lowerBound, rootValue, preOrderTraversalValues, currentSubtreeInfo) + + // Recursively reconstruct the right subtree within the range (rootValue, upperBound) using the updated root index. + rightSubtree := reconstructBSTFromRange(rootValue, upperBound, preOrderTraversalValues, currentSubtreeInfo) + + // Create a new BST node with the current root value and the reconstructed left and right subtrees. + return &BST{Value: rootValue, Left: leftSubtree, Right: rightSubtree} +} diff --git a/Trees/Binary Search Trees/reconstruct_bst.java b/Trees/Binary Search Trees/reconstruct_bst.java new file mode 100644 index 00000000..4e2b8575 --- /dev/null +++ b/Trees/Binary Search Trees/reconstruct_bst.java @@ -0,0 +1,113 @@ +/* + Reconstruct BST + The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: + Current Node + Left Subtree + Right Subtree + + Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), + write a function that creates the relevant BST and returns its root node. + + The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. + + Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] + Sample Output: + 10 + / \ + 4 17 + / \ \ + 2 5 19 + / / +1 18 + + The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. + It reconstructs the BST using a range-based approach. Here's how the algorithm works: + + The ReconstructBst function initializes a treeInfo struct to keep track of the current root index. + + The ReconstructBst function calls the reconstructBSTFromRange helper function, passing the minimum and maximum integer values + as the initial range, the pre-order traversal values, and the treeInfo struct. + + The reconstructBSTFromRange function first checks if the current root index has reached the end of the pre-order traversal values. + If so, it returns nil indicating an empty subtree. + + It retrieves the value of the current root from the pre-order traversal values. + + It checks if the root value is outside the valid range defined by the lower and upper bounds. If so, it returns + + The time complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. + This is because the function processes each node exactly once. + + The space complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. + This is because the function creates BST nodes and recursively calls itself to construct the left and right subtrees. + The space complexity is determined by the height of the BST, which can be at most n in the worst case for a skewed BST. + +*/ +class TreeNode { + int value; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + value = x; + left = null; + right = null; + } +} + +class TreeInfo { + int rootIdx; + + TreeInfo(int idx) { + rootIdx = idx; + } +} + +public class ReconstructBST { + + public static TreeNode reconstructBst(int[] preOrderTraversalValues) { + // Create a TreeInfo object to keep track of the current root index. + TreeInfo treeInfo = new TreeInfo(0); + + // Call the helper function to reconstruct the BST from the given range and return the result. + return reconstructBstFromRange(Integer.MIN_VALUE, Integer.MAX_VALUE, preOrderTraversalValues, treeInfo); + } + + private static TreeNode reconstructBstFromRange(int lowerBound, int upperBound, int[] preOrderTraversalValues, TreeInfo currentSubtreeInfo) { + // Check if the root index has reached the end of the pre-order traversal values. If so, return null indicating an empty subtree. + if (currentSubtreeInfo.rootIdx == preOrderTraversalValues.length) { + return null; + } + + // Get the value of the current root from the pre-order traversal values. + int rootValue = preOrderTraversalValues[currentSubtreeInfo.rootIdx]; + + // Check if the root value is out of the valid range defined by the lower and upper bounds. If so, return null indicating an invalid subtree. + if (rootValue < lowerBound || rootValue >= upperBound) { + return null; + } + + // Increment the root index to move to the next element in the pre-order traversal values. + currentSubtreeInfo.rootIdx++; + + // Recursively reconstruct the left subtree within the range (lowerBound, rootValue) using the updated root index. + TreeNode leftSubtree = reconstructBstFromRange(lowerBound, rootValue, preOrderTraversalValues, currentSubtreeInfo); + + // Recursively reconstruct the right subtree within the range (rootValue, upperBound) using the updated root index. + TreeNode rightSubtree = reconstructBstFromRange(rootValue, upperBound, preOrderTraversalValues, currentSubtreeInfo); + + // Create a new TreeNode with the current root value and the reconstructed left and right subtrees. + TreeNode rootNode = new TreeNode(rootValue); + rootNode.left = leftSubtree; + rootNode.right = rightSubtree; + + return rootNode; + } + + public static void main(String[] args) { + int[] preOrderTraversalValues = {10, 5, 2, 7, 15, 12, 20}; + TreeNode root = reconstructBst(preOrderTraversalValues); + + // Printing the reconstructed BST is left as an exercise + } +} diff --git a/Trees/Binary Search Trees/reconstruct_bst.js b/Trees/Binary Search Trees/reconstruct_bst.js new file mode 100644 index 00000000..02196c94 --- /dev/null +++ b/Trees/Binary Search Trees/reconstruct_bst.js @@ -0,0 +1,105 @@ +/** Reconstruct BST + The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root + node and visits nodes in the following order: + Current Node + Left Subtree + Right Subtree + + Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree + (BST), write a function that creates the relevant BST and returns its root node. + + The input array will contain the values of BST nodes in the order in which these nodes would be + visited with a pre-order traversal. + Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] + Sample Output: + 10 + / \ + 4 17 + / \ \ + 2 5 19 + / / +1 18 +*/ +/** + * Class representing a node in the Binary Search Tree (BST). + */ + class TreeNode { + /** + * Create a new TreeNode. + * @param {*} value - The value to be stored in the node. + */ + constructor(value) { + this.val = value; + this.left = null; + this.right = null; + } + } + + /** + * Reconstructs a Binary Search Tree (BST) from its pre-order traversal. + * @param {number[]} preorder - The pre-order traversal array of the BST. + * @return {TreeNode} - The root node of the reconstructed BST. + */ + function constructBST(preorder) { + // Base case: if the array is empty, return null + if (preorder.length === 0) { + return null; + } + + // The first element in the pre-order array is the root value + const rootValue = preorder[0]; + const rootNode = new TreeNode(rootValue); + + // Find the index where the elements are greater than the root value + let i = 1; + while (i < preorder.length && preorder[i] < rootValue) { + i++; + } + + // Split the remaining elements into left and right subtrees + const leftSubtree = preorder.slice(1, i); + const rightSubtree = preorder.slice(i); + + // Recursively construct the left and right subtrees + rootNode.left = constructBST(leftSubtree); + rootNode.right = constructBST(rightSubtree); + + return rootNode; + } + + // Example usage + const preorder = [10, 4, 2, 1, 5, 17, 19, 18]; + const root = constructBST(preorder); + + /** + * Prints the values of the BST in ascending order using an in-order traversal. + * @param {TreeNode} node - The root node of the BST. + */ + function printInOrder(node) { + if (node === null) { + return; + } + + printInOrder(node.left); + console.log(node.val); + printInOrder(node.right); + } + + // Print the values of the reconstructed BST in ascending order + printInOrder(root); + +/** + * The time complexity of the constructBST function can be analyzed as follows: + +In each recursive call, we split the pre-order traversal array into two parts based on the root value. This operation takes O(N) time, where N is the number of elements in the array. +Since the function is called recursively for the left and right subtrees, the total time complexity can be expressed as a summation of the work done in each recursive call. +In the worst case, the pre-order traversal array is completely unbalanced, resulting in a linear chain of nodes. In this case, the function will make N recursive calls, each taking O(N) time. +Therefore, the overall time complexity of the constructBST function is O(N^2) in the worst case. +However, in the average case, when the BST is balanced, the time complexity can be approximated as O(NlogN). + */ + +/** + * In each recursive call, the function creates new arrays for the left and right subtrees using the slice method. The space required for these arrays is proportional to the size of the pre-order traversal array. +In the worst case, when the BST is completely unbalanced, the size of the arrays will be O(N), where N is the number of elements in the pre-order traversal array. +Therefore, the overall space complexity of the `constructBST function is O(N) in the worst case. + */ \ No newline at end of file diff --git a/Trees/Binary Search Trees/reconstruct_bst.py b/Trees/Binary Search Trees/reconstruct_bst.py new file mode 100644 index 00000000..35b5ee6a --- /dev/null +++ b/Trees/Binary Search Trees/reconstruct_bst.py @@ -0,0 +1,96 @@ +''' + Reconstruct BST + The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: + Current Node + Left Subtree + Right Subtree + + Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), + write a function that creates the relevant BST and returns its root node. + + The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. + + Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] + Sample Output: + 10 + / \ + 4 17 + / \ \ + 2 5 19 + / / +1 18 + + The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. + It reconstructs the BST using a range-based approach. Here's how the algorithm works: + + The ReconstructBst function initializes a treeInfo struct to keep track of the current root index. + + The ReconstructBst function calls the reconstructBSTFromRange helper function, passing the minimum and maximum integer values + as the initial range, the pre-order traversal values, and the treeInfo struct. + + The reconstructBSTFromRange function first checks if the current root index has reached the end of the pre-order traversal values. + If so, it returns nil indicating an empty subtree. + + It retrieves the value of the current root from the pre-order traversal values. + + It checks if the root value is outside the valid range defined by the lower and upper bounds. If so, it returns + + The time complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. + This is because the function processes each node exactly once. + + The space complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. + This is because the function creates BST nodes and recursively calls itself to construct the left and right subtrees. + The space complexity is determined by the height of the BST, which can be at most n in the worst case for a skewed BST. + +''' +class TreeNode: + def __init__(self, val): + self.val = val + self.left = None + self.right = None + +class TreeInfo: + def __init__(self, root_idx): + self.root_idx = root_idx + +def reconstruct_bst(pre_order_traversal_values): + # Create a TreeInfo object to keep track of the current root index. + tree_info = TreeInfo(0) + + # Call the helper function to reconstruct the BST from the given range and return the result. + return reconstruct_bst_from_range(float('-inf'), float('inf'), pre_order_traversal_values, tree_info) + +def reconstruct_bst_from_range(lower_bound, upper_bound, pre_order_traversal_values, current_subtree_info): + # Check if the root index has reached the end of the pre-order traversal values. If so, return None indicating an empty subtree. + if current_subtree_info.root_idx == len(pre_order_traversal_values): + return None + + # Get the value of the current root from the pre-order traversal values. + root_value = pre_order_traversal_values[current_subtree_info.root_idx] + + # Check if the root value is out of the valid range defined by the lower and upper bounds. If so, return None indicating an invalid subtree. + if root_value < lower_bound or root_value >= upper_bound: + return None + + # Increment the root index to move to the next element in the pre-order traversal values. + current_subtree_info.root_idx += 1 + + # Recursively reconstruct the left subtree within the range (lower_bound, root_value) using the updated root index. + left_subtree = reconstruct_bst_from_range(lower_bound, root_value, pre_order_traversal_values, current_subtree_info) + + # Recursively reconstruct the right subtree within the range (root_value, upper_bound) using the updated root index. + right_subtree = reconstruct_bst_from_range(root_value, upper_bound, pre_order_traversal_values, current_subtree_info) + + # Create a new TreeNode with the current root value and the reconstructed left and right subtrees. + root_node = TreeNode(root_value) + root_node.left = left_subtree + root_node.right = right_subtree + + return root_node + +# Example usage: +pre_order_traversal_values = [10, 5, 2, 7, 15, 12, 20] +tree_info = TreeInfo(0) # Initialize tree_info with root index 0 +root = reconstruct_bst_from_range(float('-inf'), float('inf'), pre_order_traversal_values, tree_info) + +# Printing the reconstructed BST is left as an exercise diff --git a/Trees/Binary_Search_Trees/BST_search.cpp b/Trees/Binary Search Trees/search.cpp similarity index 100% rename from Trees/Binary_Search_Trees/BST_search.cpp rename to Trees/Binary Search Trees/search.cpp diff --git a/Trees/Binary Search Trees/validate_bst.go b/Trees/Binary Search Trees/validate_bst.go new file mode 100644 index 00000000..842f8acd --- /dev/null +++ b/Trees/Binary Search Trees/validate_bst.go @@ -0,0 +1,71 @@ +/* + Write a function that takes in a potentially invalid Binary Search Tree (BST) + and returns a boolean representing whether the BST is valid. + Sample Input : + 10 + / \ + 5 15 + / \ / \ + 2 5 13 22 + / \ +1 14 + Output : True + + Explanation: + This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can + point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating + whether the tree is a valid BST or not. + + The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree + is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum + and maximum values that the current node's value can take in order to be a valid BST. + + The validateBST() method first checks whether the current node's value is within the valid range determined + by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. + + If the current node's value is within the valid range, the method then recursively calls itself on the left + and right child nodes to check whether their values are within their valid ranges. The valid range for the + left child node is defined by the minimum value and the parent node's value, while the valid range for the + right child node is defined by the parent node's value and the maximum value. + + If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree + is a valid BST. + + O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST + +*/ +package main + +import "math" + +type BST struct { + Value int + + Left *BST + Right *BST +} + +// ValidateBst is a method of BST that checks if the binary search tree is valid +func (tree *BST) ValidateBst() bool { + return tree.validateBST(math.MinInt32, math.MaxInt32) +} + +// validateBST is a recursive helper function that checks if the binary search tree is valid +// min is the minimum value that a node in the subtree rooted at this node can have +// max is the maximum value that a node in the subtree rooted at this node can have +func (tree *BST) validateBST(min, max int) bool { + // if the current node's value is outside the allowed range, then the tree is invalid + if tree.Value < min || tree.Value >= max { + return false + } + // recursively check the left subtree, making sure all values are less than the current node's value + if tree.Left != nil && !tree.Left.validateBST(min, tree.Value) { + return false + } + // recursively check the right subtree, making sure all values are greater than or equal to the current node's value + if tree.Right != nil && !tree.Right.validateBST(tree.Value, max) { + return false + } + // if we reach this point, then the tree is valid + return true +} diff --git a/Trees/Binary Trees/Trie.js b/Trees/Binary Trees/Trie.js new file mode 100644 index 00000000..b4f6522c --- /dev/null +++ b/Trees/Binary Trees/Trie.js @@ -0,0 +1,93 @@ +/* +Description +A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker. + +Implement the Trie class: + +Trie() Initializes the trie object. +void insert(String word) Inserts the string word into the trie. +boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. +boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise. + +Input +["Trie", "insert", "search", "search", "startsWith", "insert", "search"] +[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] +Output +[null, null, true, false, true, null, true] + +Explanation +Trie trie = new Trie(); +trie.insert("apple"); +trie.search("apple"); // return True +trie.search("app"); // return False +trie.startsWith("app"); // return True +trie.insert("app"); +trie.search("app"); // return True + +Time Complexity for each operation is O(n) +Space Complexity O(n*m) where n is the number of words inserted and m is the average length of the words. + +Explanation: +insert() -> traverse through each character of the input word and initializes it if necessary. If the end of the word is reached set isEnd to true. +search() -> Search In each child node until the end of the word is reached, then if end of the node is also reached return true else false. +startsWith() -> Similar to search method but we only check if end of the prefix is reached and we don't need to check if it is the end of the node. + +*/ + + +class Trie { + constructor() { + this.root = new Node(); + } + + insert(word) { + this.root.insert(word, 0); + } + + search(word) { + return this.root.search(word, 0); + } + + startsWith(prefix) { + return this.root.startsWith(prefix, 0); + } +} + +class Node { + constructor() { + this.nodes = new Array(26); + this.isEnd = false; + } + + // Function to insert the word in the tree + insert(word, idx) { + if (idx >= word.length) return; // handle edge case + const i = word.charCodeAt(idx) - 'a'.charCodeAt(0); + if (!this.nodes[i]) { + this.nodes[i] = new Node(); // initialize the node[i] if the letter was not found before + } + + if (idx === word.length - 1) this.nodes[i].isEnd = true; // signifies that this is the end of the word + this.nodes[i].insert(word, idx + 1); // recursive call to populate the child node + } + + // Function to search the word in the tree + search(word, idx) { + if (idx >= word.length) return false; + const node = this.nodes[word.charCodeAt(idx) - 'a'.charCodeAt(0)]; + if (!node) return false; // if the node is null it means that it was not initialised hence the character was never found. + if (idx === word.length - 1 && node.isEnd) return true; //if it is the last character and the end of the node then return true + + return node.search(word, idx + 1); // recursive call search in the child node + } + + //Function to search the prefix in tree + startsWith(prefix, idx) { + if (idx >= prefix.length) return false; + const node = this.nodes[prefix.charCodeAt(idx) - 'a'.charCodeAt(0)]; + if (!node) return false; + if (idx === prefix.length - 1) return true; // Very similar to above method but here we don't need to check if it is the end of the node. + + return node.startsWith(prefix, idx + 1); + } +} diff --git a/Trees/Binary_Trees/binary_tree_bfs.cpp b/Trees/Binary Trees/bfs.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_bfs.cpp rename to Trees/Binary Trees/bfs.cpp diff --git a/Trees/Binary Trees/bfs.go b/Trees/Binary Trees/bfs.go new file mode 100644 index 00000000..792b95d0 --- /dev/null +++ b/Trees/Binary Trees/bfs.go @@ -0,0 +1,65 @@ +/* + Breadth First Search: + Sample Input: + A + / | \ + B C D + / \ / \ + E F G H + / \ \ + I J K + Output: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"] + + Explanation: + This code defines a struct `Node` with two properties: `Name`, a string representing the name of the node, + and `Children`, a slice of pointers to other `Node` objects representing the children of the node. + + The method `BreadthFirstSearch` is defined on `Node`. This method takes an empty string slice `array` as + an argument and performs a breadth-first search traversal of the tree rooted at the current node. + + It returns a slice of strings representing the names of the nodes visited in breadth-first order. + + The breadth-first search algorithm starts with a queue containing only the current node. The algorithm then proceeds as follows: + + 1. Dequeue the first node from the queue. + 2. Add the name of the dequeued node to the `array`. + 3. Enqueue all the children of the dequeued node to the end of the queue. + 4. Repeat steps 1-3 until the queue is empty. + + At the end of the algorithm, the `array` contains the names of all the nodes visited in breadth-first order. The method returns this `array`. + + Time complexity: O(v + e) + Space complexity: O(v) + where v is the number of vertices of the input graph and e is the number of edges of the input graph +*/ +package main + +// Define a Node struct that has a name and a slice of child nodes. +type Node struct { + Name string + Children []*Node +} + +// Define a method BreadthFirstSearch on the Node struct that takes an array of strings as an argument. +// This method performs a breadth-first search on the tree starting from the node the method is called on. +// It appends the names of all nodes visited to the input array and returns the modified array. +func (n *Node) BreadthFirstSearch(array []string) []string { + // Create a queue of nodes starting with the current node. + queue := []*Node{n} + + // While there are still nodes in the queue to be visited: + for len(queue) > 0 { + // Dequeue the first node in the queue and add its name to the array. + current := queue[0] + queue = queue[1:] + array = append(array, current.Name) + + // Add all of the node's children to the end of the queue. + for _, child := range current.Children { + queue = append(queue, child) + } + } + + // Return the modified array. + return array +} diff --git a/Trees/Binary Trees/binary_tree.go b/Trees/Binary Trees/binary_tree.go new file mode 100644 index 00000000..03981c65 --- /dev/null +++ b/Trees/Binary Trees/binary_tree.go @@ -0,0 +1,116 @@ +/* + A tree is called binary tree if each node has zero child, one child or two children. Empty tree is also a valid binary + tree. We can visualize a binary tree as consisting of a root and two disjoint binary trees, called the left and right + subtrees of the root. + + Types of Binary Trees + 1) Strict Binary Tree: A binary tree is called strict binary tree if each node has exactly two children or no children. + 2) Full Binary Tree: A binary tree is called full binary tree if each node has exactly two children and all leaf nodes + are at the same level. + 3) Complete Binary Tree: Before defining the complete binary tree, let us assume that the height of the binary tree + is ℎ. In complete binary trees, if we give numbering for the nodes by starting at the root (let us say the root node + has 1) then we get a complete sequence from 1 to the number of nodes in the tree. While traversing we should give + numbering for nil pointers also. A binary tree is called complete binary tree if all leaf nodes are at height ℎ or ℎ − 1 + and also without any missing number in the sequence. + + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 +*/ +package main + +import ( + "fmt" + "math/rand" +) + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} +// NewBinaryTree returns a new, random binary tree +func NewBinaryTree(n, k int) *BinaryTreeNode { + var root * BinaryTreeNode + for _, v := range rand.Perm(n) { + root = Insert(root, (1 + v) * k) + } + return root +} +// Insert, inserts an element in binary tree +func Insert(root *BinaryTreeNode, v int) *BinaryTreeNode { + if root == nil { + // fmt.Printf("%d root", v) + return &BinaryTreeNode{nil, v, nil} + } + // data less than root of data the insert in left subtree + if v < root.data { + // fmt.Printf("%d left\n", v) + root.left = Insert(root.left, v) + return root + } + // data greater than or equal to root of data the insert in right subtree + // fmt.Printf("%d right\n", v) + root.right = Insert(root.right, v) + return root +} + +// Pre-order traversal +// Preorder traversal is defined as follows: +// 1 Visit the root. +// 2 Traverse the left subtree in Preorder. +// 3 Traverse the right subtree in Preorder. +// Time Complexity: O(n). Space Complexity: O(n). +// The nodes of tree would be visited in the order: 1 2 4 5 3 6 7 +func PreOrder(root *BinaryTreeNode) { + + if root == nil { + return + } + fmt.Printf("%d ", root.data) + PreOrder(root.left) + PreOrder(root.right) +} + +// Inorder traversal is defined as follows: +// 1 Traverse the left subtree in Inorder. +// 2 Visit the root. +// 3 Traverse the right subtree in Inorder. +// Time Complexity: O(n). Space Complexity: O(n). +// The nodes of tree would be visited in the order: 4 2 5 1 6 3 7 +func InOrder(root *BinaryTreeNode) { + if root == nil { + return + } + InOrder(root.left) + fmt.Printf("%d", root.data) + InOrder(root.right) +} + +// PostOrder traversal is defined as follows: +// 1 Traverse the left subtree in PostOrder. +// 2 Traverse the right subtree in PostOrder. +// 3 Visit the root. +// The nodes of the tree would be visited in the order: 4 5 2 6 7 3 1 +func PostOrder(root *BinaryTreeNode) { + if root == nil { + return + } + PostOrder(root.left) + PostOrder(root.right) + fmt.Printf("%d", root.data) +} + + + +func main() { + t1 := NewBinaryTree(10, 1) + PreOrder(t1) + fmt.Println() + InOrder(t1) + fmt.Println() + PostOrder(t1) + fmt.Println() +} \ No newline at end of file diff --git a/Trees/Binary Trees/branch_sum.go b/Trees/Binary Trees/branch_sum.go new file mode 100644 index 00000000..2288f1bf --- /dev/null +++ b/Trees/Binary Trees/branch_sum.go @@ -0,0 +1,53 @@ +/* + Write a function that takes in a Binary Tree and returns a list of its branch + sums ordered from leftmost branch sum to rightmost branch sum. + + A branch sum is the sum of all values in a Binary Tree branch. A Binary Tree + branch is a path of nodes in a tree that starts at the root node and ends at + any leaf node. + + Sample INput: + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + / \ / + 8 9 10 + + Output: [15, 16, 18, 10, 11] length of output will be always total number of leaves + Time and Space complexity : O(n) time | O(n) space - where n is the number of nodes in the Binary Tree +*/ +package main + +type BinaryTree struct { + Value int + Left *BinaryTree + Right *BinaryTree +} + +func BranchSums(root *BinaryTree) []int { + sums := []int{} + calculateBranchSums(root, 0, &sums) + return sums +} +/* + +As you recursively traverse the tree, if you reach a leaf node +(a node with no "left" or "right" Binary Tree nodes), +add the relevant running sum that you've calculated to a list of +sums (which you'll also have to pass to the recursive function). +If you reach a node that isn't a leaf node, keep recursively traversing +its children nodes, passing the correctly updated running sum to them. +*/ +func calculateBranchSums(node *BinaryTree, runningSum int, sums *[]int) { + if node == nil { + return + } + runningSum += node.Value + if node.Left == nil && node.Right == nil { + *sums = append(*sums, runningSum) + } + calculateBranchSums(node.Left, runningSum, sums) + calculateBranchSums(node.Right, runningSum, sums) +} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree_build_tree_preorder.cpp b/Trees/Binary Trees/build_tree_preorder.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_build_tree_preorder.cpp rename to Trees/Binary Trees/build_tree_preorder.cpp diff --git a/Trees/Binary Trees/calculate_size.go b/Trees/Binary Trees/calculate_size.go new file mode 100644 index 00000000..5fb3fe4e --- /dev/null +++ b/Trees/Binary Trees/calculate_size.go @@ -0,0 +1,45 @@ +// Size of binary tree +package main + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: calculate the size of left and right subtree recursively +// add 1 (curr node) and return to its parent +func Size(root *BinaryTreeNode) int { + if root == nil { + return 0 + } else { + return Size(root.left) + 1 + Size(root.right) + } +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: use level order traversal and count nodes +func SizeWithoutUsingRecursion(root *BinaryTreeNode) int { + if root == nil { + return 0 + } + var result int + queue := []*BinaryTreeNode{root} + for len(queue) > 0 { + qlen := len(queue) + //var level []int + for i := 0; i < qlen; i++ { + node := queue[0] + result++ + //level = append(level, node.data) + queue = queue[1:] + if node.left != nil { + queue = append(queue, node.left) + } + if node.right != nil { + queue = append(queue, node.right) + } + } + } + return result +} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree_count_nodes.cpp b/Trees/Binary Trees/count_nodes.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_count_nodes.cpp rename to Trees/Binary Trees/count_nodes.cpp diff --git a/Trees/Binary Trees/delete.go b/Trees/Binary Trees/delete.go new file mode 100644 index 00000000..3ff0be59 --- /dev/null +++ b/Trees/Binary Trees/delete.go @@ -0,0 +1,23 @@ +// Delete binary tree +package main + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: before deleting parent node, delete all its children nodes +// using post order traversal we can solve this problem +func DeleteTree(root *BinaryTreeNode) *BinaryTreeNode { + if root == nil { + return nil + } + // delete both subtrees + root.left = DeleteTree(root.left) + root.right = DeleteTree(root.right) + // delete current node after deleting subtrees + root = nil + return root +} \ No newline at end of file diff --git a/Trees/Binary Trees/dfs.cpp b/Trees/Binary Trees/dfs.cpp new file mode 100644 index 00000000..8732fb28 --- /dev/null +++ b/Trees/Binary Trees/dfs.cpp @@ -0,0 +1,87 @@ +// Implementation of Depth First Search +/* + This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. + It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. + The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. + The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper + function recursively visits each node and its children. + Sample Input : + // 1 + // / \ + // 2 3 + // / \ / \ + // 4 5 6 7 + Output : 1 2 4 5 3 6 7 + + The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) + in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. + + The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like + structure, where each node has only one child, the maximum depth is equal to the height of the tree. + Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. + In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, + so the space complexity becomes O(V). +*/ +#include +#include +#include + +using namespace std; + +// Node represents a node in a graph. +struct Node { + int value; + vector children; +}; + +// DFS traverses the graph using Depth-First Search starting from the given node. +void DFS(Node* node) { + // Create a set to keep track of visited nodes. + unordered_map visited; + + // Call the recursive helper function to perform DFS. + dfsHelper(node, visited); +} + +// dfsHelper is a recursive function that performs Depth-First Search on the graph. +void dfsHelper(Node* node, unordered_map& visited) { + // Mark the current node as visited. + visited[node] = true; + + // Process the current node (print its value in this case). + cout << node->value << endl; + + // Traverse the children of the current node. + for (Node* child : node->children) { + // If the child node has not been visited, recursively call dfsHelper on it. + if (!visited[child]) { + dfsHelper(child, visited); + } + } +} + +int main() { + // Create a sample graph. + // 1 + // / \ + // 2 3 + // / \ / \ + // 4 5 6 7 + Node node1{1}; + Node node2{2}; + Node node3{3}; + Node node4{4}; + Node node5{5}; + Node node6{6}; + Node node7{7}; + + node1.children = {&node2, &node3}; + node2.children = {&node4, &node5}; + node3.children = {&node6, &node7}; + + // Perform DFS starting from node1. + cout << "Depth-First Search:" << endl; + DFS(&node1); + + return 0; +} diff --git a/Trees/Binary Trees/dfs.go b/Trees/Binary Trees/dfs.go new file mode 100644 index 00000000..8f17ab43 --- /dev/null +++ b/Trees/Binary Trees/dfs.go @@ -0,0 +1,83 @@ +// Implementation of Depth First Search +/* + This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. + It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. + The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. + The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper + function recursively visits each node and its children. + Sample Input : + // 1 + // / \ + // 2 3 + // / \ / \ + // 4 5 6 7 + Output : 1 2 4 5 3 6 7 + + The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) + in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. + + The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like + structure, where each node has only one child, the maximum depth is equal to the height of the tree. + Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. + In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, + so the space complexity becomes O(V). +*/ +package main + +import "fmt" + +// Node represents a node in a graph. +type Node struct { + Value int + Children []*Node +} + +// DFS traverses the graph using Depth-First Search starting from the given node. +func DFS(node *Node) { + // Create a set to keep track of visited nodes. + visited := make(map[*Node]bool) + + // Call the recursive helper function to perform DFS. + dfsHelper(node, visited) +} + +// dfsHelper is a recursive function that performs Depth-First Search on the graph. +func dfsHelper(node *Node, visited map[*Node]bool) { + // Mark the current node as visited. + visited[node] = true + + // Process the current node (print its value in this case). + fmt.Println(node.Value) + + // Traverse the children of the current node. + for _, child := range node.Children { + // If the child node has not been visited, recursively call dfsHelper on it. + if !visited[child] { + dfsHelper(child, visited) + } + } +} + +func main() { + // Create a sample graph. + // 1 + // / \ + // 2 3 + // / \ / \ + // 4 5 6 7 + node1 := &Node{Value: 1} + node2 := &Node{Value: 2} + node3 := &Node{Value: 3} + node4 := &Node{Value: 4} + node5 := &Node{Value: 5} + node6 := &Node{Value: 6} + node7 := &Node{Value: 7} + + node1.Children = []*Node{node2, node3} + node2.Children = []*Node{node4, node5} + node3.Children = []*Node{node6, node7} + + // Perform DFS starting from node1. + fmt.Println("Depth-First Search:") + DFS(node1) +} diff --git a/Trees/Binary Trees/dfs.java b/Trees/Binary Trees/dfs.java new file mode 100644 index 00000000..acf9a7ff --- /dev/null +++ b/Trees/Binary Trees/dfs.java @@ -0,0 +1,96 @@ +// Implementation of Depth First Search +/* + This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. + It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. + The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. + The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper + function recursively visits each node and its children. + Sample Input : + // 1 + // / \ + // 2 3 + // / \ / \ + // 4 5 6 7 + Output : 1 2 4 5 3 6 7 + + The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) + in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. + + The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like + structure, where each node has only one child, the maximum depth is equal to the height of the tree. + Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. + In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, + so the space complexity becomes O(V). +*/ +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +// Node represents a node in a graph. +class Node { + int value; + List children; + + public Node(int value) { + this.value = value; + this.children = new ArrayList<>(); + } +} + +// DFS traverses the graph using Depth-First Search starting from the given node. +class DepthFirstSearch { + public static void DFS(Node node) { + // Create a set to keep track of visited nodes. + Set visited = new HashSet<>(); + + // Call the recursive helper function to perform DFS. + dfsHelper(node, visited); + } + + // dfsHelper is a recursive function that performs Depth-First Search on the graph. + private static void dfsHelper(Node node, Set visited) { + // Mark the current node as visited. + visited.add(node); + + // Process the current node (print its value in this case). + System.out.println(node.value); + + // Traverse the children of the current node. + for (Node child : node.children) { + // If the child node has not been visited, recursively call dfsHelper on it. + if (!visited.contains(child)) { + dfsHelper(child, visited); + } + } + } +} + +public class Main { + public static void main(String[] args) { + // Create a sample graph. + // 1 + // / \ + // 2 3 + // / \ / \ + // 4 5 6 7 + Node node1 = new Node(1); + Node node2 = new Node(2); + Node node3 = new Node(3); + Node node4 = new Node(4); + Node node5 = new Node(5); + Node node6 = new Node(6); + Node node7 = new Node(7); + + node1.children.add(node2); + node1.children.add(node3); + node2.children.add(node4); + node2.children.add(node5); + node3.children.add(node6); + node3.children.add(node7); + + // Perform DFS starting from node1. + System.out.println("Depth-First Search:"); + DepthFirstSearch.DFS(node1); + } +} diff --git a/Trees/Binary Trees/dfs.js b/Trees/Binary Trees/dfs.js new file mode 100644 index 00000000..ffcb6027 --- /dev/null +++ b/Trees/Binary Trees/dfs.js @@ -0,0 +1,79 @@ +// Implementation of Depth First Search +/* + This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. + It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. + The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. + The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper + function recursively visits each node and its children. + Sample Input : + // 1 + // / \ + // 2 3 + // / \ / \ + // 4 5 6 7 + Output : 1 2 4 5 3 6 7 + + The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) + in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. + + The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like + structure, where each node has only one child, the maximum depth is equal to the height of the tree. + Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. + In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, + so the space complexity becomes O(V). +*/ +// Node represents a node in a graph. +class Node { + constructor(value) { + this.value = value; + this.children = []; + } +} + +// DFS traverses the graph using Depth-First Search starting from the given node. +function DFS(node) { + // Create a set to keep track of visited nodes. + let visited = new Set(); + + // Call the recursive helper function to perform DFS. + dfsHelper(node, visited); +} + +// dfsHelper is a recursive function that performs Depth-First Search on the graph. +function dfsHelper(node, visited) { + // Mark the current node as visited. + visited.add(node); + + // Process the current node (print its value in this case). + console.log(node.value); + + // Traverse the children of the current node. + for (let child of node.children) { + // If the child node has not been visited, recursively call dfsHelper on it. + if (!visited.has(child)) { + dfsHelper(child, visited); + } + } +} + +// Create a sample graph. +// 1 +// / \ +// 2 3 +// / \ / \ +// 4 5 6 7 +let node1 = new Node(1); +let node2 = new Node(2); +let node3 = new Node(3); +let node4 = new Node(4); +let node5 = new Node(5); +let node6 = new Node(6); +let node7 = new Node(7); + +node1.children = [node2, node3]; +node2.children = [node4, node5]; +node3.children = [node6, node7]; + +// Perform DFS starting from node1. +console.log("Depth-First Search:"); +DFS(node1); diff --git a/Trees/Binary Trees/dfs.py b/Trees/Binary Trees/dfs.py new file mode 100644 index 00000000..8a8014ec --- /dev/null +++ b/Trees/Binary Trees/dfs.py @@ -0,0 +1,74 @@ +''' + Implementation of Depth First Search + + This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. + It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. + The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. + The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper + function recursively visits each node and its children. + Sample Input : + // 1 + // / \ + // 2 3 + // / \ / \ + // 4 5 6 7 + Output : 1 2 4 5 3 6 7 + + The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) + in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. + + The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like + structure, where each node has only one child, the maximum depth is equal to the height of the tree. + Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. + In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, + so the space complexity becomes O(V). +''' +# Node represents a node in a graph. +class Node: + def __init__(self, value): + self.value = value + self.children = [] + +# DFS traverses the graph using Depth-First Search starting from the given node. +def DFS(node): + # Create a set to keep track of visited nodes. + visited = set() + + # Call the recursive helper function to perform DFS. + dfsHelper(node, visited) + +# dfsHelper is a recursive function that performs Depth-First Search on the graph. +def dfsHelper(node, visited): + # Mark the current node as visited. + visited.add(node) + + # Process the current node (print its value in this case). + print(node.value) + + # Traverse the children of the current node. + for child in node.children: + # If the child node has not been visited, recursively call dfsHelper on it. + if child not in visited: + dfsHelper(child, visited) + +# Create a sample graph. +# 1 +# / \ +# 2 3 +# / \ / \ +# 4 5 6 7 +node1 = Node(1) +node2 = Node(2) +node3 = Node(3) +node4 = Node(4) +node5 = Node(5) +node6 = Node(6) +node7 = Node(7) + +node1.children = [node2, node3] +node2.children = [node4, node5] +node3.children = [node6, node7] + +# Perform DFS starting from node1. +print("Depth-First Search:") +DFS(node1) diff --git a/Trees/Binary Trees/diameter.cpp b/Trees/Binary Trees/diameter.cpp new file mode 100644 index 00000000..e58b4a78 --- /dev/null +++ b/Trees/Binary Trees/diameter.cpp @@ -0,0 +1,120 @@ +/* + Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree + is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. + + Sample Input : + 1 + / \ + 3 2 + / \ + 7 4 + / \ + 8 5 + / \ + 9 6 + Output: 6 + Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 + + Explanation: + This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two + nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. + + The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. + It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. + + In the getTreeInfo function: + + The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and + diameter 0. + + The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left + and right child nodes. + + The longest path passing through the root node is determined by adding the heights of the left and right subtrees. + + The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. + + The current diameter is determined by taking the maximum among the longest path through the root and the maximum + diameter seen so far. + + The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. + + Finally, the function returns a TreeInfo struct with the current height and diameter. + The max function is a helper function that returns the maximum of two integers. + + Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and + diameter of the tree and considering the longest path passing through the root. + + Average case: + Time Complexity O(n) when the tree is balanced + Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree +*/ +#include + +struct BinaryTree { + int value; + BinaryTree* left; + BinaryTree* right; +}; + +struct TreeInfo { + int height; + int diameter; +}; + +// Calculates the diameter of a binary tree. +int BinaryTreeDiameter(BinaryTree* tree) { + return getTreeInfo(tree).diameter; +} + +// Recursively calculates the height and diameter of the binary tree. +TreeInfo getTreeInfo(BinaryTree* tree) { + // Base case: If the tree is nullptr, return height 0 and diameter 0. + if (tree == nullptr) { + return {0, 0}; + } + + // Recursively calculate the height and diameter of the left and right subtrees. + TreeInfo leftTreeInfo = getTreeInfo(tree->left); + TreeInfo rightTreeInfo = getTreeInfo(tree->right); + + // Calculate the longest path passing through the root node. + int longestPathThroughRoot = leftTreeInfo.height + rightTreeInfo.height; + + // Calculate the maximum diameter seen so far. + int maxDiameterSoFar = std::max(leftTreeInfo.diameter, rightTreeInfo.diameter); + + // Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. + int currentDiameter = std::max(longestPathThroughRoot, maxDiameterSoFar); + + // Calculate the current height, which is the maximum height among the left and right subtrees plus 1. + int currentHeight = 1 + std::max(leftTreeInfo.height, rightTreeInfo.height); + + // Return the current height and diameter as the tree information. + return {currentHeight, currentDiameter}; +} + +// Returns the maximum of two integers. +int max(int a, int b) { + return (a > b) ? a : b; +} + +int main() { + // Test the BinaryTreeDiameter function with a sample binary tree + BinaryTree* tree = new BinaryTree{1, + new BinaryTree{2, + new BinaryTree{4, nullptr, nullptr}, + new BinaryTree{5, nullptr, nullptr}}, + new BinaryTree{3, + nullptr, + new BinaryTree{6, + new BinaryTree{7, nullptr, nullptr}, + nullptr}}}; + + int diameter = BinaryTreeDiameter(tree); + std::cout << "Diameter of the binary tree: " << diameter << std::endl; + + delete tree; + + return 0; +} diff --git a/Trees/Binary Trees/diameter.go b/Trees/Binary Trees/diameter.go new file mode 100644 index 00000000..af749bdb --- /dev/null +++ b/Trees/Binary Trees/diameter.go @@ -0,0 +1,105 @@ +/* + Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree + is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. + + Sample Input : + 1 + / \ + 3 2 + / \ + 7 4 + / \ + 8 5 + / \ + 9 6 + Output: 6 + Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 + + Explanation: + This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two + nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. + + The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. + It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. + + In the getTreeInfo function: + + The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and + diameter 0. + + The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left + and right child nodes. + + The longest path passing through the root node is determined by adding the heights of the left and right subtrees. + + The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. + + The current diameter is determined by taking the maximum among the longest path through the root and the maximum + diameter seen so far. + + The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. + + Finally, the function returns a TreeInfo struct with the current height and diameter. + The max function is a helper function that returns the maximum of two integers. + + Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and + diameter of the tree and considering the longest path passing through the root. + + Average case: + Time Complexity O(n) when the tree is balanced + Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree +*/ +package main + +// This is an input class. Do not edit. +type BinaryTree struct { + Value int + + Left *BinaryTree + Right *BinaryTree +} + +type TreeInfo struct { + height int + diameter int +} + +// Calculates the diameter of a binary tree. +func BinaryTreeDiameter(tree *BinaryTree) int { + return getTreeInfo(tree).diameter +} + +// Recursively calculates the height and diameter of the binary tree. +func getTreeInfo(tree *BinaryTree) TreeInfo { + // Base case: If the tree is nil, return height 0 and diameter 0. + if tree == nil { + return TreeInfo{0, 0} + } + + // Recursively calculate the height and diameter of the left and right subtrees. + leftTreeInfo := getTreeInfo(tree.Left) + rightTreeInfo := getTreeInfo(tree.Right) + + // Calculate the longest path passing through the root node. + longestPathThroughRoot := leftTreeInfo.height + rightTreeInfo.height + + // Calculate the maximum diameter seen so far. + maxDiameterSoFar := max(leftTreeInfo.diameter, rightTreeInfo.diameter) + + // Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. + currentDiameter := max(longestPathThroughRoot, maxDiameterSoFar) + + // Calculate the current height, which is the maximum height among the left and right subtrees plus 1. + currentHeight := 1 + max(leftTreeInfo.height, rightTreeInfo.height) + + // Return the current height and diameter as the tree information. + return TreeInfo{currentHeight, currentDiameter} +} + +// Returns the maximum of two integers. +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/Trees/Binary Trees/diameter.java b/Trees/Binary Trees/diameter.java new file mode 100644 index 00000000..6e0addb2 --- /dev/null +++ b/Trees/Binary Trees/diameter.java @@ -0,0 +1,118 @@ +/* + Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree + is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. + + Sample Input : + 1 + / \ + 3 2 + / \ + 7 4 + / \ + 8 5 + / \ + 9 6 + Output: 6 + Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 + + Explanation: + This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two + nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. + + The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. + It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. + + In the getTreeInfo function: + + The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and + diameter 0. + + The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left + and right child nodes. + + The longest path passing through the root node is determined by adding the heights of the left and right subtrees. + + The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. + + The current diameter is determined by taking the maximum among the longest path through the root and the maximum + diameter seen so far. + + The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. + + Finally, the function returns a TreeInfo struct with the current height and diameter. + The max function is a helper function that returns the maximum of two integers. + + Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and + diameter of the tree and considering the longest path passing through the root. + + Average case: + Time Complexity O(n) when the tree is balanced + Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree +*/ +public class BinaryTree { + int value; + BinaryTree left; + BinaryTree right; + + public BinaryTree(int value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +class TreeInfo { + int height; + int diameter; + + public TreeInfo(int height, int diameter) { + this.height = height; + this.diameter = diameter; + } +} + +public class Main { + // Calculates the diameter of a binary tree. + public static int binaryTreeDiameter(BinaryTree tree) { + return getTreeInfo(tree).diameter; + } + + // Recursively calculates the height and diameter of the binary tree. + private static TreeInfo getTreeInfo(BinaryTree tree) { + // Base case: If the tree is null, return height 0 and diameter 0. + if (tree == null) { + return new TreeInfo(0, 0); + } + + // Recursively calculate the height and diameter of the left and right subtrees. + TreeInfo leftTreeInfo = getTreeInfo(tree.left); + TreeInfo rightTreeInfo = getTreeInfo(tree.right); + + // Calculate the longest path passing through the root node. + int longestPathThroughRoot = leftTreeInfo.height + rightTreeInfo.height; + + // Calculate the maximum diameter seen so far. + int maxDiameterSoFar = Math.max(leftTreeInfo.diameter, rightTreeInfo.diameter); + + // Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. + int currentDiameter = Math.max(longestPathThroughRoot, maxDiameterSoFar); + + // Calculate the current height, which is the maximum height among the left and right subtrees plus 1. + int currentHeight = Math.max(leftTreeInfo.height, rightTreeInfo.height) + 1; + + // Return the current height and diameter as the tree information. + return new TreeInfo(currentHeight, currentDiameter); + } + + public static void main(String[] args) { + // Example usage + BinaryTree tree = new BinaryTree(1); + tree.left = new BinaryTree(2); + tree.right = new BinaryTree(3); + tree.left.left = new BinaryTree(4); + tree.left.right = new BinaryTree(5); + + int diameter = binaryTreeDiameter(tree); + System.out.println("Diameter of the binary tree: " + diameter); + } +} diff --git a/Trees/Binary Trees/diameter.js b/Trees/Binary Trees/diameter.js new file mode 100644 index 00000000..934f4097 --- /dev/null +++ b/Trees/Binary Trees/diameter.js @@ -0,0 +1,108 @@ +/* + Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree + is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. + + Sample Input : + 1 + / \ + 3 2 + / \ + 7 4 + / \ + 8 5 + / \ + 9 6 + Output: 6 + Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 + + Explanation: + This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two + nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. + + The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. + It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. + + In the getTreeInfo function: + + The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and + diameter 0. + + The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left + and right child nodes. + + The longest path passing through the root node is determined by adding the heights of the left and right subtrees. + + The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. + + The current diameter is determined by taking the maximum among the longest path through the root and the maximum + diameter seen so far. + + The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. + + Finally, the function returns a TreeInfo struct with the current height and diameter. + The max function is a helper function that returns the maximum of two integers. + + Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and + diameter of the tree and considering the longest path passing through the root. + + Average case: + Time Complexity O(n) when the tree is balanced + Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree +*/ +class BinaryTree { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +class TreeInfo { + constructor(height, diameter) { + this.height = height; + this.diameter = diameter; + } +} + +function binaryTreeDiameter(tree) { + return getTreeInfo(tree).diameter; +} + +function getTreeInfo(tree) { + // Base case: If the tree is null, return height 0 and diameter 0. + if (tree === null) { + return new TreeInfo(0, 0); + } + + // Recursively calculate the height and diameter of the left and right subtrees. + const leftTreeInfo = getTreeInfo(tree.left); + const rightTreeInfo = getTreeInfo(tree.right); + + // Calculate the longest path passing through the root node. + const longestPathThroughRoot = leftTreeInfo.height + rightTreeInfo.height; + + // Calculate the maximum diameter seen so far. + const maxDiameterSoFar = Math.max( + leftTreeInfo.diameter, + rightTreeInfo.diameter + ); + + // Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. + const currentDiameter = Math.max(longestPathThroughRoot, maxDiameterSoFar); + + // Calculate the current height, which is the maximum height among the left and right subtrees plus 1. + const currentHeight = Math.max(leftTreeInfo.height, rightTreeInfo.height) + 1; + + // Return the current height and diameter as the tree information. + return new TreeInfo(currentHeight, currentDiameter); +} + +// Example usage +const tree = new BinaryTree(1); +tree.left = new BinaryTree(2); +tree.right = new BinaryTree(3); +tree.left.left = new BinaryTree(4); +tree.left.right = new BinaryTree(5); + +const diameter = binaryTreeDiameter(tree); +console.log("Diameter of the binary tree:", diameter); diff --git a/Trees/Binary Trees/diameter.py b/Trees/Binary Trees/diameter.py new file mode 100644 index 00000000..49def1bd --- /dev/null +++ b/Trees/Binary Trees/diameter.py @@ -0,0 +1,98 @@ +''' + Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree + is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. + + Sample Input : + 1 + / \ + 3 2 + / \ + 7 4 + / \ + 8 5 + / \ + 9 6 + Output: 6 + Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 + + Explanation: + This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two + nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. + + The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. + It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. + + In the getTreeInfo function: + + The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and + diameter 0. + + The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left + and right child nodes. + + The longest path passing through the root node is determined by adding the heights of the left and right subtrees. + + The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. + + The current diameter is determined by taking the maximum among the longest path through the root and the maximum + diameter seen so far. + + The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. + + Finally, the function returns a TreeInfo struct with the current height and diameter. + The max function is a helper function that returns the maximum of two integers. + + Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and + diameter of the tree and considering the longest path passing through the root. + + Average case: + Time Complexity O(n) when the tree is balanced + Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree +''' +class BinaryTree: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + +class TreeInfo: + def __init__(self, height, diameter): + self.height = height + self.diameter = diameter + +def binary_tree_diameter(tree): + return get_tree_info(tree).diameter + +def get_tree_info(tree): + # Base case: If the tree is None, return height 0 and diameter 0. + if tree is None: + return TreeInfo(0, 0) + + # Recursively calculate the height and diameter of the left and right subtrees. + left_tree_info = get_tree_info(tree.left) + right_tree_info = get_tree_info(tree.right) + + # Calculate the longest path passing through the root node. + longest_path_through_root = left_tree_info.height + right_tree_info.height + + # Calculate the maximum diameter seen so far. + max_diameter_so_far = max(left_tree_info.diameter, right_tree_info.diameter) + + # Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. + current_diameter = max(longest_path_through_root, max_diameter_so_far) + + # Calculate the current height, which is the maximum height among the left and right subtrees plus 1. + current_height = max(left_tree_info.height, right_tree_info.height) + 1 + + # Return the current height and diameter as the tree information. + return TreeInfo(current_height, current_diameter) + +# Example usage +tree = BinaryTree(1) +tree.left = BinaryTree(2) +tree.right = BinaryTree(3) +tree.left.left = BinaryTree(4) +tree.left.right = BinaryTree(5) + +diameter = binary_tree_diameter(tree) +print("Diameter of the binary tree:", diameter) diff --git a/Trees/Binary Trees/find_branch_sum.go b/Trees/Binary Trees/find_branch_sum.go new file mode 100644 index 00000000..41f9bd28 --- /dev/null +++ b/Trees/Binary Trees/find_branch_sum.go @@ -0,0 +1,55 @@ +/* + Write a function that takes in a Binary Tree and returns a list of its branch + sums ordered from leftmost branch sum to rightmost branch sum. + + + A branch sum is the sum of all values in a Binary Tree branch. A Binary Tree + branch is a path of nodes in a tree that starts at the root node and ends at + any leaf node. + + Sample Input + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + / \ / + 8 9 10 + + Output:[15, 16, 18, 10, 11] + +*/ + +/* + As you recursively traverse the tree, if you reach a leaf node (a node with no "left" or "right" Binary Tree nodes), + add the relevant running sum that you've calculated to a list of sums (which you'll also have to pass to the recursive + function). If you reach a node that isn't a leaf node, keep recursively traversing its children nodes, + passing the correctly updated running sum to them. + + Time and Space complexity : O(n) time | O(n) space - where n is the number of nodes in the Binary Tree +*/ +package main + +type BinaryTree struct { + Value int + Left *BinaryTree + Right *BinaryTree +} + +func BranchSums(root *BinaryTree) []int { + sums := []int{} + calculateBranchSums(root, 0, &sums) + return sums +} + +func calculateBranchSums(node *BinaryTree, runningSum int, sums *[]int) { + if node == nil { + return + } + runningSum += node.Value + if node.Left == nil && node.Right == nil { + *sums = append(*sums, runningSum) + } + calculateBranchSums(node.Left, runningSum, sums) + calculateBranchSums(node.Right, runningSum, sums) +} \ No newline at end of file diff --git a/Trees/Binary Trees/find_max.go b/Trees/Binary Trees/find_max.go new file mode 100644 index 00000000..d86907b4 --- /dev/null +++ b/Trees/Binary Trees/find_max.go @@ -0,0 +1,59 @@ +// Find max in Binary tree +package main + +import "math" + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: find maximum in left sub tree, find maximum in right subtree +// compare them with root data and select the one which is giving the max value +// recursive appraoch +func FindMax(root *BinaryTreeNode) int { + max := math.MinInt32 + if root != nil { + rootVal := root.data + left := FindMax(root.left) + right := FindMax(root.right) + if left > max { + max = left + } else { + max = right + } + if rootVal > max { + max = rootVal + } + } + return max +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: Using level order traversal observe the elements data +func FindMaxWithoutRecursion(root *BinaryTreeNode) int { + max := math.MinInt32 + if root == nil { + return max + } + queue := []*BinaryTreeNode{root} + for len(queue) > 0 { + qlen := len(queue) + for i := 0; i < qlen; i++ { + node := queue[0] + if node.data > max { + max = node.data + } + queue = queue[1:] + if node.left != nil { + queue = append(queue, node.left) + } + if node.right != nil { + queue = append(queue, node.right) + } + } + } + return max +} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree_compute_height.cpp b/Trees/Binary Trees/height.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_compute_height.cpp rename to Trees/Binary Trees/height.cpp diff --git a/Trees/Binary Trees/height.go b/Trees/Binary Trees/height.go new file mode 100644 index 00000000..8ec43847 --- /dev/null +++ b/Trees/Binary Trees/height.go @@ -0,0 +1,27 @@ +// Binary tree Find height +package main + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: Recursively calculate height of left and right subtrees of a node +// and assign height to the node as max of heights of two children + 1 +func Height(root *BinaryTreeNode) int { + if root == nil { + return 0 + } else { + // compute depth of each subtree + leftHeight := Height(root.left) + rightHeight := Height(root.right) + if leftHeight > rightHeight { + return leftHeight + 1 + } else { + return rightHeight + 1 + } + + } +} \ No newline at end of file diff --git a/Trees/Binary Trees/height_balanced_binary_tree.cpp b/Trees/Binary Trees/height_balanced_binary_tree.cpp new file mode 100644 index 00000000..5a9a7a59 --- /dev/null +++ b/Trees/Binary Trees/height_balanced_binary_tree.cpp @@ -0,0 +1,151 @@ +/* + You're given the root node of a Binary Tree. Write a function that returns true + if this Binary Tree is height balanced and false if it isn't. + + Explanation: + + The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: + + - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and + pointers to its left and right child nodes. + + - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean + field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing + the height of the tree. + + - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. + It takes the root of the tree as input and returns a boolean value indicating the balance status. + + - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. + It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height + of the tree. + + - In the `getTreeInfo` function, there are two base cases: + - If the current tree node is `nil`, it is considered balanced with height -1. + - If the current tree node is not `nil`, the function recursively calculates the tree information of its + left and right subtrees. + + - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced + (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered + balanced. + + - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and + adding 1. + + - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get + the absolute value of an integer. + + To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with + the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. + + The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the + binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree + information. + + The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. + This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum + depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the + `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. + + Overall, the space complexity is determined by the height of the binary tree, and the time complexity is + determined by the number of nodes in the binary tree. + +*/ +#include +#include + +// Node class represents a node in a binary tree +class BinaryTree { +public: + int value; + BinaryTree* left; + BinaryTree* right; + + BinaryTree(int value) { + this->value = value; + this->left = nullptr; + this->right = nullptr; + } +}; + +// TreeInfo class represents the information of a binary tree, including its balance status and height +class TreeInfo { +public: + bool isBalanced; + int height; + + TreeInfo(bool isBalanced, int height) { + this->isBalanced = isBalanced; + this->height = height; + } +}; + +// heightBalancedBinaryTree checks if a binary tree is height-balanced +bool heightBalancedBinaryTree(BinaryTree* tree) { + // Retrieve the tree information using the helper function + TreeInfo treeInfo = getTreeInfo(tree); + + // Return the balance status of the tree + return treeInfo.isBalanced; +} + +// getTreeInfo retrieves the information of a binary tree, including its balance status and height +TreeInfo getTreeInfo(BinaryTree* tree) { + // Base case: If the tree is nullptr, it is considered balanced with height -1 + if (tree == nullptr) { + return TreeInfo(true, -1); + } + + // Recursively calculate the tree information of the left and right subtrees + TreeInfo leftSubtreeInfo = getTreeInfo(tree->left); + TreeInfo rightSubtreeInfo = getTreeInfo(tree->right); + + // Check if both left and right subtrees are balanced and their height difference is at most 1 + bool isBalanced = leftSubtreeInfo.isBalanced && rightSubtreeInfo.isBalanced && + std::abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1; + + // Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1 + int height = std::max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1; + + // Create and return the tree information + return TreeInfo(isBalanced, height); +} + +// Helper function to create a binary tree +BinaryTree* createBinaryTree(int value) { + return new BinaryTree(value); +} + +// Main function +int main() { + // Create a sample binary tree + // 1 + // / \ + // 2 3 + // / \ / + // 4 5 6 + BinaryTree* node1 = createBinaryTree(1); + BinaryTree* node2 = createBinaryTree(2); + BinaryTree* node3 = createBinaryTree(3); + BinaryTree* node4 = createBinaryTree(4); + BinaryTree* node5 = createBinaryTree(5); + BinaryTree* node6 = createBinaryTree(6); + + node1->left = node2; + node1->right = node3; + node2->left = node4; + node2->right = node5; + node3->left = node6; + + // Check if the binary tree is height-balanced + bool isBalanced = heightBalancedBinaryTree(node1); + + // Print the result + if (isBalanced) { + std::cout << "The binary tree is height-balanced." << std::endl; + } else { + std::cout << "The binary tree is not height-balanced." << std::endl; + } + + return 0; +} diff --git a/Trees/Binary Trees/height_balanced_binary_tree.go b/Trees/Binary Trees/height_balanced_binary_tree.go new file mode 100644 index 00000000..ecf82710 --- /dev/null +++ b/Trees/Binary Trees/height_balanced_binary_tree.go @@ -0,0 +1,154 @@ +/* + You're given the root node of a Binary Tree. Write a function that returns true + if this Binary Tree is height balanced and false if it isn't. + + Explanation: + + The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: + + - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and + pointers to its left and right child nodes. + + - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean + field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing + the height of the tree. + + - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. + It takes the root of the tree as input and returns a boolean value indicating the balance status. + + - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. + It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height + of the tree. + + - In the `getTreeInfo` function, there are two base cases: + - If the current tree node is `nil`, it is considered balanced with height -1. + - If the current tree node is not `nil`, the function recursively calculates the tree information of its + left and right subtrees. + + - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced + (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered + balanced. + + - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and + adding 1. + + - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get + the absolute value of an integer. + + To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with + the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. + + The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the + binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree + information. + + The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. + This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum + depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the + `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. + + Overall, the space complexity is determined by the height of the binary tree, and the time complexity is + determined by the number of nodes in the binary tree. + +*/ +package main + +import "fmt" + +// This is an input class. Do not edit. +type BinaryTree struct { + Value int + + Left *BinaryTree + Right *BinaryTree +} + +type TreeeInfo struct { + isBalanced bool + height int +} + +// HeightBalancedBinaryTree checks if a binary tree is height-balanced. +func HeightBalancedBinaryTree(tree *BinaryTree) bool { + // Retrieve the tree information using the helper function. + treeInfo := getTreeInfo(tree) + + // Return the balance status of the tree. + return treeInfo.isBalanced +} + +// getTreeInfo retrieves the information of a binary tree, including its balance status and height. +func getTreeInfo(tree *BinaryTree) TreeeInfo { + // Base case: If the tree is nil, it is considered balanced with height -1. + if tree == nil { + return TreeeInfo{isBalanced: true, height: -1} + } + + // Recursively calculate the tree information of the left and right subtrees. + leftSubtreeInfo := getTreeInfo(tree.Left) + rightSubtreeInfo := getTreeInfo(tree.Right) + + // Check if both left and right subtrees are balanced and their height difference is at most 1. + isBalanced := leftSubtreeInfo.isBalanced && rightSubtreeInfo.isBalanced && + abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1 + + // Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1. + height := max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1 + + // Create and return the tree information. + return TreeeInfo{ + isBalanced: isBalanced, + height: height, + } +} + +// max returns the maximum of two integers. +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// abs returns the absolute value of an integer. +func abs(a int) int { + if a < 0 { + return -a + } + return a +} + +func main() { + // Create a binary tree + tree := &BinaryTree{ + Value: 1, + Left: &BinaryTree{ + Value: 2, + Left: &BinaryTree{ + Value: 4, + }, + Right: &BinaryTree{ + Value: 5, + }, + }, + Right: &BinaryTree{ + Value: 3, + Right: &BinaryTree{ + Value: 6, + Left: &BinaryTree{ + Value: 7, + }, + }, + }, + } + + // Check if the binary tree is height-balanced + isBalanced := HeightBalancedBinaryTree(tree) + + // Output the result + if isBalanced { + fmt.Println("The binary tree is height-balanced.") + } else { + fmt.Println("The binary tree is not height-balanced.") + } +} diff --git a/Trees/Binary Trees/height_balanced_binary_tree.java b/Trees/Binary Trees/height_balanced_binary_tree.java new file mode 100644 index 00000000..da82efce --- /dev/null +++ b/Trees/Binary Trees/height_balanced_binary_tree.java @@ -0,0 +1,146 @@ +/* + You're given the root node of a Binary Tree. Write a function that returns true + if this Binary Tree is height balanced and false if it isn't. + + Explanation: + + The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: + + - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and + pointers to its left and right child nodes. + + - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean + field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing + the height of the tree. + + - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. + It takes the root of the tree as input and returns a boolean value indicating the balance status. + + - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. + It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height + of the tree. + + - In the `getTreeInfo` function, there are two base cases: + - If the current tree node is `nil`, it is considered balanced with height -1. + - If the current tree node is not `nil`, the function recursively calculates the tree information of its + left and right subtrees. + + - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced + (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered + balanced. + + - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and + adding 1. + + - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get + the absolute value of an integer. + + To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with + the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. + + The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the + binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree + information. + + The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. + This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum + depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the + `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. + + Overall, the space complexity is determined by the height of the binary tree, and the time complexity is + determined by the number of nodes in the binary tree. + +*/ +// Node class represents a node in a binary tree +class BinaryTree { + int value; + BinaryTree left; + BinaryTree right; + + BinaryTree(int value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +// TreeInfo class represents the information of a binary tree, including its balance status and height +class TreeInfo { + boolean isBalanced; + int height; + + TreeInfo(boolean isBalanced, int height) { + this.isBalanced = isBalanced; + this.height = height; + } +} + +// heightBalancedBinaryTree checks if a binary tree is height-balanced +class HeightBalancedBinaryTree { + public static boolean heightBalancedBinaryTree(BinaryTree tree) { + // Retrieve the tree information using the helper function + TreeInfo treeInfo = getTreeInfo(tree); + + // Return the balance status of the tree + return treeInfo.isBalanced; + } + + // getTreeInfo retrieves the information of a binary tree, including its balance status and height + private static TreeInfo getTreeInfo(BinaryTree tree) { + // Base case: If the tree is null, it is considered balanced with height -1 + if (tree == null) { + return new TreeInfo(true, -1); + } + + // Recursively calculate the tree information of the left and right subtrees + TreeInfo leftSubtreeInfo = getTreeInfo(tree.left); + TreeInfo rightSubtreeInfo = getTreeInfo(tree.right); + + // Check if both left and right subtrees are balanced and their height difference is at most 1 + boolean isBalanced = leftSubtreeInfo.isBalanced && rightSubtreeInfo.isBalanced && + Math.abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1; + + // Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1 + int height = Math.max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1; + + // Create and return the tree information + return new TreeInfo(isBalanced, height); + } + + // Helper function to create a binary tree + private static BinaryTree createBinaryTree(int value) { + return new BinaryTree(value); + } + + // Main function + public static void main(String[] args) { + // Create a sample binary tree + // 1 + // / \ + // 2 3 + // / \ / + // 4 5 6 + BinaryTree node1 = createBinaryTree(1); + BinaryTree node2 = createBinaryTree(2); + BinaryTree node3 = createBinaryTree(3); + BinaryTree node4 = createBinaryTree(4); + BinaryTree node5 = createBinaryTree(5); + BinaryTree node6 = createBinaryTree(6); + + node1.left = node2; + node1.right = node3; + node2.left = node4; + node2.right = node5; + node3.left = node6; + + // Check if the binary tree is height-balanced + boolean isBalanced = heightBalancedBinaryTree(node1); + + // Print the result + if (isBalanced) { + System.out.println("The binary tree is height-balanced."); + } else { + System.out.println("The binary tree is not height-balanced."); + } + } +} diff --git a/Trees/Binary Trees/height_balanced_binary_tree.js b/Trees/Binary Trees/height_balanced_binary_tree.js new file mode 100644 index 00000000..66d1b308 --- /dev/null +++ b/Trees/Binary Trees/height_balanced_binary_tree.js @@ -0,0 +1,142 @@ +/* + You're given the root node of a Binary Tree. Write a function that returns true + if this Binary Tree is height balanced and false if it isn't. + + Explanation: + + The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: + + - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and + pointers to its left and right child nodes. + + - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean + field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing + the height of the tree. + + - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. + It takes the root of the tree as input and returns a boolean value indicating the balance status. + + - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. + It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height + of the tree. + + - In the `getTreeInfo` function, there are two base cases: + - If the current tree node is `nil`, it is considered balanced with height -1. + - If the current tree node is not `nil`, the function recursively calculates the tree information of its + left and right subtrees. + + - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced + (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered + balanced. + + - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and + adding 1. + + - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get + the absolute value of an integer. + + To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with + the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. + + The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the + binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree + information. + + The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. + This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum + depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the + `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. + + Overall, the space complexity is determined by the height of the binary tree, and the time complexity is + determined by the number of nodes in the binary tree. + +*/ +// Node class represents a node in a binary tree +class BinaryTree { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +// TreeInfo class represents the information of a binary tree, including its balance status and height +class TreeInfo { + constructor(isBalanced, height) { + this.isBalanced = isBalanced; + this.height = height; + } +} + +// heightBalancedBinaryTree checks if a binary tree is height-balanced +function heightBalancedBinaryTree(tree) { + // Retrieve the tree information using the helper function + const treeInfo = getTreeInfo(tree); + + // Return the balance status of the tree + return treeInfo.isBalanced; +} + +// getTreeInfo retrieves the information of a binary tree, including its balance status and height +function getTreeInfo(tree) { + // Base case: If the tree is null, it is considered balanced with height -1 + if (tree === null) { + return new TreeInfo(true, -1); + } + + // Recursively calculate the tree information of the left and right subtrees + const leftSubtreeInfo = getTreeInfo(tree.left); + const rightSubtreeInfo = getTreeInfo(tree.right); + + // Check if both left and right subtrees are balanced and their height difference is at most 1 + const isBalanced = + leftSubtreeInfo.isBalanced && + rightSubtreeInfo.isBalanced && + Math.abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1; + + // Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1 + const height = Math.max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1; + + // Create and return the tree information + return new TreeInfo(isBalanced, height); +} + +// Helper function to create a binary tree +function createBinaryTree(value) { + return new BinaryTree(value); +} + +// Main function +function main() { + // Create a sample binary tree + // 1 + // / \ + // 2 3 + // / \ / + // 4 5 6 + const node1 = createBinaryTree(1); + const node2 = createBinaryTree(2); + const node3 = createBinaryTree(3); + const node4 = createBinaryTree(4); + const node5 = createBinaryTree(5); + const node6 = createBinaryTree(6); + + node1.left = node2; + node1.right = node3; + node2.left = node4; + node2.right = node5; + node3.left = node6; + + // Check if the binary tree is height-balanced + const isBalanced = heightBalancedBinaryTree(node1); + + // Print the result + if (isBalanced) { + console.log("The binary tree is height-balanced."); + } else { + console.log("The binary tree is not height-balanced."); + } +} + +// Call the main function +main(); diff --git a/Trees/Binary Trees/height_balanced_binary_tree.py b/Trees/Binary Trees/height_balanced_binary_tree.py new file mode 100644 index 00000000..c84809fd --- /dev/null +++ b/Trees/Binary Trees/height_balanced_binary_tree.py @@ -0,0 +1,137 @@ +''' + You're given the root node of a Binary Tree. Write a function that returns true + if this Binary Tree is height balanced and false if it isn't. + + Explanation: + + The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: + + - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and + pointers to its left and right child nodes. + + - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean + field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing + the height of the tree. + + - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. + It takes the root of the tree as input and returns a boolean value indicating the balance status. + + - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. + It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height + of the tree. + + - In the `getTreeInfo` function, there are two base cases: + - If the current tree node is `nil`, it is considered balanced with height -1. + - If the current tree node is not `nil`, the function recursively calculates the tree information of its + left and right subtrees. + + - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced + (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered + balanced. + + - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and + adding 1. + + - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get + the absolute value of an integer. + + To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with + the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. + + The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the + binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree + information. + + The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. + This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum + depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the + `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. + + Overall, the space complexity is determined by the height of the binary tree, and the time complexity is + determined by the number of nodes in the binary tree. + +''' + +# This is an input class. Do not edit. +class BinaryTree: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + +class TreeInfo: + def __init__(self, is_balanced, height): + self.is_balanced = is_balanced + self.height = height + +# Checks if a binary tree is height-balanced. +def height_balanced_binary_tree(tree): + # Retrieve the tree information using the helper function. + tree_info = get_tree_info(tree) + + # Return the balance status of the tree. + return tree_info.is_balanced + +# Retrieves the information of a binary tree, including its balance status and height. +def get_tree_info(tree): + # Base case: If the tree is None, it is considered balanced with height -1. + if tree is None: + return TreeInfo(True, -1) + + # Recursively calculate the tree information of the left and right subtrees. + left_subtree_info = get_tree_info(tree.left) + right_subtree_info = get_tree_info(tree.right) + + # Check if both left and right subtrees are balanced and their height difference is at most 1. + is_balanced = left_subtree_info.is_balanced and right_subtree_info.is_balanced and \ + abs(left_subtree_info.height - right_subtree_info.height) <= 1 + + # Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1. + height = max(left_subtree_info.height, right_subtree_info.height) + 1 + + # Create and return the tree information. + return TreeInfo(is_balanced, height) + +# Returns the maximum of two integers. +def max(a, b): + return a if a > b else b + +# Returns the absolute value of an integer. +def abs(a): + return -a if a < 0 else a + +# Main function +def main(): + # Create a sample binary tree + # 1 + # / \ + # 2 3 + # / \ / + # 4 5 6 + node1 = BinaryTree(1) + node2 = BinaryTree(2) + node3 = BinaryTree(3) + node4 = BinaryTree(4) + node5 = BinaryTree(5) + node6 = BinaryTree(6) + + node1.left = node2 + node1.right = node3 + node2.left = node4 + node2.right = node5 + node3.left = node6 + + # Check if the binary tree is height-balanced + is_balanced = height_balanced_binary_tree(node1) + + # Print the result + if is_balanced: + print("The binary tree is height-balanced.") + else: + print("The binary tree is not height-balanced.") + +# Call the main function +if __name__ == "__main__": + main() + + diff --git a/Trees/Binary Trees/inorder_traversal.cpp b/Trees/Binary Trees/inorder_traversal.cpp new file mode 100644 index 00000000..1f303f0e --- /dev/null +++ b/Trees/Binary Trees/inorder_traversal.cpp @@ -0,0 +1,83 @@ +// In-Order Traversal of a Binary-Tree +// Program Author : Abhisek Kumar Gupta +/* + 40 + / \ + 10 30 + / \ / \ + 5 -1 -1 28 + / \ / \ + 1 -1 15 20 + / \ /\ /\ + -1 -1 -1 -1 -1 -1 + Input : 40 10 5 1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 + Output : 1->5->10->40->30->15->28->20 +*/ +#include +#include +#include + +// Definition for a binary tree node. +struct TreeNode { + int val; + TreeNode* left; + TreeNode* right; + TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} +}; + +class Solution { +public: + std::vector inorderTraversal(TreeNode* root) { + // Vector to store the in-order traversal result + std::vector result; + + // Stack to simulate the recursive call stack + std::stack stack; + + // Current node starts from the root + TreeNode* current = root; + + // Continue traversal until the current node is null and the stack is empty + while (current != nullptr || !stack.empty()) { + // Traverse all the way to the leftmost node, pushing each node onto the stack + while (current != nullptr) { + stack.push(current); + current = current->left; + } + + // Pop the top node from the stack (current leftmost node) + current = stack.top(); + stack.pop(); + + // Add the value of the current node to the result vector + result.push_back(current->val); + + // Move to the right subtree of the current node + current = current->right; + } + + // Return the final in-order traversal result + return result; + } +}; + +// Example usage +int main() { + // Create a sample binary tree + TreeNode* root = new TreeNode(1); + root->right = new TreeNode(2); + root->right->left = new TreeNode(3); + + // Perform in-order traversal + Solution solution; + std::vector result = solution.inorderTraversal(root); + + // Print the result + for (int val : result) { + std::cout << val << " "; + } + + // Output: 1 3 2 + return 0; +} + diff --git a/Trees/Binary Trees/inorder_traversal.go b/Trees/Binary Trees/inorder_traversal.go new file mode 100644 index 00000000..564404c9 --- /dev/null +++ b/Trees/Binary Trees/inorder_traversal.go @@ -0,0 +1,46 @@ +package main + +import "fmt" + +// TreeNode definition +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func inorderTraversal(root *TreeNode) []int { + var result []int + var stack []*TreeNode + current := root + + for current != nil || len(stack) > 0 { + // Traverse all the way to the leftmost node, pushing each node onto the stack + for current != nil { + stack = append(stack, current) + current = current.Left + } + + // Pop the top node from the stack (current leftmost node) + current, stack = stack[len(stack)-1], stack[:len(stack)-1] + + // Add the value of the current node to the result slice + result = append(result, current.Val) + + // Move to the right subtree of the current node + current = current.Right + } + + return result +} + +func main() { + // Create a sample binary tree + root := &TreeNode{Val: 1, Right: &TreeNode{Val: 2, Left: &TreeNode{Val: 3}}} + + // Perform in-order traversal + result := inorderTraversal(root) + + // Print the result + fmt.Println(result) // Output: [1 3 2] +} diff --git a/Trees/Binary Trees/inorder_traversal.java b/Trees/Binary Trees/inorder_traversal.java new file mode 100644 index 00000000..7269a032 --- /dev/null +++ b/Trees/Binary Trees/inorder_traversal.java @@ -0,0 +1,53 @@ + +/** + * Given the root of a binary tree, return the inorder traversal of its nodes' values. + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +/** + + */ +class Solution { + public List inorderTraversal(TreeNode root) { + // List to store the in-order traversal result + List result = new ArrayList<>(); + + // Stack to simulate the recursive call stack + Stack stack = new Stack<>(); + + // Current node starts from the root + TreeNode current = root; + + // Continue traversal until current node is null and stack is empty + while (current != null || !stack.isEmpty()) { + // Traverse all the way to the leftmost node, pushing each node onto the stack + while (current != null) { + stack.push(current); + current = current.left; + } + + // Pop the top node from the stack (current leftmost node) + current = stack.pop(); + + // Add the value of the current node to the result list + result.add(current.val); + + // Move to the right subtree of the current node + current = current.right; + } + + // Return the final in-order traversal result + return result; + } +} diff --git a/Trees/Binary Trees/inorder_traversal.js b/Trees/Binary Trees/inorder_traversal.js new file mode 100644 index 00000000..2ebc4f1e --- /dev/null +++ b/Trees/Binary Trees/inorder_traversal.js @@ -0,0 +1,43 @@ +class TreeNode { + constructor(val) { + this.val = val; + this.left = this.right = null; + } +} + +class Solution { + inorderTraversal(root) { + const result = []; + const stack = []; + let current = root; + + while (current || stack.length > 0) { + // Traverse all the way to the leftmost node, pushing each node onto the stack + while (current) { + stack.push(current); + current = current.left; + } + + // Pop the top node from the stack (current leftmost node) + current = stack.pop(); + + // Add the value of the current node to the result array + result.push(current.val); + + // Move to the right subtree of the current node + current = current.right; + } + + return result; + } +} + +// Example usage +const root = new TreeNode(1); +root.right = new TreeNode(2); +root.right.left = new TreeNode(3); + +const solution = new Solution(); +const result = solution.inorderTraversal(root); + +console.log(result); // Output: [1, 3, 2] diff --git a/Trees/Binary Trees/inorder_traversal.py b/Trees/Binary Trees/inorder_traversal.py new file mode 100644 index 00000000..acd1982c --- /dev/null +++ b/Trees/Binary Trees/inorder_traversal.py @@ -0,0 +1,37 @@ +class TreeNode: + def __init__(self, val): + self.val = val + self.left = self.right = None + +class Solution: + def inorderTraversal(self, root): + result = [] + stack = [] + current = root + + while current or stack: + # Traverse all the way to the leftmost node, pushing each node onto the stack + while current: + stack.append(current) + current = current.left + + # Pop the top node from the stack (current leftmost node) + current = stack.pop() + + # Add the value of the current node to the result list + result.append(current.val) + + # Move to the right subtree of the current node + current = current.right + + return result + +# Example usage +root = TreeNode(1) +root.right = TreeNode(2) +root.right.left = TreeNode(3) + +solution = Solution() +result = solution.inorderTraversal(root) + +print(result) # Output: [1, 3, 2] diff --git a/Trees/Binary Trees/invert.cpp b/Trees/Binary Trees/invert.cpp new file mode 100644 index 00000000..abc22ee3 --- /dev/null +++ b/Trees/Binary Trees/invert.cpp @@ -0,0 +1,52 @@ +// Invert Binary tree +#include + +class BinaryTreeNode { +public: + int data; + BinaryTreeNode* left; + BinaryTreeNode* right; + + BinaryTreeNode(int val) : data(val), left(nullptr), right(nullptr) {} +}; + +BinaryTreeNode* invertTree(BinaryTreeNode* root) { + if (root != nullptr) { + root->left = invertTree(root->right); + root->right = invertTree(root->left); + } + return root; +} + +BinaryTreeNode* invertTree2(BinaryTreeNode* root) { + if (root != nullptr) { + // swap the pointers in this node + BinaryTreeNode* temp = root->left; + root->left = root->right; + root->right = temp; + + invertTree2(root->left); + invertTree2(root->right); + } + return root; +} + +int main() { + // Example usage: + // Construct a binary tree + BinaryTreeNode* root = new BinaryTreeNode(1); + root->left = new BinaryTreeNode(2); + root->right = new BinaryTreeNode(3); + root->left->left = new BinaryTreeNode(4); + root->left->right = new BinaryTreeNode(5); + + // Invert the binary tree using the first approach + BinaryTreeNode* invertedRoot = invertTree(root); + + // Invert the binary tree using the second approach + BinaryTreeNode* invertedRoot2 = invertTree2(root); + + // Additional code for printing or further usage... + + return 0; +} diff --git a/Trees/Binary Trees/invert.go b/Trees/Binary Trees/invert.go new file mode 100644 index 00000000..ff5ae6b7 --- /dev/null +++ b/Trees/Binary Trees/invert.go @@ -0,0 +1,32 @@ +// Invert Binary tree +package main + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: The inverse of an empty tree is an empty tree +// The inverse of a tree with root r, and subtrees right and left is a tree with +// root, whose right subtree is the inverse of left and whoose left subtree +// is the inverse of right +func InvertTree(root *BinaryTreeNode) *BinaryTreeNode { + if root != nil { + root.left, root.right = InvertTree(root.right), InvertTree(root.left) + } + return root +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Method2 : swap pointers +func InvertTree2(root *BinaryTreeNode) *BinaryTreeNode { + if root == nil { + return root + } + // swap the pointers in this node + root.left, root.right = root.right, root.left + InvertTree(root.left) + InvertTree(root.right) + return root +} diff --git a/Trees/Binary Trees/invert.java b/Trees/Binary Trees/invert.java new file mode 100644 index 00000000..6a01db3b --- /dev/null +++ b/Trees/Binary Trees/invert.java @@ -0,0 +1,61 @@ +class BinaryTreeNode { + int data; + BinaryTreeNode left, right; + + public BinaryTreeNode(int data) { + this.data = data; + this.left = this.right = null; + } +} + +public class BinaryTreeInverter { + // Time Complexity: O(n). Space Complexity: O(n). + // Approach: The inverse of an empty tree is an empty tree + // The inverse of a tree with root r, and subtrees right and left is a tree with + // root, whose right subtree is the inverse of left and whose left subtree + // is the inverse of right + public BinaryTreeNode invertTree(BinaryTreeNode root) { + if (root != null) { + root.left = invertTree(root.right); + root.right = invertTree(root.left); + } + return root; + } + + // Time Complexity: O(n). Space Complexity: O(1). + // Method2: swap pointers + public BinaryTreeNode invertTree2(BinaryTreeNode root) { + if (root != null) { + // swap the pointers in this node + BinaryTreeNode temp = root.left; + root.left = root.right; + root.right = temp; + + invertTree2(root.left); + invertTree2(root.right); + } + return root; + } + + // Additional methods or code as needed... + + public static void main(String[] args) { + // Example usage: + // Construct a binary tree + BinaryTreeNode root = new BinaryTreeNode(1); + root.left = new BinaryTreeNode(2); + root.right = new BinaryTreeNode(3); + root.left.left = new BinaryTreeNode(4); + root.left.right = new BinaryTreeNode(5); + + BinaryTreeInverter inverter = new BinaryTreeInverter(); + + // Invert the binary tree using the first approach + BinaryTreeNode invertedRoot = inverter.invertTree(root); + + // Invert the binary tree using the second approach + BinaryTreeNode invertedRoot2 = inverter.invertTree2(root); + + // Additional code for printing or further usage... + } +} diff --git a/Trees/Binary Trees/invert.js b/Trees/Binary Trees/invert.js new file mode 100644 index 00000000..a4bf6822 --- /dev/null +++ b/Trees/Binary Trees/invert.js @@ -0,0 +1,61 @@ +class BinaryTreeNode { + constructor(data) { + this.data = data; + this.left = this.right = null; + } +} + +class BinaryTreeInverter { + // Time Complexity: O(n). Space Complexity: O(n). + // Approach: The inverse of an empty tree is an empty tree + // The inverse of a tree with root r, and subtrees right and left is a tree with + // root, whose right subtree is the inverse of left and whoose left subtree + // is the inverse of right + invertTree(root) { + if (root !== null) { + root.left = this.invertTree(root.right); + root.right = this.invertTree(root.left); + } + return root; + } + + // Time Complexity: O(n). Space Complexity: O(1). + // Method2: swap pointers + invertTree2(root) { + if (root !== null) { + // swap the pointers in this node + let temp = root.left; + root.left = root.right; + root.right = temp; + + this.invertTree2(root.left); + this.invertTree2(root.right); + } + return root; + } + + // Additional methods or code as needed... + + // Example usage: + static main() { + // Construct a binary tree + let root = new BinaryTreeNode(1); + root.left = new BinaryTreeNode(2); + root.right = new BinaryTreeNode(3); + root.left.left = new BinaryTreeNode(4); + root.left.right = new BinaryTreeNode(5); + + let inverter = new BinaryTreeInverter(); + + // Invert the binary tree using the first approach + let invertedRoot = inverter.invertTree(root); + + // Invert the binary tree using the second approach + let invertedRoot2 = inverter.invertTree2(root); + + // Additional code for printing or further usage... + } +} + +// Example usage: +BinaryTreeInverter.main(); diff --git a/Trees/Binary Trees/invert.py b/Trees/Binary Trees/invert.py new file mode 100644 index 00000000..1d2201d2 --- /dev/null +++ b/Trees/Binary Trees/invert.py @@ -0,0 +1,33 @@ +# Invert Binary tree +class BinaryTreeNode: + def __init__(self, data): + self.left = None + self.data = data + self.right = None + +def invert_tree(root): + if root: + root.left, root.right = invert_tree(root.right), invert_tree(root.left) + return root + +def invert_tree2(root): + if root is not None: + # swap the pointers in this node + root.left, root.right = root.right, root.left + invert_tree2(root.left) + invert_tree2(root.right) + return root + +# Example usage: +# Assuming you have a binary tree +root = BinaryTreeNode(1) +root.left = BinaryTreeNode(2) +root.right = BinaryTreeNode(3) +root.left.left = BinaryTreeNode(4) +root.left.right = BinaryTreeNode(5) + +# Invert the binary tree using the first approach +inverted_root = invert_tree(root) + +# Invert the binary tree using the second approach +inverted_root2 = invert_tree2(root) diff --git a/Trees/Binary Trees/is_symmetric.cpp b/Trees/Binary Trees/is_symmetric.cpp new file mode 100644 index 00000000..ed9fa410 --- /dev/null +++ b/Trees/Binary Trees/is_symmetric.cpp @@ -0,0 +1,153 @@ +/* + Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical + if the left and right subtrees are mirror images of each other. + + Explanation: + + 1. The code defines a class `BinaryTree` representing a binary tree node. It has an `int` value and pointers + to its left and right children. + 2. The `SymmetricalTree` function is the main entry point. It calls the helper function `treesAreMirrored` + to check if the left and right subtrees are mirrored. + 3. The `treesAreMirrored` function checks if two binary trees are mirrored. It uses recursion to compare + corresponding nodes in the left and right subtrees. + 4. In the `treesAreMirrored` function, the base case checks if both the left and right trees are non-null + and have the same value. If so, it recursively checks if their subtrees are mirrored. + 5. If either the left or right tree is null or their values are not equal, they are not mirrored. + If both the left and right trees are null, they are considered mirrored. + 6. In the `main` function, a binary tree is created for testing purposes. + 7. The `SymmetricalTree` function is called to check if the binary tree is symmetrical. + 8. The result is printed to the console. + 9. Memory cleanup is performed by deleting the dynamically allocated nodes. + + The time and space complexity of the given code snippet can be analyzed as follows: + + 1. Time Complexity: + - The `SymmetricalTree` function calls the `treesAreMirrored` function, which performs a recursive traversal of the binary tree. + - In the worst case, the recursion visits each node once, so the time complexity is O(N), where N is the number of nodes in the tree. + + 2. Space Complexity: + - The space complexity is determined by the maximum depth of the recursion stack. + - In the worst case, the binary tree is linear, resulting in a recursion depth of N, where N is the number of nodes in the tree. + - Therefore, the space complexity is O(N) due to the recursion stack usage. + + It's important to note that the space complexity can be optimized by using an iterative approach instead of recursion. By using an iterative algorithm that leverages a stack or queue to perform a level-order traversal, we can achieve a space complexity of O(W), where W is the maximum width (number of nodes at the same level) of the binary tree. + +*/ +#include + +using namespace std; + +// This is an input class. Do not edit. +class BinaryTree { +public: + int Value; + BinaryTree* Left; + BinaryTree* Right; +}; + +// SymmetricalTree checks if a binary tree is symmetrical. +bool SymmetricalTreeRecursive(BinaryTree* tree) { + // Call the helper function to check if the left and right subtrees are mirrored. + return treesAreMirrored(tree->Left, tree->Right); +} + +// treesAreMirrored checks if two binary trees are mirrored. +bool treesAreMirrored(BinaryTree* left, BinaryTree* right) { + // Base case: If both left and right trees are non-null and have the same value, + // recursively check if their subtrees are mirrored. + if (left != nullptr && right != nullptr && left->Value == right->Value) { + return treesAreMirrored(left->Left, right->Right) && treesAreMirrored(left->Right, right->Left); + } + + // If either left or right tree is null or their values are not equal, they are not mirrored. + // Also, if both left and right trees are null, they are considered mirrored. + return left == right; +} + +// Approach 2: Iterative Approach using Stack +/* + In this iterative approach, we use two stacks (stackLeft and stackRight) to perform a mirror traversal of the + left and right subtrees. The process is similar to the original code snippet but implemented iteratively + using a while loop and stacks. The stacks are initialized with the left and right children of the root node, + and in each iteration, we compare the corresponding nodes from both stacks and check for asymmetry. + The children of the left and right nodes are pushed onto their respective stacks in reverse order to + maintain the mirror traversal. The loop continues until both stacks are empty or an asymmetry is detected. + Finally, the function returns whether the tree is symmetric or not. + + The time complexity of this algorithm is O(n), where n is the number of nodes in the binary tree, as + it traverses each node once. The space complexity is O(max(d, h)), where d is the maximum width of + the tree (number of nodes at the widest level) and h is the height of the tree. The space complexity + depends on the maximum number of nodes stored in the stacks during the traversal. + + +*/ +struct BinaryTree { + int value; + BinaryTree* left; + BinaryTree* right; +}; + +bool SymmetricalTreeIterative(BinaryTree* tree) { + std::stack stackLeft; + std::stack stackRight; + stackLeft.push(tree->left); // Initialize stackLeft with the left child of the root node + stackRight.push(tree->right); // Initialize stackRight with the right child of the root node + + // Perform mirror traversal of the left and right subtrees + while (!stackLeft.empty()) { + BinaryTree* left = stackLeft.top(); + BinaryTree* right = stackRight.top(); + stackLeft.pop(); + stackRight.pop(); + + if (left == nullptr && right == nullptr) { + continue; // Both left and right subtrees are symmetric, continue to the next iteration + } + + if (left == nullptr || right == nullptr || left->value != right->value) { + return false; // Asymmetry detected, tree is not symmetric + } + + // Push the children of left and right onto the respective stacks in reverse order + stackLeft.push(left->left); + stackLeft.push(left->right); + stackRight.push(right->right); + stackRight.push(right->left); + } + + return true; // Tree is symmetric +} + + +int main() { + // Create a binary tree for testing + BinaryTree* tree = new BinaryTree(); + tree->Value = 1; + tree->Left = new BinaryTree(); + tree->Left->Value = 2; + tree->Right = new BinaryTree(); + tree->Right->Value = 2; + tree->Left->Left = new BinaryTree(); + tree->Left->Left->Value = 3; + tree->Right->Right = new BinaryTree(); + tree->Right->Right->Value = 3; + + // Check if the tree is symmetrical + bool isSymmetrical = SymmetricalTree(tree); + + // Output the result + if (isSymmetrical) { + cout << "The binary tree is symmetrical." << endl; + } else { + cout << "The binary tree is not symmetrical." << endl; + } + + // Clean up the allocated memory + delete tree->Left->Left; + delete tree->Right->Right; + delete tree->Left; + delete tree->Right; + delete tree; + + return 0; +} diff --git a/Trees/Binary Trees/is_symmetric.go b/Trees/Binary Trees/is_symmetric.go new file mode 100644 index 00000000..f0df1350 --- /dev/null +++ b/Trees/Binary Trees/is_symmetric.go @@ -0,0 +1,138 @@ +/* + Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical + if the left and right subtrees are mirror images of each other. +*/ +package main + +// This is an input class. Do not edit. +type BinaryTree struct { + Value int + + Left *BinaryTree + Right *BinaryTree +} +/* +The code consists of two functions. The SymmetricalTree function serves as an entry point and checks if the +binary tree is symmetrical by calling the treesAreMirrored helper function with the left and right subtrees +of the root node. + +The treesAreMirrored function is a recursive helper function that checks if two binary trees are mirrored. +It uses a bottom-up approach to compare corresponding nodes of the left and right subtrees. + +The function performs the following steps: + + 1. Base case: If both the left and right trees are non-nil and have the same value, recursively check if + their respective subtrees are mirrored by calling treesAreMirrored with the left subtree's left and + the right subtree's right children, as well as the left subtree's right and the right subtree's left children. + + 2. If either the left or right tree is nil or their values are not equal, they are not mirrored, and the function returns false. + + 3. If both the left and right trees are nil, they are considered mirrored, and the function returns true. + + The recursive nature of the treesAreMirrored function allows it to traverse and compare corresponding nodes + in a symmetrical manner. If the function successfully reaches the base case for all nodes, it indicates + that the binary tree is symmetrical. + + Overall, the code leverages recursion and the concept of mirror images to determine if a binary tree is symmetrical or not. + The time and space complexity of the code snippet can be analyzed as follows: + + Time Complexity: + The time complexity of the SymmetricalTree function primarily depends on the size of the binary tree. + In the worst case, where the tree is symmetric, the function needs to traverse all the nodes of the tree once. + + Let's assume there are 'n' nodes in the tree. In the worst case, the function will visit each node exactly once. + Therefore, the time complexity is O(n), where 'n' is the number of nodes in the binary tree. + + Space Complexity: + The space complexity is determined by the recursive calls and the stack space used during the recursion. + + In the worst case, when the binary tree is highly unbalanced and resembles a linked list, the depth of the + recursion can be 'n' (the number of nodes in the tree). This means that the space required on the function + call stack will be O(n). + + Additionally, the space complexity also includes the space used to store the function arguments and local variables. + However, as these are constant-sized values (pointers), they don't contribute significantly to the overall space complexity. + + Therefore, the overall space complexity is O(n), where 'n' is the number of nodes in the binary tree, considering the worst-case scenario. + + In summary, the time complexity is O(n), and the space complexity is O(n), where 'n' is the number of nodes in the binary tree. +*/ +// Approach 1: Recursive Approach +// SymmetricalTree checks if a binary tree is symmetrical. +func SymmetricalTreerecursive(tree *BinaryTree) bool { + // Call the helper function to check if the left and right subtrees are mirrored. + return treesAreMirrored(tree.Left, tree.Right) +} + +// treesAreMirrored checks if two binary trees are mirrored. +func treesAreMirrored(left, right *BinaryTree) bool { + // Base case: If both left and right trees are non-nil and have the same value, + // recursively check if their subtrees are mirrored. + if left != nil && right != nil && left.Value == right.Value { + return treesAreMirrored(left.Left, right.Right) && treesAreMirrored(left.Right, right.Left) + } + + // If either left or right tree is nil or their values are not equal, they are not mirrored. + // Also, if both left and right trees are nil, they are considered mirrored. + return left == right +} + +/* Explanation: + + The code snippet defines a function SymmetricalTree that checks if a binary tree is symmetrical. + The binary tree is represented by the BinaryTree struct, which has a Value field and pointers to + its left and right child nodes. + + The function uses two stacks, stackLeft and stackRight, to perform a mirror traversal of the + left and right subtrees of the input tree. It starts by pushing the left child of the root + node onto stackLeft and the right child of the root node onto stackRight. + + The function then enters a loop that continues until the stackLeft is empty. In each iteration + of the loop, it pops the top nodes from stackLeft and stackRight, assigning them to variables + left and right, respectively. + + If both left and right are nil, it means the corresponding subtrees are symmetric, so the loop + continues to the next iteration. + + If either left or right is nil, or their values are not equal, it means the tree is not symmetric, + and the function returns false. + + Otherwise, it pushes the left child of left and the right child of right onto stackLeft and stackRight, + respectively, in reverse order. This ensures that the subsequent nodes popped from the stacks are + compared as mirror images. + + Once the loop is completed, and no asymmetry has been detected, the function returns true, indicating + that the binary tree is symmetric. + + The time complexity of this algorithm is O(n), where n is the number of nodes in the binary tree, as + it traverses each node once. The space complexity is O(max(d, h)), where d is the maximum width of + the tree (number of nodes at the widest level) and h is the height of the tree. The space complexity + depends on the maximum number of nodes stored in the stacks during the traversal. +*/ + +// Approach 2: Iterative Approach using Stack +func SymmetricalTreeIterative(tree *BinaryTree) bool { + stackLeft := []*BinaryTree{tree.Left} // Initialize stackLeft with the left child of the root node + stackRight := []*BinaryTree{tree.Right} // Initialize stackRight with the right child of the root node + + // Perform mirror traversal of the left and right subtrees + for len(stackLeft) > 0 { + var left, right *BinaryTree + left, stackLeft = stackLeft[len(stackLeft)-1], stackLeft[:len(stackLeft)-1] // Pop the top node from stackLeft + right, stackRight = stackRight[len(stackRight)-1], stackRight[:len(stackRight)-1] // Pop the top node from stackRight + + if left == nil && right == nil { + continue // Both left and right subtrees are symmetric, continue to the next iteration + } + + if left == nil || right == nil || left.Value != right.Value { + return false // Asymmetry detected, tree is not symmetric + } + + // Push the children of left and right onto the respective stacks in reverse order + stackLeft = append(stackLeft, left.Left, left.Right) + stackRight = append(stackRight, right.Right, right.Left) + } + + return true // Tree is symmetric +} \ No newline at end of file diff --git a/Trees/Binary Trees/is_symmetric.java b/Trees/Binary Trees/is_symmetric.java new file mode 100644 index 00000000..1aac1f27 --- /dev/null +++ b/Trees/Binary Trees/is_symmetric.java @@ -0,0 +1,44 @@ +/* + Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical + if the left and right subtrees are mirror images of each other. +*/ +import java.util.Stack; + +public class BinaryTree { + // Assume that BinaryTree has properties: left, right, and value. + + public boolean isSymmetricalTreeIterative(BinaryTree tree) { + Stack stackLeft = new Stack<>(); + Stack stackRight = new Stack<>(); + + // Initialize stackLeft with the left child of the root node + // Initialize stackRight with the right child of the root node + stackLeft.push(tree.getLeft()); + stackRight.push(tree.getRight()); + + // Perform mirror traversal of the left and right subtrees + while (!stackLeft.isEmpty()) { + BinaryTree left = stackLeft.pop(); + BinaryTree right = stackRight.pop(); + + if (left == null && right == null) { + continue; // Both left and right subtrees are symmetric, continue to the next iteration + } + + if (left == null || right == null || left.getValue() != right.getValue()) { + return false; // Asymmetry detected, tree is not symmetric + } + + // Push the children of left and right onto the respective stacks in reverse order + stackLeft.push(left.getLeft()); + stackLeft.push(left.getRight()); + + stackRight.push(right.getRight()); + stackRight.push(right.getLeft()); + } + + return true; // Tree is symmetric + } + + // Other methods and properties for BinaryTree class +} diff --git a/Trees/Binary Trees/is_symmetric.js b/Trees/Binary Trees/is_symmetric.js new file mode 100644 index 00000000..f275a07b --- /dev/null +++ b/Trees/Binary Trees/is_symmetric.js @@ -0,0 +1,50 @@ +/* + Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical + if the left and right subtrees are mirror images of each other. +*/ +class BinaryTree { + constructor(value, left = null, right = null) { + this.value = value; + this.left = left; + this.right = right; + } +} + +function isSymmetricalTreeIterative(tree) { + const stackLeft = (tree && [tree.left]) || []; // Initialize stackLeft with the left child of the root node + const stackRight = (tree && [tree.right]) || []; // Initialize stackRight with the right child of the root node + + // Perform mirror traversal of the left and right subtrees + while (stackLeft.length > 0) { + const left = stackLeft.pop() || null; // Pop the top node from stackLeft + const right = stackRight.pop() || null; // Pop the top node from stackRight + + if (!left && !right) { + continue; // Both left and right subtrees are symmetric, continue to the next iteration + } + + if (!left || !right || left.value !== right.value) { + return false; // Asymmetry detected, tree is not symmetric + } + + // Push the children of left and right onto the respective stacks in reverse order + if (left) { + stackLeft.push(left.left, left.right); + } + if (right) { + stackRight.push(right.right, right.left); + } + } + + return true; // Tree is symmetric +} + +// Example usage: +// Construct a symmetric tree +const symmetricTree = new BinaryTree( + 1, + new BinaryTree(2, new BinaryTree(3), new BinaryTree(4)), + new BinaryTree(2, new BinaryTree(4), new BinaryTree(3)) +); + +console.log(isSymmetricalTreeIterative(symmetricTree)); // Output: true diff --git a/Trees/Binary Trees/is_symmetric.py b/Trees/Binary Trees/is_symmetric.py new file mode 100644 index 00000000..8870f998 --- /dev/null +++ b/Trees/Binary Trees/is_symmetric.py @@ -0,0 +1,36 @@ +''' + Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical + if the left and right subtrees are mirror images of each other. +''' +class BinaryTree: + def __init__(self, value, left=None, right=None): + self.value = value + self.left = left + self.right = right + +def is_symmetrical_tree_iterative(tree): + stack_left = [tree.left] if tree else [] # Initialize stackLeft with the left child of the root node + stack_right = [tree.right] if tree else [] # Initialize stackRight with the right child of the root node + + # Perform mirror traversal of the left and right subtrees + while stack_left: + left = stack_left.pop() if stack_left else None + right = stack_right.pop() if stack_right else None + + if not left and not right: + continue # Both left and right subtrees are symmetric, continue to the next iteration + + if not left or not right or left.value != right.value: + return False # Asymmetry detected, tree is not symmetric + + # Push the children of left and right onto the respective stacks in reverse order + stack_left.extend([left.left, left.right] if left else []) + stack_right.extend([right.right, right.left] if right else []) + + return True # Tree is symmetric + +# Example usage: +# Construct a symmetric tree +symmetric_tree = BinaryTree(1, BinaryTree(2, BinaryTree(3), BinaryTree(4)), BinaryTree(2, BinaryTree(4), BinaryTree(3))) + +print(is_symmetrical_tree_iterative(symmetric_tree)) # Output: True diff --git a/Trees/Binary_Trees/binary_tree_bfs_print_level_by_level.cpp b/Trees/Binary Trees/level_by_level.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_bfs_print_level_by_level.cpp rename to Trees/Binary Trees/level_by_level.cpp diff --git a/Trees/Binary_Trees/binary_tree_level_order_traversal.cpp b/Trees/Binary Trees/level_order_traversal.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_level_order_traversal.cpp rename to Trees/Binary Trees/level_order_traversal.cpp diff --git a/Trees/Binary Trees/level_order_traversal.go b/Trees/Binary Trees/level_order_traversal.go new file mode 100644 index 00000000..7791c772 --- /dev/null +++ b/Trees/Binary Trees/level_order_traversal.go @@ -0,0 +1,41 @@ +package main + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} + +// Level order traversal is defined as follows: +// 1 Visit the root. +// 2 While traversing level 􀝈, keep all the elements at level 􀝈 + 1 in queue. +// 3 Go to the next level and visit all the nodes at that level. +// 4 Repeat this until all levels are completed. +// The nodes of the tree are visited in the order: [1] [2 3] [ 4 5 6 7] +// Time Complexity: O(n), Space Complexity: O(n) In the worst case, all the nodes on the entire last level could be in the queue. +func LevelOrder(root *BinaryTreeNode) [][]int { + if root == nil { + return [][]int{} + } + // Data from each level is being returned as a separate list + var result [][]int + queue := []*BinaryTreeNode{root} + for len(queue) > 0 { + qlen := len(queue) + var level []int + for i:= 0; i < qlen; i++ { + node := queue[0] + level = append(level, node.data) + queue = queue[1:] + // if there are left children then append them in queue + if node.left != nil { + queue = append(queue, node.left) + } + // if there are right children then append them in queue + if node.right != nil { + queue = append(queue, node.right) + } + } + } + return result +} diff --git a/Trees/Binary Trees/node_depth.go b/Trees/Binary Trees/node_depth.go new file mode 100644 index 00000000..573cd9ed --- /dev/null +++ b/Trees/Binary Trees/node_depth.go @@ -0,0 +1,45 @@ +/* + The distance between a node in a Binary Tree and the tree's root is called the + node's depth. + + Write a function that takes in a Binary Tree and returns the sum of its nodes' + depths. + + Sample Input: + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + / \ +8 9 + Output: 16 +*/ + +/* + The depth of any node in the tree is equal to the depth of its parent node plus 1. + By starting at the root node whose depth is 0, you can pass down to every node in + the tree its respective depth, and you can implement the algorithm that does this + and that sums up all of the depths either recursively or iteratively. + + Time and Space complexity + Average case: when the tree is balanced + O(n) time | O(h) space - where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree +*/ +package main + +type BinaryTree struct { + Value int + Left, Right *BinaryTree +} + +func NodeDepths(root *BinaryTree) int { + return nodeDepthHelper(root, 0) +} + +func nodeDepthHelper(root *BinaryTree, depth int) int { + if root == nil { + return 0 + } + return depth + nodeDepthHelper(root.Left, depth + 1) + nodeDepthHelper(root.Right, depth + 1) +} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree_postorder_traversal.cpp b/Trees/Binary Trees/postorder_traversal.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_postorder_traversal.cpp rename to Trees/Binary Trees/postorder_traversal.cpp diff --git a/Trees/Binary_Trees/binary_tree_preorder_traversal.cpp b/Trees/Binary Trees/preorder_traversal.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_preorder_traversal.cpp rename to Trees/Binary Trees/preorder_traversal.cpp diff --git a/Trees/Binary Trees/remove_leaf_nodes.go b/Trees/Binary Trees/remove_leaf_nodes.go new file mode 100644 index 00000000..7c29aaf7 --- /dev/null +++ b/Trees/Binary Trees/remove_leaf_nodes.go @@ -0,0 +1,26 @@ +// Remove leaf nodes +package main + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: recurse both left and right subtree and check if the node doesn't have +// left and right children +func RemoveLeafNodes(root *BinaryTreeNode) *BinaryTreeNode { + if root == nil { + return root + } + // if it doesnt have left and right children then delete it + if root.left == nil && root.right == nil { + root = nil + return root + } else { // recurse to left and right subtree + root.left = RemoveLeafNodes(root.left) + root.right = RemoveLeafNodes(root.right) + } + return root +} \ No newline at end of file diff --git a/Trees/Binary Trees/search_an_element.go b/Trees/Binary Trees/search_an_element.go new file mode 100644 index 00000000..142e6d53 --- /dev/null +++ b/Trees/Binary Trees/search_an_element.go @@ -0,0 +1,57 @@ +// Search an element in Binary tree +package main + +type BinaryTreeNode struct { + left *BinaryTreeNode + data int + right *BinaryTreeNode +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: recurse down the tree choose left or right branch by comparing data with each node's data +func SearchAnElement(root *BinaryTreeNode, data int) *BinaryTreeNode { + // base case empty tree + if root == nil { + return root + } else { + // if found return root + if data == root.data { + return root + } else { + // recurse down correct subtree + temp := SearchAnElement(root.left, data) + if temp != nil { + return temp + } else { + return SearchAnElement(root.right, data) + } + } + } +} + +// Time Complexity: O(n). Space Complexity: O(n). +// Approach: using level order traversal we can solve this problem, check whether +// the root data is equal to the element we want to search +func SearchAnElementWithoutRecursion(root *BinaryTreeNode, data int) *BinaryTreeNode { + if root == nil { + return root + } + queue := []*BinaryTreeNode{root} + for len(queue) > 0 { + qlen := len(queue) + for i := 0; i < qlen; i++ { + node := queue[0] + if data == node.data { + return node + } + queue = queue[1:] + if node.left != nil { + queue = append(queue, node.left) + } + if node.right != nil { + queue = append(queue, node.right) + } + } + } + return nil +} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree_sum_of_all_nodes.cpp b/Trees/Binary Trees/sum_of_all_nodes.cpp similarity index 100% rename from Trees/Binary_Trees/binary_tree_sum_of_all_nodes.cpp rename to Trees/Binary Trees/sum_of_all_nodes.cpp diff --git a/Trees/Binary_Search_Trees/BST_delete_from_bst.cpp b/Trees/Binary_Search_Trees/BST_delete_from_bst.cpp deleted file mode 100644 index 31d1ca65..00000000 --- a/Trees/Binary_Search_Trees/BST_delete_from_bst.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// Binary Search Tree : Deleting from Binary Search Tree -// Program Author : Abhisek Kumar Gupta -/* - Input : 7 3 9 2 4 8 10 -1 - Value to be deleted : 5 - Output : Tree before deletion - 7 - / \ - 3 9 - / \ / \ - 2 4 8 10 - - Inorder Traversal : 2 3 4 7 8 9 10 - - Tree after deletion - 8 - / \ - 3 9 - / \ \ - 2 4 10 - - Inorder Traversal after deletion : 2 3 4 8 9 10 -*/ -#include -using namespace std; - -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -void bfs(Node* root){ - queue q; - q.push(root); - q.push(NULL); - while(!q.empty()){ - Node* element = q.front(); - if(element == NULL){ - cout << "\n"; - q.pop(); - if(!q.empty()){ - q.push(NULL); - } - } - else{ - cout << element->data << "->"; - q.pop(); - if(element->left != NULL){ - q.push(element->left); - } - if(element->right != NULL){ - q.push(element->right); - } - } - } -} -Node* insert_into_binary_search_tree(Node* root, int data){ - if(root == NULL) - return new Node(data); - if(data <= root->data){ - root->left = insert_into_binary_search_tree(root->left, data); - } - else{ - root->right = insert_into_binary_search_tree(root->right, data); - } - return root; -} -Node* build_binary_search_tree(){ - int data; - cin >> data; - Node* root = NULL; - while(data != -1){ - root = insert_into_binary_search_tree(root, data); - cin >> data; - } - return root; -} -Node* delete_from_BST(Node* root, int data){ - if(root == NULL) - return NULL; - if(data < root->data){ - root->left = delete_from_BST(root->left, data); - return root; - } - else if(data == root->data){ - if(root->left == NULL && root->right == NULL){ - delete root; - return NULL; - } - if(root->left == NULL && root->right != NULL){ - Node* temp = root; - root = root->right; - delete temp; - return root; - } - if(root->left != NULL && root->right == NULL){ - Node* temp = root; - root = root->left; - delete temp; - return root; - } - Node* replace = root->right; - while(replace->left != NULL){ - replace = replace->left; - } - root->data = replace->data; - root->right = delete_from_BST(root->right, replace->data); - return root; - } - else{ - root->right = delete_from_BST(root->right, data); - return root; - } - return root; -} -void inorder_traversal(Node* root){ - if(root == NULL){ - return; - } - inorder_traversal(root->left); - cout << root->data << "->"; - inorder_traversal(root->right); -} -int main(){ - Node* root = build_binary_search_tree(); - cout << "\nInorder Traversal\n"; - inorder_traversal(root); - cout << "\n\n"; - cout << "Printing level by level\n"; - bfs(root); - int node_to_be_deleted; - cin >> node_to_be_deleted; - root = delete_from_BST(root, node_to_be_deleted); - cout << endl; - cout << "Printing level by level after deletion\n"; - bfs(root); - cout << "\n"; - cout << "Inorder Traversal\n"; - inorder_traversal(root); - cout << "\n\n"; - return 0; -} \ No newline at end of file diff --git a/Trees/Binary_Search_Trees/BST_flatten_a_linked_list_from_a_tree.cpp b/Trees/Binary_Search_Trees/BST_flatten_a_linked_list_from_a_tree.cpp deleted file mode 100644 index 7ede6c13..00000000 --- a/Trees/Binary_Search_Trees/BST_flatten_a_linked_list_from_a_tree.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// Binary Search Tree : Flatten a Tree [BST to sorted linkedlist] -// Program Author : Abhisek Kumar Gupta -/* - Input : 7 3 9 2 4 8 10 -1 - Output : - 7 - / \ - 3 9 - / \ / \ - 2 4 8 10 - Inorder Traversal : 2 3 4 7 8 9 10 - Flattened Linkedlist : 2->3->4->7->8->9->10-> -*/ -#include -using namespace std; - -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -class Linked_List{ - public: - Node* head; - Node* tail; -}; -Linked_List flatten_list(Node* root){ - Linked_List l; - if(root == NULL){ - l.head = l.tail = NULL; - return l; - } - if(root->left == NULL && root->right == NULL){ - l.head = l.tail = root; - return l; - } - if(root->left != NULL && root->right == NULL){ - Linked_List left_linked_list = flatten_list(root->left); - left_linked_list.tail->right = root; - l.head = left_linked_list.head; - l.tail = root; - return l; - } - if(root->left == NULL && root->right != NULL){ - Linked_List right_linked_list = flatten_list(root->right); - root->right = right_linked_list.head; - l.head = root; - l.tail = right_linked_list.tail; - return l; - } - Linked_List left_linked_list = flatten_list(root->left); - Linked_List right_linked_list = flatten_list(root->right); - left_linked_list.tail->right = root; - root->right = right_linked_list.head; - - l.head = left_linked_list.head; - l.tail = right_linked_list.tail; - return l; -} -void bfs(Node* root){ - queue q; - q.push(root); - q.push(NULL); - while(!q.empty()){ - Node* element = q.front(); - if(element == NULL){ - cout << "\n"; - q.pop(); - if(!q.empty()){ - q.push(NULL); - } - } - else{ - cout << element->data << "->"; - q.pop(); - if(element->left != NULL){ - q.push(element->left); - } - if(element->right != NULL){ - q.push(element->right); - } - } - } - return; -} -Node* insert_into_binary_search_tree(Node* root, int data){ - if(root == NULL){ - return new Node(data); - } - if(data <= root->data){ - root->left = insert_into_binary_search_tree(root->left, data); - } - else{ - root->right = insert_into_binary_search_tree(root->right, data); - } - return root; -} -Node* build_binary_search_tree(){ - int data; - cin >> data; - Node* root = NULL; - while(data != -1){ - root = insert_into_binary_search_tree(root, data); - cin >> data; - } - return root; -} -void inorder(Node*root){ - if(root == NULL) - return; - inorder(root->left); - cout << root->data << "->"; - inorder(root->right); -} - -int main(){ - Node* root = build_binary_search_tree(); - cout << endl << "Inorder Traversal\n"; - inorder(root); - cout << endl; - bfs(root); - cout << endl; - Linked_List l = flatten_list(root); - Node* temp = l.head; - - while(temp != NULL){ - cout << temp->data << "->"; - temp = temp->right; - } -return 0; -} \ No newline at end of file diff --git a/Trees/Binary_Search_Trees/BST_is_valid_bst.cpp b/Trees/Binary_Search_Trees/BST_is_valid_bst.cpp deleted file mode 100644 index 40fc0a26..00000000 --- a/Trees/Binary_Search_Trees/BST_is_valid_bst.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Binary Search Tree : Is valid Binary Search Tree? -// Program Author : Abhisek Kumar Gupta -/* - Input : 7 3 9 2 4 8 10 -1 - - Output : YES - 7 - / \ - 3 9 - / \ / \ - 2 4 8 10 - -*/ -#include -using namespace std; - -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -void bfs(Node* root){ - queue q; - q.push(root); - q.push(NULL); - while(!q.empty()){ - Node* element = q.front(); - if(element == NULL){ - cout << "\n"; - q.pop(); - if(!q.empty()){ - q.push(NULL); - } - } - else{ - cout << element->data << "->"; - q.pop(); - if(element->left != NULL){ - q.push(element->left); - } - if(element->right != NULL){ - q.push(element->right); - } - } - } - return; -} -Node* insert_into_binary_search_tree(Node* root, int data){ - if(root == NULL) - return new Node(data); - if(data <= root->data){ - root->left = insert_into_binary_search_tree(root->left, data); - } - else{ - root->right = insert_into_binary_search_tree(root->right, data); - } - return root; -} -Node* build_binary_search_tree(){ - int data; - cin >> data; - Node* root = NULL; - while(data != -1){ - root = insert_into_binary_search_tree(root, data); - cin >> data; - } - return root; -} -bool is_binary_search_tree(Node* root, int minimum = INT_MIN, int maximum = INT_MAX){ - if(root == NULL) - return true; - if(root->data >= minimum && root->data <= maximum && is_binary_search_tree(root->left, minimum, root->data) && is_binary_search_tree(root->right, root->data, maximum)) - return true; - return false; - -} -int main(){ - Node* root = build_binary_search_tree(); - cout << endl; - bfs(root); - cout << endl; - if(is_binary_search_tree(root)){ - cout << "YES\n"; - } - else{ - cout << "NO\n"; - } - return 0; -} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree.go b/Trees/Binary_Trees/binary_tree.go deleted file mode 100644 index 0b2ce78c..00000000 --- a/Trees/Binary_Trees/binary_tree.go +++ /dev/null @@ -1,379 +0,0 @@ -/* - A tree is called binary tree if each node has zero child, one child or two children. Empty tree is also a valid binary - tree. We can visualize a binary tree as consisting of a root and two disjoint binary trees, called the left and right - subtrees of the root. - - Types of Binary Trees - 1) Strict Binary Tree: A binary tree is called strict binary tree if each node has exactly two children or no children. - 2) Full Binary Tree: A binary tree is called full binary tree if each node has exactly two children and all leaf nodes - are at the same level. - 3) Complete Binary Tree: Before defining the complete binary tree, let us assume that the height of the binary tree - is ℎ. In complete binary trees, if we give numbering for the nodes by starting at the root (let us say the root node - has 1) then we get a complete sequence from 1 to the number of nodes in the tree. While traversing we should give - numbering for nil pointers also. A binary tree is called complete binary tree if all leaf nodes are at height ℎ or ℎ − 1 - and also without any missing number in the sequence. - - 1 - / \ - 2 3 - / \ / \ - 4 5 6 7 -*/ -package main - -import ( - "fmt" - "math" - "math/rand" -) - -type BinaryTreeNode struct { - left *BinaryTreeNode - data int - right *BinaryTreeNode -} -// NewBinaryTree returns a new, random binary tree -func NewBinaryTree(n, k int) *BinaryTreeNode { - var root * BinaryTreeNode - for _, v := range rand.Perm(n) { - root = Insert(root, (1 + v) * k) - } - return root -} -// Insert, inserts an element in binary tree -func Insert(root *BinaryTreeNode, v int) *BinaryTreeNode { - if root == nil { - // fmt.Printf("%d root", v) - return &BinaryTreeNode{nil, v, nil} - } - // data less than root of data the insert in left subtree - if v < root.data { - // fmt.Printf("%d left\n", v) - root.left = Insert(root.left, v) - return root - } - // data greater than or equal to root of data the insert in right subtree - // fmt.Printf("%d right\n", v) - root.right = Insert(root.right, v) - return root -} - -// Pre-order traversal -// Preorder traversal is defined as follows: -// 1 Visit the root. -// 2 Traverse the left subtree in Preorder. -// 3 Traverse the right subtree in Preorder. -// Time Complexity: O(n). Space Complexity: O(n). -// The nodes of tree would be visited in the order: 1 2 4 5 3 6 7 -func PreOrder(root *BinaryTreeNode) { - - if root == nil { - return - } - fmt.Printf("%d ", root.data) - PreOrder(root.left) - PreOrder(root.right) -} - -// Inorder traversal is defined as follows: -// 1 Traverse the left subtree in Inorder. -// 2 Visit the root. -// 3 Traverse the right subtree in Inorder. -// Time Complexity: O(n). Space Complexity: O(n). -// The nodes of tree would be visited in the order: 4 2 5 1 6 3 7 -func InOrder(root *BinaryTreeNode) { - if root == nil { - return - } - InOrder(root.left) - fmt.Printf("%d", root.data) - InOrder(root.right) -} - -// PostOrder traversal is defined as follows: -// 1 Traverse the left subtree in PostOrder. -// 2 Traverse the right subtree in PostOrder. -// 3 Visit the root. -// The nodes of the tree would be visited in the order: 4 5 2 6 7 3 1 -func PostOrder(root *BinaryTreeNode) { - if root == nil { - return - } - PostOrder(root.left) - PostOrder(root.right) - fmt.Printf("%d", root.data) -} - -// Level order traversal is defined as follows: -// 1 Visit the root. -// 2 While traversing level 􀝈, keep all the elements at level 􀝈 + 1 in queue. -// 3 Go to the next level and visit all the nodes at that level. -// 4 Repeat this until all levels are completed. -// The nodes of the tree are visited in the order: [1] [2 3] [ 4 5 6 7] -// Time Complexity: O(n), Space Complexity: O(n) In the worst case, all the nodes on the entire last level could be in the queue. -func LevelOrder(root *BinaryTreeNode) [][]int { - if root == nil { - return [][]int{} - } - // Data from each level is being returned as a separate list - var result [][]int - queue := []*BinaryTreeNode{root} - for len(queue) > 0 { - qlen := len(queue) - var level []int - for i:= 0; i < qlen; i++ { - node := queue[0] - level = append(level, node.data) - queue = queue[1:] - // if there are left children then append them in queue - if node.left != nil { - queue = append(queue, node.left) - } - // if there are right children then append them in queue - if node.right != nil { - queue = append(queue, node.right) - } - } - } - return result -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: find maximum in left sub tree, find maximum in right subtree -// compare them with root data and select the one which is giving the max value -// recursive appraoch -func FindMax(root *BinaryTreeNode) int { - max := math.MinInt32 - if root != nil { - rootVal := root.data - left := FindMax(root.left) - right := FindMax(root.right) - if left > max { - max = left - } else { - max = right - } - if rootVal > max { - max = rootVal - } - } - return max -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: Using level order traversal observe the elements data -func FindMaxWithoutRecursion(root *BinaryTreeNode) int { - max := math.MinInt32 - if root == nil { - return max - } - queue := []*BinaryTreeNode{root} - for len(queue) > 0 { - qlen := len(queue) - for i := 0; i < qlen; i++ { - node := queue[0] - if node.data > max { - max = node.data - } - queue = queue[1:] - if node.left != nil { - queue = append(queue, node.left) - } - if node.right != nil { - queue = append(queue, node.right) - } - } - } - return max -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: recurse down the tree choose left or right branch by comparing data with each node's data -func SearchAnElement(root *BinaryTreeNode, data int) *BinaryTreeNode { - // base case empty tree - if root == nil { - return root - } else { - // if found return root - if data == root.data { - return root - } else { - // recurse down correct subtree - temp := SearchAnElement(root.left, data) - if temp != nil { - return temp - } else { - return SearchAnElement(root.right, data) - } - } - } -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: using level order traversal we can solve this problem, check whether -// the root data is equal to the element we want to search -func SearchAnElementWithoutRecursion(root *BinaryTreeNode, data int) *BinaryTreeNode { - if root == nil { - return root - } - queue := []*BinaryTreeNode{root} - for len(queue) > 0 { - qlen := len(queue) - for i := 0; i < qlen; i++ { - node := queue[0] - if data == node.data { - return node - } - queue = queue[1:] - if node.left != nil { - queue = append(queue, node.left) - } - if node.right != nil { - queue = append(queue, node.right) - } - } - } - return nil -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: calculate the size of left and right subtree recursively -// add 1 (curr node) and return to its parent -func Size(root *BinaryTreeNode) int { - if root == nil { - return 0 - } else { - return Size(root.left) + 1 + Size(root.right) - } -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: use level order traversal and count nodes -func SizeWithoutUsingRecursion(root *BinaryTreeNode) int { - if root == nil { - return 0 - } - var result int - queue := []*BinaryTreeNode{root} - for len(queue) > 0 { - qlen := len(queue) - //var level []int - for i := 0; i < qlen; i++ { - node := queue[0] - result++ - //level = append(level, node.data) - queue = queue[1:] - if node.left != nil { - queue = append(queue, node.left) - } - if node.right != nil { - queue = append(queue, node.right) - } - } - } - return result -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: Recursively calculate height of left and right subtrees of a node -// and assign height to the node as max of heights of two children + 1 -func Height(root *BinaryTreeNode) int { - if root == nil { - return 0 - } else { - // compute depth of each subtree - leftHeight := Height(root.left) - rightHeight := Height(root.right) - if leftHeight > rightHeight { - return leftHeight + 1 - } else { - return rightHeight + 1 - } - - } -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: The inverse of an empty tree is an empty tree -// The inverse of a tree with root r, and subtrees right and left is a tree with -// root, whose right subtree is the inverse of left and whoose left subtree -// is the inverse of right -func InvertTree(root *BinaryTreeNode) *BinaryTreeNode { - if root != nil { - root.left, root.right = InvertTree(root.right), InvertTree(root.left) - } - return root -} - -// Time Complexity: O(􀝊). Space Complexity: O(􀝊). -// Method2 : swap pointers -func InvertTree2(root *BinaryTreeNode) *BinaryTreeNode { - if root == nil { - return root - } - // swap the pointers in this node - root.left, root.right = root.right, root.left - InvertTree(root.left) - InvertTree(root.right) - return root -} - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: before deleting parent node, delete all its children nodes -// using post order traversal we can solve this problem -func DeleteTree(root *BinaryTreeNode) *BinaryTreeNode { - if root == nil { - return nil - } - // deleet both subtrees - root.left = DeleteTree(root.left) - root.right = DeleteTree(root.right) - // delete current node after deleting subtrees - root = nil - return root -} - - -// Time Complexity: O(n). Space Complexity: O(n). -// Approach: recurse both left and right subtree and check if the node doesn't have -// left and right children -func RemoveLeafNodes(root *BinaryTreeNode) *BinaryTreeNode { - if root == nil { - return root - } - // if it doesnt have left and right children then delete it - if root.left == nil && root.right == nil { - root = nil - return root - } else { // recurse to left and right subtree - root.left = RemoveLeafNodes(root.left) - root.right = RemoveLeafNodes(root.right) - } - return root -} - -func main() { - t1 := NewBinaryTree(10, 1) - PreOrder(t1) - fmt.Println() - InOrder(t1) - fmt.Println() - PostOrder(t1) - fmt.Println() - msg := FindMax(t1) - fmt.Println(msg) - res := SearchAnElement(t1, 1) - fmt.Println(res) - size := Size(t1) - fmt.Println(size) - size = SizeWithoutUsingRecursion(t1) - fmt.Println("Size without recursion") - fmt.Println(size) - height := Height(t1) - fmt.Println(height) - invert := InvertTree(t1) - fmt.Println(invert) - deleteLeaf := RemoveLeafNodes(t1) - fmt.Println(deleteLeaf) - PreOrder(deleteLeaf) -} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree_create_tree_from_preorder_and_inorder.cpp b/Trees/Binary_Trees/binary_tree_create_tree_from_preorder_and_inorder.cpp deleted file mode 100644 index 7bbba478..00000000 --- a/Trees/Binary_Trees/binary_tree_create_tree_from_preorder_and_inorder.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Binary Tree : Create binary tree from preorder and inorder traversal -// Program Author : Abhisek Kumar Gupta -/* - Input : No of elements : 8 - Inorder : 4 8 2 5 1 6 3 7 - Preorder : 1 2 4 8 5 3 6 7 - Output : - 1 - / \ - 2 3 - / \ / \ - 4 5 6 7 - / - 8 -*/ -#include -using namespace std; -const int some_number = 1004; -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - } -}; -void bfs(Node* root){ - queue q; - q.push(root); - q.push(NULL); - while (!q.empty()){ - Node* element = q.front(); - if(element == NULL){ - cout << "\n"; - q.pop(); - if(!q.empty()){ - q.push(NULL); - } - } - else{ - q.pop(); - cout << element->data << "->"; - if(element->left != NULL){ - q.push(element->left); - } - if(element->right != NULL){ - q.push(element->right); - } - } - } - return; -} -Node* create_tree_from_preorder_and_inorder(int *Inorder, int *Preorder, int start, int end){ - static int i = 0; - if(start > end){ - return NULL; - } - int index = -1; - Node* root = new Node(Preorder[i]); - for(int j = start; j <= end; j++){ - if(Preorder[i] == Inorder[j]){ - index = j; - break; - } - } - i++; - root->left = create_tree_from_preorder_and_inorder(Inorder, Preorder, start, index - 1); - root->right = create_tree_from_preorder_and_inorder(Inorder, Preorder, index + 1, end); - return root; - -} -int main(){ - int In[some_number]; - int Pre[some_number]; - int no_of_elements; - cin >> no_of_elements; - int end = no_of_elements - 1; - int start = 0; - for(int i = 0; i < no_of_elements; i++) - cin >> Pre[i]; - for(int i = 0; i < no_of_elements; i++) - cin >> In[i]; - Node* root = create_tree_from_preorder_and_inorder(In, Pre, start, end); - bfs(root); - return 0; -} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree_diameter_of_a_tree.cpp b/Trees/Binary_Trees/binary_tree_diameter_of_a_tree.cpp deleted file mode 100644 index 896e21f8..00000000 --- a/Trees/Binary_Trees/binary_tree_diameter_of_a_tree.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Binary Tree : Diameter of tree O(n^2) -// Program Author : Abhisek Kumar Gupta -/* - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - 1 -1 15 20 - / \ /\ /\ - 1 -1 -1 -1 -1 -1 - /\ - -1 -1 - Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 - Output : 7 -*/ -#include -using namespace std; - -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -Node* build_binary_tree(){ - int data; - cin >> data; - if(data == -1) - return NULL; - Node* root = new Node(data); - root->left = build_binary_tree(); - root->right = build_binary_tree(); - return root; -} -int calculate_height(Node* root){ - if(root == NULL) - return 0; - int left_height = calculate_height(root->left); - int right_height = calculate_height(root->right); - return max(left_height, right_height) + 1; - -} - -int calculate_diameter_of_tree(Node* root){ - if(root == NULL) - return 0; - int left_height = calculate_height(root->left); - int right_height = calculate_height(root->right); - int option1 = left_height + right_height; // if diameter passes through root - int option2 = calculate_diameter_of_tree(root->left); // if diameter lies in left subtree - int option3 = calculate_diameter_of_tree(root->right); // if diameter lies in right subtree - return max(option1, max(option2, option3)); -} - - -int main(){ - Node* root = build_binary_tree(); - int diameter = calculate_diameter_of_tree(root); - cout << "Diameter of tree is : " << diameter << endl; - return 0; -} diff --git a/Trees/Binary_Trees/binary_tree_diameter_of_a_tree_optimal.cpp b/Trees/Binary_Trees/binary_tree_diameter_of_a_tree_optimal.cpp deleted file mode 100644 index 683e1254..00000000 --- a/Trees/Binary_Trees/binary_tree_diameter_of_a_tree_optimal.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Binary Tree : Diameter of tree O(n) -// Program Author : Abhisek Kumar Gupta -/* - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - 1 -1 15 20 - / \ /\ /\ - 1 -1 -1 -1 -1 -1 - /\ - -1 -1 - Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 - Output : Height : 5 - Diameter : 7 -*/ -#include -using namespace std; - -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -class Pair{ - public: - int height; - int diameter; -}; -Node* build_binary_tree(){ - int data; - cin >> data; - if(data == -1) - return NULL; - Node* root = new Node(data); - root->left = build_binary_tree(); - root->right = build_binary_tree(); - return root; -} -Pair compute_diameter(Node* root){ - Pair p; - if(root == NULL){ - p.height = 0; - p.diameter = 0; - return p; - } - Pair left = compute_diameter(root->left); - Pair right = compute_diameter(root->right); - p.height = max(left.height, right.height) + 1; - p.diameter = max(left.height + right.height, max(left.diameter, right.diameter)); - return p; -} -int main(){ - Node* root = build_binary_tree(); - Pair p = compute_diameter(root); - cout << p.height << endl; - cout << p.diameter << endl; - return 0; -} \ No newline at end of file diff --git a/Trees/Binary_Trees/binary_tree_inorder_traversal.cpp b/Trees/Binary_Trees/binary_tree_inorder_traversal.cpp deleted file mode 100644 index 4d195fb3..00000000 --- a/Trees/Binary_Trees/binary_tree_inorder_traversal.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// In-Order Traversal of a Binary-Tree -// Program Author : Abhisek Kumar Gupta -/* - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - 1 -1 15 20 - / \ /\ /\ - -1 -1 -1 -1 -1 -1 - Input : 40 10 5 1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 - Output : 1->5->10->40->30->15->28->20 -*/ -#include -using namespace std; -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -Node* build_binary_tree(){ - int data; - cin >> data; - if(data == -1){ - return NULL; - } - Node* root = new Node(data); - root->left = build_binary_tree(); - root->right = build_binary_tree(); - return root; -} -void print_binary_tree(Node* root){ - if(root == NULL){ - return; - } - print_binary_tree(root->left); - cout << root->data << "->"; - print_binary_tree(root->right); -} -int main(){ - Node* root = build_binary_tree(); - print_binary_tree(root); - return 0; -} - diff --git a/Trees/Binary_Trees/binary_tree_is_height_balanced.cpp b/Trees/Binary_Trees/binary_tree_is_height_balanced.cpp deleted file mode 100644 index 9b5f8d8c..00000000 --- a/Trees/Binary_Trees/binary_tree_is_height_balanced.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Binary Tree : Replace parent with sum of child nodes -// Program Author : Abhisek Kumar Gupta -/* - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - -1 -1 -1 -1 - - Input : 40 10 5 -1 -1 -1 30 -1 28 -1 -1 - Output : Height : 3 - Yes it's Balanced - - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - 1 -1 15 20 - / \ /\ /\ - 1 -1 -1 -1 -1 -1 - /\ - -1 -1 - Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 - Output : Height : 5 - No it's not Balanced - -*/ - -#include -using namespace std; - -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -class Height_Balanced{ - public: - int height; - bool balance; -}; -Node* build_binary_tree(){ - int data; - cin >> data; - if(data == -1) - return NULL; - Node* root = new Node(data); - root->left = build_binary_tree(); - root->right = build_binary_tree(); - return root; -} -Height_Balanced is_height_balanced(Node* root){ - Height_Balanced p; - if(root == NULL){ - p.height = 0; - p.balance = true; - return p; - } - Height_Balanced left = is_height_balanced(root->left); - Height_Balanced right = is_height_balanced(root->right); - p.height = max(left.height, right.height) + 1; - if(abs(left.height - right.height) <= 1 && left.balance && right.balance){ - p.balance = true; - } - else{ - p.balance = false; - } - return p; -} -int main(){ - Node* root = build_binary_tree(); - if(is_height_balanced(root).balance){ - cout << "Height : " << is_height_balanced(root).height << endl; - cout << "Yes it's Balanced" << endl; - } - else{ - cout << "Height : " << is_height_balanced(root).height << endl; - cout << "No its not Balanced" << endl; - } -} diff --git a/Trees/Binary_Trees/binary_tree_replace_parent_with_sum_of_child.cpp b/Trees/Binary_Trees/binary_tree_replace_parent_with_sum_of_child.cpp deleted file mode 100644 index cfff790e..00000000 --- a/Trees/Binary_Trees/binary_tree_replace_parent_with_sum_of_child.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Binary Tree : Replace parent with sum of child nodes -// Program Author : Abhisek Kumar Gupta -/* - 40 - / \ - 10 30 - / \ / \ - 5 -1 -1 28 - / \ / \ - 1 -1 15 20 - / \ /\ /\ - 1 -1 -1 -1 -1 -1 - /\ - -1 -1 - Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 - Output : Tree before replacing parent with sum of child nodes - 40-> - 10->30-> - 5->28-> - 1->15->20-> - 1-> - Tree after replacing parent with sum of child nodes - 110-> - 7->63-> - 2->35-> - 1->15->20-> - 1-> - -*/ - -#include -using namespace std; -class Node{ - public: - int data; - Node* left; - Node* right; - - Node(int x){ - data = x; - left = NULL; - right = NULL; - } -}; -void bfs(Node* root){ - queue q; - q.push(root); - q.push(NULL); - while(!q.empty()){ - Node* element = q.front(); - if(element == NULL){ - cout << "\n"; - q.pop(); - if(!q.empty()){ - q.push(NULL); - } - } - else{ - q.pop(); - cout << element->data << "->"; - if(element->left != NULL){ - q.push(element->left); - } - if(element->right != NULL){ - q.push(element->right); - } - } - } -} -Node* build_binary_tree(){ - int data; - cin >> data; - if(data == -1) - return NULL; - Node* root = new Node(data); - root->left = build_binary_tree(); - root->right = build_binary_tree(); - return root; -} - -int replace_parent_with_sum_of_child(Node* root){ - if(root == NULL){ - return 0; - } - if(root->left == NULL && root->right == NULL){ - return root->data; - } - int left_sum = replace_parent_with_sum_of_child(root->left); - int right_sum = replace_parent_with_sum_of_child(root->right); - int temp = root->data; - root->data = left_sum + right_sum; - return root->data + temp; -} -int main(){ - Node* root = build_binary_tree(); - bfs(root); - replace_parent_with_sum_of_child(root); - cout << endl; - bfs(root); -} \ No newline at end of file diff --git a/Trees/Binary_Trees/dfs.go b/Trees/Binary_Trees/dfs.go deleted file mode 100644 index 8722104b..00000000 --- a/Trees/Binary_Trees/dfs.go +++ /dev/null @@ -1,15 +0,0 @@ -// Implementation of Depth First Search -package main - -type Node struct { - Value int - Children []*Node -} - -func (n *Node) DepthFirstSearch(array []int) []int { - array = append(array, n.Value) - // call dfs on children of each node - for _, child := range n.Children { - array = child.DepthFirstSearch(array) - } -} \ No newline at end of file diff --git a/Trees/Implement_Trie.cpp b/Trees/Implement_Trie.cpp new file mode 100644 index 00000000..7aa8ed5e --- /dev/null +++ b/Trees/Implement_Trie.cpp @@ -0,0 +1,115 @@ +/* +Approach: +- We define a Trie node structure that contains an array of child nodes and a flag to indicate the end of a word. +- The Trie class provides methods to insert words into the Trie, search for words, and check if any word starts with a given prefix. +- The insert operation involves traversing the Trie for each character of the word and creating a new node if the path doesn't exist. At the end, we mark the last node as the end of a word. +- The search operation is similar to the insert operation, but it returns true only if the last node is marked as the end of a word. +- The startsWith operation also involves traversing the Trie for each character of the prefix. It returns true regardless of whether the last node is marked as the end of a word or not. + +Time Complexity: +- The time complexity of inserting a word into the Trie is O(m), where m is the length of the word. +- The time complexity of searching for a word or checking for a prefix is O(m), where m is the length of the word or prefix. + +Space Complexity: +- The space complexity of the Trie is O(N * M), where N is the number of words and M is the average length of the words. + +Sample Input / Output: +int main() { + Trie trie; + + trie.insert("apple"); + trie.insert("banana"); + + cout << trie.search("apple") << endl; // Output: 1 (true) + cout << trie.search("banana") << endl; // Output: 1 (true) + cout << trie.search("car") << endl; // Output: 0 (false) + + cout << trie.startsWith("app") << endl; // Output: 1 (true) + cout << trie.startsWith("ban") << endl; // Output: 1 (true) + cout << trie.startsWith("cab") << endl; // Output: 0 (false) + + return 0; +} + +*/ + +//Here's the code for implementing a Trie (Prefix Tree) in C++: + +// Trie node structure +struct TrieNode { + struct TrieNode* children[26]; // Array to store child nodes + bool isEndOfWord; // Flag to indicate end of word + TrieNode() { + isEndOfWord = false; + for (int i = 0; i < 26; i++) + children[i] = nullptr; + } +}; + +// Trie class +class Trie { +private: + TrieNode* root; // Pointer to the root node + +public: + Trie() { + root = new TrieNode(); + } + + // Insert a word into the Trie + void insert(string word) { + TrieNode* current = root; + + for (char ch : word) { + int index = ch - 'a'; + + // Create a new node if the path doesn't exist + if (!current->children[index]) { + current->children[index] = new TrieNode(); + } + + current = current->children[index]; + } + + // Mark the end of a word + current->isEndOfWord = true; + } + + // Search for a word in the Trie + bool search(string word) { + TrieNode* current = root; + + for (char ch : word) { + int index = ch - 'a'; + + // Return false if the path doesn't exist + if (!current->children[index]) { + return false; + } + + current = current->children[index]; + } + + // Return true only if the current node is the end of a word + return current->isEndOfWord; + } + + // Check if any word in the Trie starts with the given prefix + bool startsWith(string prefix) { + TrieNode* current = root; + + for (char ch : prefix) { + int index = ch - 'a'; + + // Return false if the path doesn't exist + if (!current->children[index]) { + return false; + } + + current = current->children[index]; + } + + // Return true regardless of whether the current node is the end of a word or not + return true; + } +}; diff --git a/Trees/Implement_Trie.py b/Trees/Implement_Trie.py new file mode 100644 index 00000000..6718bb9a --- /dev/null +++ b/Trees/Implement_Trie.py @@ -0,0 +1,77 @@ +''' +Approach: +1. We implement the TrieNode class with a dictionary of children nodes and a flag to indicate the end of a word. +2. The Trie class is implemented with a root TrieNode as its member variable. +3. The insert function traverses through each character of the input word and creates new TrieNode objects if necessary. +4. The search function traverses through each character of the input word and returns True only if the last node is marked as the end of a word. +5. The startsWith function is similar to the search function but returns True as long as the prefix exists in the Trie. + +Time Complexity: +- Insertion: O(m), where m is the length of the word being inserted. +- Search: O(m), where m is the length of the word being searched. +- Starts With: O(m), where m is the length of the prefix being checked. + +Space Complexity: +- O(n*m), where n is the number of words inserted and m is the average length of the words. + +Sample input and output: + +trie = Trie() +trie.insert("apple") +print(trie.search("apple")) # Output: True +print(trie.search("app")) # Output: False +print(trie.startsWith("app")) # Output: True +trie.insert("app") +print(trie.search("app")) # Output: True + +Here's an implementation of the Trie data structure, also known as the Prefix Tree, in Python: +''' + +# Trie node class +class TrieNode: + def __init__(self): + self.children = {} + self.is_end_of_word = False + + +# Trie class +class Trie: + def __init__(self): + self.root = TrieNode() + + # Function to insert a word into the Trie + def insert(self, word: str) -> None: + node = self.root + for char in word: + # If the current character doesn't exist in the Trie, create a new node + if char not in node.children: + node.children[char] = TrieNode() + # Move to next node + node = node.children[char] + # Mark the end of a word + node.is_end_of_word = True + + # Function to search for a word in the Trie + def search(self, word: str) -> bool: + node = self.root + for char in word: + # If the current character doesn't exist, the word doesn't exist + if char not in node.children: + return False + # Move to next node + node = node.children[char] + # Return True only if the last node is marked as the end of a word + return node.is_end_of_word + + # Function to check if a word is a prefix of any existing word in the Trie + def startsWith(self, prefix: str) -> bool: + node = self.root + for char in prefix: + # If the current character doesn't exist, the prefix doesn't exist + if char not in node.children: + return False + # Move to next node + node = node.children[char] + # The prefix exists if we reach this point + return True + diff --git a/Trees/MaxpathBinaryTree.cpp b/Trees/MaxpathBinaryTree.cpp new file mode 100644 index 00000000..4183551e --- /dev/null +++ b/Trees/MaxpathBinaryTree.cpp @@ -0,0 +1,60 @@ +#include +#include // For max function + +using namespace std; + +// Definition for a binary tree node. +struct TreeNode { + int val; + TreeNode* left; + TreeNode* right; + TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} +}; + +class Solution { +public: + int maxPathSum(TreeNode* root) { + int maxSum = INT_MIN; // Initialize with the minimum possible value + findMaxPathSum(root, maxSum); + return maxSum; + } + +private: + int findMaxPathSum(TreeNode* node, int& maxSum) { + if (!node) { + return 0; + } + + // Calculate the maximum path sum for the left and right subtrees + int leftMax = max(0, findMaxPathSum(node->left, maxSum)); + int rightMax = max(0, findMaxPathSum(node->right, maxSum)); + + // Calculate the maximum path sum that includes the current node + int currentMax = node->val + leftMax + rightMax; + + // Update the overall maximum path sum + maxSum = max(maxSum, currentMax); + + // Return the maximum path sum that can be extended from this node + return node->val + max(leftMax, rightMax); + } +}; + +int main() { + // Create a sample binary tree + TreeNode* root = new TreeNode(10); + root->left = new TreeNode(2); + root->right = new TreeNode(10); + root->left->left = new TreeNode(20); + root->left->right = new TreeNode(1); + root->right->right = new TreeNode(-25); + root->right->right->left = new TreeNode(3); + root->right->right->right = new TreeNode(4); + + Solution solution; + int maxSum = solution.maxPathSum(root); + + cout << "Maximum Path Sum: " << maxSum << endl; + + return 0; +} diff --git a/Trees/trie.cpp b/Trees/trie.cpp new file mode 100644 index 00000000..225b7784 --- /dev/null +++ b/Trees/trie.cpp @@ -0,0 +1,252 @@ +/* + https://leetcode.com/problems/implement-trie-prefix-tree/ + + Implement Trie (also called 'prefix tree') which supports lowercase letters only, + a search tree with multiple childrens (26 in this case). + methods: Insert - push new string into the tree. + Delete - remove string from the tree. + search - check if a string is already pushed into the tree. + StartWith - check if sum prefix is appear in the tree. + + Example: + + int main(){ + Trie trie; + + trie.Insert("abcd"); + trie.Search("abcd") // return true; + trie.Search("ab") // return false; + trie.startWith("ab") // return true; + + trie.Insert("ab"); + trie.Search("ab") // return true; + + trie.Delete("ab") + trie.Search("ab") // return false; + trie.Search("abcd") // return true; + + return 0; + } + + Time Complexity: + Insert() / Search() / startWith() | Average & Worst-case == O(m) | m == number of chars in string. + + Space Complexity: + Space complexity for a trie: O(k * N) | k == size of the node, N == number of the different nodes. + +*/ + + +#include +#include + +enum{CHARS_IN_ALPHABET = 26}; + +class TrieNode{ +public: + TrieNode() : vec(CHARS_IN_ALPHABET, nullptr), end_of_str(false) {}; + +// every node has a vector for his childrens and a flag to mark - end of word. + std::vector vec; + bool end_of_str; +}; +/****************************************************************************/ +class Trie { +public: + Trie(); + ~Trie(); + // non-copyable class + Trie(const Trie& other) = delete; + Trie& operator= (const Trie& other) = delete; + + void Insert(std::string word); + bool Delete(std::string word); + bool Search(std::string word); + bool startWith(std::string prefix); + void Destroy(TrieNode* root); + +private: + enum RecDeleteStatus{NOT_FOUND = -1, DELETE = 0 , COMPLITE = 1}; + + RecDeleteStatus RecDelete(TrieNode* curr_node, const char* string_ptr); + bool IsLeaf(TrieNode* node); + TrieNode* CreatNodes(TrieNode* node_runner, const char* rest_letters); + TrieNode* FindLastChar(const char* first_char_string); + + TrieNode* m_root; +}; +/****************************************************************************/ +Trie::Trie() // Ctor +{ + m_root = new TrieNode(); +} +/****************************************************************************/ +Trie::~Trie() // Dtor +{ + Destroy(m_root); + delete m_root; +} +/****************************************************************************/ +// function for push a word(string) inside the trie +void Trie::Insert(std::string word) +{ + const char* string_ptr = word.data(); + TrieNode* node_runner = m_root; + + while(*string_ptr) + { + // if this node does not have this latter as a child yet. + if(!node_runner->vec[*string_ptr - 'a']) + { + node_runner = CreatNodes(node_runner, string_ptr); + break; + } + + node_runner = node_runner->vec[*string_ptr - 'a']; + ++string_ptr; + } + // mark node as end of word. + node_runner->end_of_str = true; +} +/****************************************************************************/ +// function for remove a word from the trie, if the word or part of her suffix +// stand alone, the whole node will be removed. +bool Trie::Delete(std::string word) +{ + if(RecDelete(m_root, word.data()) != NOT_FOUND) + { + return true; + } + return false; +} +/****************************************************************************/ +// function for check if a string is already pushed into the tree. +bool Trie::Search(std::string word) +{ + TrieNode* node = FindLastChar(word.data()); + // if FindLastChar return nullptr the word isnt in the trie, + // if the word is found but it just a prefix of another word, return false + if(node == nullptr || node->end_of_str == false){ + return false; + } + return true; +} +/****************************************************************************/ +// function for check if sum prefix is appear in the tree. +bool Trie::startWith(std::string prefix) +{ + TrieNode* node = FindLastChar(prefix.data()); + // if FindLastChar return nullptr the word isnt in the trie, + if(node == nullptr){ + return false; + } + return true; +} +/****************************************************************************/ +// Recursive function for delete a word and the whole node if the word or part +// of her suffix stand alone. if the word is prefix of another word, no nodes +// will be removed +Trie::RecDeleteStatus Trie::RecDelete(TrieNode* curr_node, const char* string_ptr) +{ + if(curr_node == nullptr) + { + return NOT_FOUND; + } + if(*string_ptr == '\0') + { + return DELETE; + } + + int char_idx = *string_ptr - 'a'; + RecDeleteStatus status = RecDelete(curr_node->vec[char_idx], string_ptr + 1); + + if(status == DELETE) + { + if(IsLeaf(curr_node->vec[char_idx])) + { + delete curr_node->vec[char_idx]; + curr_node->vec[char_idx] = nullptr; + return DELETE; + } + else if(*(string_ptr + 1) == '\0') + { + if(curr_node->vec[char_idx]->end_of_str) + { + curr_node->vec[char_idx]->end_of_str = false; + return DELETE; + } + else + { + return NOT_FOUND; + } + } + } + if(status == NOT_FOUND) + { + return NOT_FOUND; + } + return COMPLITE; +} +/****************************************************************************/ +// function to check if node does not have childrens +bool Trie::IsLeaf(TrieNode* curr_node) +{ + for(auto it : curr_node->vec) + { + if(it != nullptr) + { + return false; + } + } + return true; +} +/****************************************************************************/ +// function for create and push new nodes ,from the input node(node_runner) +// according to the string(string_ptr) that must have a null termination sign. +TrieNode* Trie::CreatNodes(TrieNode* node_runner, const char* string_ptr) +{ + while(*string_ptr) + { + TrieNode* new_node = new TrieNode(); + + node_runner->vec[*string_ptr - 'a'] = new_node; + node_runner = new_node; + ++string_ptr; + } + + return node_runner; +} +/****************************************************************************/ +// function for search and return the node of the last character in a string +// return nullptr if the word is not found. +TrieNode* Trie::FindLastChar(const char* string_ptr) +{ + TrieNode* node_runner = m_root; + + while(*string_ptr) + { + TrieNode* next_node = node_runner->vec[*string_ptr - 'a']; + if(nullptr == next_node) + { + return nullptr; + } + + node_runner = next_node; + ++string_ptr; + } + return node_runner; +} +/****************************************************************************/ +// destroy all nodes inside the trie except the root +void Trie::Destroy(TrieNode* node) +{ + if(node == nullptr) + { + return; + } + for(auto it : node->vec) + { + Destroy(it); + delete it; + } +} diff --git a/Trees/trie.go b/Trees/trie.go new file mode 100644 index 00000000..8f98cd9f --- /dev/null +++ b/Trees/trie.go @@ -0,0 +1,117 @@ +/* +Approach: +In this implementation, we define two struct types: `TrieNode` and `Trie`. `TrieNode` represents a single node in the Trie. It has two fields: `children` (a map of rune to `TrieNode`) to store the child nodes and `isWord` (a boolean) to indicate the end of a word. + +`Trie` represents the Trie data structure and has a single field `root` (a pointer to `TrieNode`), which is initialized with an empty `TrieNode` in the `NewTrie` function. + +The Trie supports three operations: +- `Insert` function is used to add a word to the Trie. It starts from the root node and traverses through each character of the word, creating new nodes as needed. +- `Search` function is used to check if a given word exists in the Trie. It follows a similar approach to the `Insert` function but returns `true` if the final node marks the end of a word. +- `StartsWith` function is used to check if there is any word in the Trie that starts with a given prefix. It works similar to the `Search` function but does not require the final node to mark the end of a word. + +Sample Input: +In the `main` function demonstrates the usage of the Trie data structure. We insert words "apple", "app", "application", "book", and "dog" into the Trie. Then, we search for words "app", "apple", and "banana". Finally, we check if there are any words in the Trie that start with prefixes "ap", "do", and "cat". + +Time complexity of the `Insert`, `Search`, and `StartsWith` operations: O(m), where m is the length of the word or prefix being processed. + +Space complexity: O(n), where n is the total number of characters in all the words inserted in the Trie. +*/ + +//Here's an implementation of the Trie data structure in Go: + +package main + +import "fmt" + +// TrieNode represents a single node in the Trie +type TrieNode struct { + children map[rune]*TrieNode + isWord bool +} + +// Trie represents the Trie data structure +type Trie struct { + root *TrieNode +} + +// NewTrie initializes a new Trie +func NewTrie() *Trie { + return &Trie{ + root: &TrieNode{ + children: make(map[rune]*TrieNode), + isWord: false, + }, + } +} + +// Insert adds a word to the Trie +func (t *Trie) Insert(word string) { + node := t.root + + // Traverse through each character of the word + for _, ch := range word { + if node.children[ch] == nil { + node.children[ch] = &TrieNode{ + children: make(map[rune]*TrieNode), + isWord: false, + } + } + node = node.children[ch] + } + + // Mark the end of the word + node.isWord = true +} + +// Search returns true if the word exists in the Trie, otherwise false +func (t *Trie) Search(word string) bool { + node := t.root + + // Traverse through each character of the word + for _, ch := range word { + if node.children[ch] == nil { + return false + } + node = node.children[ch] + } + + // Check if the node marks the end of a word + return node.isWord +} + +// StartsWith returns true if there is any word in the Trie that starts with the given prefix, otherwise false +func (t *Trie) StartsWith(prefix string) bool { + node := t.root + + // Traverse through each character of the prefix + for _, ch := range prefix { + if node.children[ch] == nil { + return false + } + node = node.children[ch] + } + + return true +} + +func main() { + trie := NewTrie() + + // Inserting words into the Trie + trie.Insert("apple") + trie.Insert("app") + trie.Insert("application") + trie.Insert("book") + trie.Insert("dog") + + // Searching words in the Trie + fmt.Println(trie.Search("app")) // Output: true + fmt.Println(trie.Search("apple")) // Output: true + fmt.Println(trie.Search("banana")) // Output: false + + // Checking prefixes in the Trie + fmt.Println(trie.StartsWith("ap")) // Output: true + fmt.Println(trie.StartsWith("do")) // Output: true + fmt.Println(trie.StartsWith("cat")) // Output: false +} + diff --git a/Trees/trie.java b/Trees/trie.java new file mode 100644 index 00000000..2873d701 --- /dev/null +++ b/Trees/trie.java @@ -0,0 +1,97 @@ +/* +Description +A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker. + +Implement the Trie class: + +Trie() Initializes the trie object. +void insert(String word) Inserts the string word into the trie. +boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. +boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise. + +Input +["Trie", "insert", "search", "search", "startsWith", "insert", "search"] +[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] +Output +[null, null, true, false, true, null, true] + +Explanation +Trie trie = new Trie(); +trie.insert("apple"); +trie.search("apple"); // return True +trie.search("app"); // return False +trie.startsWith("app"); // return True +trie.insert("app"); +trie.search("app"); // return True + +Time Complexity for each operation is O(n) +Space Complexity O(n*m) where n is the number of words inserted and m is the average length of the words. + +Explanation: +insert() -> traverse through each character of the input word and initializes it if necessary. If the end of the word is reached set isEnd to true. +search() -> Search In each child node until the end of the word is reached, then if end of the node is also reached return true else false. +startsWith() -> Similar to search method but we only check if end of the prefix is reached and we don't need to check if it is the end of the node. + +*/ + +class Trie { + Node root; + + public Trie() { + root = new Node(); + } + + public void insert(String word) { + root.insert(word, 0); + } + + public boolean search(String word) { + return root.search(word, 0); + } + + public boolean startsWith(String prefix) { + return root.startsWith(prefix, 0); + } + + class Node { + Node[] nodes; + boolean isEnd; + + Node() { + nodes = new Node[26]; + } + + // Function to insert the word in the tree + private void insert(String word, int idx) { + if (idx >= word.length()) return; // handle edge case + int i = word.charAt(idx) - 'a'; + if (nodes[i] == null) { + nodes[i] = new Node(); // initialize the node[i] if the letter was not found before + } + + if (idx == word.length()-1) nodes[i].isEnd = true; // signifies that this is the end of the word + nodes[i].insert(word, idx+1); // recursive call to populate the child node + } + + // Function to search the word in the tree + private boolean search(String word, int idx) { + if (idx >= word.length()) return false; + Node node = nodes[word.charAt(idx) - 'a']; + if (node == null) return false; // if the node is null it means that it was not initialised hence the character was never found. + if (idx == word.length() - 1 && node.isEnd) return true; //if it is the last character and the end of the node then return true + + return node.search(word, idx+1); // recursive call search in the child node + + } + + //Function to search the prefix in tree + private boolean startsWith(String prefix, int idx) { + if (idx >= prefix.length()) return false; + Node node = nodes[prefix.charAt(idx) - 'a']; + if (node == null) return false; + if (idx == prefix.length() - 1) return true; // Very similar to above method but here we don't need to check if it is the end of the node. + + return node.startsWith(prefix, idx+1); + } + } +} \ No newline at end of file diff --git a/Tries/pattern_matching.cpp b/Tries/pattern_matching.cpp new file mode 100644 index 00000000..2d44e53d --- /dev/null +++ b/Tries/pattern_matching.cpp @@ -0,0 +1,186 @@ +/* +Given a list of n words and a pattern p that we want to search. Check if the pattern p is present the given words or not. Return true if the pattern is present and false otherwise. +Input Format : + +The first line of input contains an integer, that denotes the value of n. +The following line contains n space separated words. +The following line contains a string, that denotes the value of the pattern p. + +Output Format : + +The first and only line of output contains true if the pattern is present and false otherwise. + +Constraints: + +Time Limit: 1 sec + +Sample Input 1 : + +4 +abc def ghi cba +de + +Sample Output 2 : + +true + +Sample Input 2 : + +4 +abc def ghi hg +hi + +Sample Output 2 : + +true + +Sample Input 3 : + +4 +abc def ghi hg +hif + +Sample Output 3 : + +false + +Explaination : + The TrieNode class represents a single node in the Trie. Each node contains a character data, an array of pointers to its child nodes children, and a boolean flag isTerminal to indicate if a word ends at that node. + + The Trie class serves as the main data structure and contains a pointer to the root node of the Trie, along with a count variable to keep track of the number of words inserted. + + The insertWord function is used to insert a word into the Trie. It takes a root parameter representing the current node and a word parameter representing the word to be inserted. The function recursively traverses the Trie, creating new nodes as needed and marking the last node as terminal if the word does not already exist. + + The search function is used to search for a word in the Trie. It takes a root parameter and a word parameter. The function recursively checks if each character in the word exists as a child node starting from the given root. + + The patternMatching function takes a vector of strings vect and a pattern string. It iterates over each word in the vector and inserts all possible substrings of that word into the Trie. Finally, it performs a search operation on the pattern string in the Trie and returns the result. +*/ +#include +#include +#include +using namespace std; + +class TrieNode +{ +public: + char data; + TrieNode **children; + bool isTerminal; + + TrieNode(char data) + { + this->data = data; + children = new TrieNode *[26]; + for (int i = 0; i < 26; i++) + { + children[i] = NULL; + } + isTerminal = false; + } +}; + +class Trie +{ + TrieNode *root; + +public: + int count; + + Trie() + { + this->count = 0; + root = new TrieNode('\0'); + } + + bool insertWord(TrieNode *root, string word) + { + // Base case + if (word.size() == 0) + { + if (!root->isTerminal) + { + root->isTerminal = true; + return true; + } + else + { + return false; + } + } + + // Small calculation + int index = word[0] - 'a'; + TrieNode *child; + if (root->children[index] != NULL) + { + child = root->children[index]; + } + else + { + child = new TrieNode(word[0]); + root->children[index] = child; + } + + // Recursive call + return insertWord(child, word.substr(1)); + } + + void insertWord(string word) + { + if (insertWord(root, word)) + { + this->count++; + } + } + + bool search(TrieNode *root, string word) + { + if (word.length() == 0) + { + return true; + } + if (root->children[word[0] - 'a'] == NULL) + { + return false; + } + bool ans = search(root->children[word[0] - 'a'], word.substr(1)); + return ans; + } + + bool search(string word) + { + return search(root, word); + } + + bool patternMatching(vector vect, string pattern) + { + for (int i = 0; i < vect.size(); i++) + { + string word = vect[i]; + for (int j = 0; j < word.size(); j++) + { + insertWord(word.substr(j)); + } + } + return search(pattern); + } +}; + +int main() +{ + Trie t; + int n; + cin >> n; + string pattern; + vector vect; + + for (int i = 0; i < n; ++i) + { + string word; + cin >> word; + vect.push_back(word); + } + cin >> pattern; + + cout << (t.patternMatching(vect, pattern) ? "true" : "false"); +} \ No newline at end of file diff --git a/Tries/search_in_tries.cpp b/Tries/search_in_tries.cpp new file mode 100644 index 00000000..59cc41c3 --- /dev/null +++ b/Tries/search_in_tries.cpp @@ -0,0 +1,132 @@ +/* +Implement the function SearchWord for the Trie class. +For a Trie, write the function for searching a word. Return true if the word is found successfully, otherwise return false. +Note : main function is given for your reference which we are using internally to test the code. + +Explaination : + The given code implements a Trie data structure and focuses on implementing the search functionality. The TrieNode class represents a single node in the Trie, and the Trie class serves as the main data structure. + + The insertWord function is used to insert a word into the Trie. It takes a root parameter representing the current node and a word parameter representing the word to be inserted. The function recursively traverses the Trie, creating new nodes as needed and marking the last node as terminal. + + The search function is used to search for a word in the Trie. It takes a root parameter and a word parameter. The function recursively checks if each character in the word exists as a child node starting from the given root. It returns true if the word is found and the last node is marked as terminal, otherwise it returns false. + + In the main function, an instance of the Trie class is created. The program then enters a loop where the user can input commands. The user is prompted to enter a choice: 1 for inserting a word or 2 for searching a word. If the choice is 1, the user can enter a word to be inserted into the Trie. If the choice is 2, the user can enter a word to search for in the Trie. The result of the search operation is printed as "true" or "false" accordingly. +*/ +#include +#include +using namespace std; + +class TrieNode +{ +public: + char data; + TrieNode **children; + bool isTerminal; + + TrieNode(char data) + { + this->data = data; + children = new TrieNode *[26]; + for (int i = 0; i < 26; i++) + { + children[i] = NULL; + } + isTerminal = false; + } +}; + +class Trie +{ + TrieNode *root; + +public: + Trie() + { + root = new TrieNode('\0'); + } + + void insertWord(TrieNode *root, string word) + { + // Base case + if (word.size() == 0) + { + root->isTerminal = true; + return; + } + + // Small Calculation + int index = word[0] - 'a'; + TrieNode *child; + if (root->children[index] != NULL) + { + child = root->children[index]; + } + else + { + child = new TrieNode(word[0]); + root->children[index] = child; + } + + // Recursive call + insertWord(child, word.substr(1)); + } + + void insertWord(string word) + { + insertWord(root, word); + } + + bool search(TrieNode *root, string word) + { + if (word.length() == 0) + { + return root->isTerminal && true; + } + int index = word[0] - 'a'; + TrieNode *child; + if (root->children[index] != NULL) + { + child = root->children[index]; + } + else + { + return root->isTerminal && false; + } + bool ans = search(child, word.substr(1)); + return ans; + } + + bool search(string word) + { + return search(root, word); + } +}; + +int main() +{ + int choice; + cin >> choice; + Trie t; + + while (choice != -1) + { + string word; + bool ans; + switch (choice) + { + case 1: // insert + cin >> word; + t.insertWord(word); + break; + case 2: // search + cin >> word; + cout << (t.search(word) ? "true\n" : "false\n"); + break; + default: + return 0; + } + cin >> choice; + } + + return 0; +} \ No newline at end of file diff --git a/Tries/trie_node_class.cpp b/Tries/trie_node_class.cpp new file mode 100644 index 00000000..496362b6 --- /dev/null +++ b/Tries/trie_node_class.cpp @@ -0,0 +1,153 @@ +/* +Explaination : + The TrieNode class represents a single node in the Trie. Each node contains a character data, an array of pointers to its child nodes children, and a boolean flag isTerminal to indicate if a word ends at that node. + + The Trie class serves as the main data structure and contains a pointer to the root node of the Trie. It provides public functions to insert a word into the Trie, search for a word in the Trie, and remove a word from the Trie. + + The insertWord function is a private member function of the Trie class and is responsible for inserting a word into the Trie. It takes a root parameter representing the current node and a word parameter representing the word to be inserted. The function recursively traverses the Trie, creating new nodes as needed and marking the last node as terminal. + + The search function is also a private member function of the Trie class and is used to search for a word in the Trie. It takes a root parameter and a word parameter. The function recursively checks if each character in the word exists as a child node starting from the given root and returns true if the entire word is found and marked as terminal. + + The removeWord function is another private member function of the Trie class and is used to remove a word from the Trie. It takes a root parameter and a word parameter. The function recursively searches for the word in the Trie, marks the last node of the word as non-terminal, and removes any unnecessary child nodes that are not part of other words. + + In the main function, an instance of the Trie class is created. Words "and", "dot", and "double" are inserted into the Trie using the insertWord function. The search function is then used to check if the word "and" exists in the Trie, and the result is printed. The removeWord function is called to remove the word "and" from the Trie, and again the search function is used to check if the word "and" exists in the Trie after removal, and the result is printed. +*/ +#include +#include +using namespace std; + +class TrieNode +{ +public: + char data; + TrieNode **children; + bool isTerminal; + + TrieNode(char data) + { + this->data = data; + children = new TrieNode *[26]; + for (int i = 0; i < 26; i++) + { + children[i] = NULL; + } + isTerminal = false; + } +}; + +class Trie +{ + TrieNode *root; + +public: + Trie() + { + root = new TrieNode('\0'); + } + +private: + void insertWord(TrieNode *root, string word) + { + // Base Case + if (word.length() == 0) + { + root->isTerminal = true; + return; + } + // Small Calculation + int index = word[0] - 'a'; + TrieNode *child; + if (root->children[index] != NULL) + { + child = root->children[index]; + } + else + { + child = new TrieNode(word[0]); + root->children[index] = child; + } + // Recursive Call + insertWord(child, word.substr(1)); + } + + bool search(TrieNode *root, string word) + { + if (word.length() == 0) + { + return root->isTerminal && true; + } + int index = word[0] - 'a'; + TrieNode *child; + if (root->children[index] != NULL) + { + child = root->children[index]; + } + else + { + return root->isTerminal && false; + } + bool ans = search(child, word.substr(1)); + return ans; + } + void removeWord(TrieNode *root, string word) + { + if (word.size() == 0) + { + root->isTerminal = false; + return; + } + int index = word[0] - 'a'; + TrieNode *child; + if (root->children[index] != NULL) + { + child = root->children[index]; + } + else + { + // Word not found + return; + } + removeWord(child, word.substr(1)); + + // Remove Child Node if it is useless + if (child->isTerminal == false) + { + for (int i = 0; i < 26; i++) + { + if (child->children[i] != NULL) + { + return; + } + } + delete child; + root->children[index] = NULL; + } + } + +public: + void insertWord(string word) + { + insertWord(root, word); + } + + bool search(string word) + { + return search(root, word); + } + + void removeWord(string word) + { + removeWord(root, word); + } +}; + +int main() +{ + Trie t; + t.insertWord("and"); + t.insertWord("dot"); + t.insertWord("double"); + cout << t.search("and") << endl; + t.removeWord("and"); + cout << t.search("and") << endl; +} \ No newline at end of file diff --git a/sorting/.DS_Store b/sorting/.DS_Store new file mode 100644 index 00000000..bb12766e Binary files /dev/null and b/sorting/.DS_Store differ diff --git a/sorting/Cyclic_Sort.java b/sorting/Cyclic_Sort.java new file mode 100644 index 00000000..53cd5dd7 --- /dev/null +++ b/sorting/Cyclic_Sort.java @@ -0,0 +1,43 @@ +/* +What is Cyclic Sort? +The basic idea behind cycle sort is to divide the input array into cycles, where each cycle consists of elements that belong to the same position in the sorted output array. The algorithm then performs a series of swaps to place each element in its correct position within its cycle, until all cycles are complete and the array is sorted. +It is usually used where elements are in the range of (1,n) + +Note : This Algorithm solution is for elements from 1-N , where N is the number of elements in the array. +Time Complexity Analysis: + Worst Case: O(n) + Average Case: O(n) + Best Case: O(n) +Auxiliary Space: O(1) +*/ + +import java.util.Arrays; + +public class Cyclic_Sort { + public static void main(String[] args) { + int[] arr={3, 5, 2, 1, 4}; //Sample Input + sort(arr); + System.out.println(Arrays.toString(arr)); //Printing the original array + } + + //Cyclic Sort Program + static void sort(int[] arr){ + int i=0; //Variable to iterate over each element of array + while(i +#include + +using namespace std; + +void bubbleSort(vector& arr) { + int n = arr.size(); + // Traverse through all array elements + for (int i = 0; i < n - 1; i++) { + // Last i elements are already sorted + for (int j = 0; j < n - i - 1; j++) { + // Swap adjacent elements if they are in the wrong order + if (arr[j] > arr[j + 1]) { + swap(arr[j], arr[j + 1]); + } + } + } +} + +int main() { + vector arr = {64, 25, 12, 22, 11}; + bubbleSort(arr); + cout << "Sorted array: "; + for (auto i : arr) { + cout << i << " "; + } + return 0; +} diff --git a/sorting/bubble_sort.go b/sorting/bubble_sort.go new file mode 100644 index 00000000..e3c1da0a --- /dev/null +++ b/sorting/bubble_sort.go @@ -0,0 +1,51 @@ +/* + Here's how the Bubble Sort algorithm works: + + 1. We start by comparing the first two elements of the array. If the first element is greater than the second + element, we swap them. + 2. We then compare the second and third elements. If the second element is greater than the third element, + we swap them. + 3. We continue this process until we reach the end of the array. At this point, the largest element will + be at the end of the array. + 4. We then repeat steps 1-3 for the remaining unsorted portion of the array until the entire array is sorted. + + The time complexity of Bubble Sort is O(n^2) in the worst and average case, and O(n) in the best case when + the input array is already sorted. + + The space complexity is O(1) as Bubble Sort operates on the input array in-place. + + Bubble sort is O(n) on a list that is already sorted i.e. Best case + + Sample Input : [2, 1, 9, 3, 5, 4, 0] + Output : [0 1 2 3 4 5 9] +*/ + +package main + +import "fmt" + +func bubbleSort(arr []int) { + n := len(arr) + flag := true + // Traverse through all array elements + for i := 0; i < n-1; i++ { + // Last i elements are already sorted + for j := 0; j < n-i-1; j++ { + // Swap adjacent elements if they are in the wrong order + if arr[j] > arr[j+1] { + arr[j], arr[j+1] = arr[j+1], arr[j] + flag = false + } + } + if flag { + fmt.Println("Already sorted so no further redundant passes best case O(n)") + break + } + } +} + +func main() { + arr := []int{64, 25, 12, 22, 11} + bubbleSort(arr) + fmt.Println("Sorted array:", arr) +} diff --git a/sorting/bubble_sort.java b/sorting/bubble_sort.java new file mode 100644 index 00000000..56ffc099 --- /dev/null +++ b/sorting/bubble_sort.java @@ -0,0 +1,47 @@ +/* + Here's how the Bubble Sort algorithm works: + + 1. We start by comparing the first two elements of the array. If the first element is greater than the second + element, we swap them. + 2. We then compare the second and third elements. If the second element is greater than the third element, + we swap them. + 3. We continue this process until we reach the end of the array. At this point, the largest element will + be at the end of the array. + 4. We then repeat steps 1-3 for the remaining unsorted portion of the array until the entire array is sorted. + + The time complexity of Bubble Sort is O(n^2) in the worst and average case, and O(n) in the best case when + the input array is already sorted. + + The space complexity is O(1) as Bubble Sort operates on the input array in-place. + + Bubble sort is O(n) on a list that is already sorted i.e. Best case + + Sample Input : [2, 1, 9, 3, 5, 4, 0] + Output : [0 1 2 3 4 5 9] +*/ + +import java.util.Arrays; + +public class bubble_sort { + public static void bubbleSort(int[] arr) { + int n = arr.length; + // Traverse through all array elements + for (int i = 0; i < n - 1; i++) { + // Last i elements are already in place + for (int j = 0; j < n - i - 1; j++) { + // Swap adjacent elements if they are in the wrong order + if (arr[j] > arr[j + 1]) { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } + } + + public static void main(String[] args) { + int[] arr = {64, 34, 25, 12, 22, 11, 90}; + bubbleSort(arr); + System.out.println(Arrays.toString(arr)); + } +} diff --git a/sorting/bubble_sort.js b/sorting/bubble_sort.js new file mode 100644 index 00000000..af6b2ae4 --- /dev/null +++ b/sorting/bubble_sort.js @@ -0,0 +1,41 @@ +/* + Here's how the Bubble Sort algorithm works: + + 1. We start by comparing the first two elements of the array. If the first element is greater than the second + element, we swap them. + 2. We then compare the second and third elements. If the second element is greater than the third element, + we swap them. + 3. We continue this process until we reach the end of the array. At this point, the largest element will + be at the end of the array. + 4. We then repeat steps 1-3 for the remaining unsorted portion of the array until the entire array is sorted. + + The time complexity of Bubble Sort is O(n^2) in the worst and average case, and O(n) in the best case when + the input array is already sorted. + + The space complexity is O(1) as Bubble Sort operates on the input array in-place. + + Bubble sort is O(n) on a list that is already sorted i.e. Best case + + Sample Input : [2, 1, 9, 3, 5, 4, 0] + Output : [0 1 2 3 4 5 9] +*/ + +function bubbleSort(arr) { + var n = arr.length; + // Traverse through all array elements + for (var i = 0; i < n - 1; i++) { + // Last i elements are already sorted + for (var j = 0; j < n - i - 1; j++) { + // Swap adjacent elements if they are in the wrong order + if (arr[j] > arr[j + 1]) { + var temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } +} + +var arr = [64, 25, 12, 22, 11]; +bubbleSort(arr); +console.log("Sorted array: " + arr); diff --git a/sorting/bubble_sort.py b/sorting/bubble_sort.py new file mode 100644 index 00000000..16e30fd7 --- /dev/null +++ b/sorting/bubble_sort.py @@ -0,0 +1,35 @@ +''' + Here's how the Bubble Sort algorithm works: + + 1. We start by comparing the first two elements of the array. If the first element is greater than the second + element, we swap them. + 2. We then compare the second and third elements. If the second element is greater than the third element, + we swap them. + 3. We continue this process until we reach the end of the array. At this point, the largest element will + be at the end of the array. + 4. We then repeat steps 1-3 for the remaining unsorted portion of the array until the entire array is sorted. + + The time complexity of Bubble Sort is O(n^2) in the worst and average case, and O(n) in the best case when + the input array is already sorted. + + The space complexity is O(1) as Bubble Sort operates on the input array in-place. + + Bubble sort is O(n) on a list that is already sorted i.e. Best case + + Sample Input : [2, 1, 9, 3, 5, 4, 0] + Output : [0 1 2 3 4 5 9] +''' + +def bubbleSort(arr): + n = len(arr) + # Traverse through all array elements + for i in range(n - 1): + # Last i elements are already sorted + for j in range(0, n - i - 1): + # Swap adjacent elements if they are in the wrong order + if arr[j] > arr[j + 1]: + arr[j], arr[j + 1] = arr[j + 1], arr[j] + +arr = [64, 25, 12, 22, 11] +bubbleSort(arr) +print("Sorted array:", arr) diff --git a/sorting/bubblesort.cpp b/sorting/bubblesort.cpp deleted file mode 100644 index c17ea866..00000000 --- a/sorting/bubblesort.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Implementation of Bubble sort. -// Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm -// that repeatedly steps through the input list element by element, -// comparing the current element with the one after it, swapping their values if needed. -// These passes through the list are repeated until no swaps had to be performed during a pass, -// meaning that the list has become fully sorted. (Source wiki) https://en.wikipedia.org/wiki/Bubble_sort - -// Time Complexity worst-case and average complexity O(n^{2}) -// Bubble sort is O(n) on a list that is already sorted i.e. Best case - -// Sample Input : [2, 1, 9, 3, 5, 4, 0] -// Output : [0 1 2 3 4 5 9] -#include -using namespace std; - -void BubbleSort(int A[], int n){ - int flag = 0; - for(int i = 0; i < n - 1; i++){ - for(int j = 0; j < n - i; j++){ - if(A[j] > A[j+1]){ - int temp = A[j]; - A[j] = A[j+1]; - A[j+1] = temp; - flag = 1; // // hack if the array is already sorted, no need for redundant passes - } - } - if(flag == 0){ cout << "Already sorted so no further redundant passes best case O(n)"; break;} - } -} -int main(){ - int A[] = {5,1,2,3,4,5}; - BubbleSort(A,6); - for(int i = 0; i < 6; i++){ - cout << A[i] << " "; - } - -return 0; -} diff --git a/sorting/bubblesort.go b/sorting/bubblesort.go deleted file mode 100644 index e24a5a86..00000000 --- a/sorting/bubblesort.go +++ /dev/null @@ -1,36 +0,0 @@ -// Implementation of Bubble sort. -// Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm -// that repeatedly steps through the input list element by element, -// comparing the current element with the one after it, swapping their values if needed. -// These passes through the list are repeated until no swaps had to be performed during a pass, -// meaning that the list has become fully sorted. (Source wiki) https://en.wikipedia.org/wiki/Bubble_sort - -// Time Complexity worst-case and average complexity O(n^{2}) -// Bubble sort is O(n) on a list that is already sorted i.e. Best case - -// Sample Input : [2, 1, 9, 3, 5, 4, 0] -// Output : [0 1 2 3 4 5 9] - -package main - -import "fmt" - -func main() { - arr := [6]int{2, 1, 3, 5, 4, 0} - // arr := [6]int{0, 1, 2, 3, 4, 5} uncomment for best case - flag := true - for i := 0; i < 6; i++ { - for j := 0; j < 6 - i - 1; j++ { - if(arr[j] > arr[j + 1]) { - arr[j], arr[j + 1] = arr[j + 1], arr[j] - flag = false // hack if the array is already sorted, no need for redundant passes - } - } - if flag { - fmt.Println("Already sorted so no further redundant passes best case O(n)") - break - } - } - fmt.Println(arr) - -} \ No newline at end of file diff --git a/sorting/bubblesort.js b/sorting/bubblesort.js deleted file mode 100644 index 63c8fb0f..00000000 --- a/sorting/bubblesort.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -Bubble sort is a sorting algorithm that compares two adjacent elements -and swaps them until they are in the intended order. -*/ - -const inputArr = [4,5,67,56,3,35,45]; - -const bubbleSort = (arr) => { - let len = arr.length; - - //loop to access each array element - for(let i = 0; i < len-1; i++) - - //loop to compare array elements - for(let j = i+1; j < len; j++) - - //compare two adjacent elements - if(arr[i] > arr[j]) - { - //swapping elements if elements are not in the intended order - let temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp - - } - return arr; -} -bubbleSort(inputArr); -console.log(inputArr); \ No newline at end of file diff --git a/sorting/bubblesort.py b/sorting/bubblesort.py deleted file mode 100644 index 7485a324..00000000 --- a/sorting/bubblesort.py +++ /dev/null @@ -1,28 +0,0 @@ -# Implementation of Bubble sort. -# Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm -# that repeatedly steps through the input list element by element, -# comparing the current element with the one after it, swapping their values if needed. -# These passes through the list are repeated until no swaps had to be performed during a pass, -# meaning that the list has become fully sorted. (Source wiki) https://en.wikipedia.org/wiki/Bubble_sort - -# Time Complexity worst-case and average complexity O(n^{2}) -# Bubble sort is O(n) on a list that is already sorted i.e. Best case - -# Sample Input : [2, 1, 9, 3, 5, 4, 0] -# Output : [0 1 2 3 4 5 9] -data = [] -n = int(input("Enter the number of elements")) # Total number of elements in an array - -for i in range(0,n): - num = int(input()) # Asking user the element of an array - data.append(num) # Appending the element in an array - -data_copy = data[:] # Making copy of the array data - -for i in range(0, len(data_copy)): - for j in range(0, len(data_copy) - i - 1): - if(data_copy[j] > data_copy[j + 1]): - data_copy[j], data_copy[j + 1] = data_copy[j + 1], data_copy[j] - -print(data_copy) -print(sorted(data)) diff --git a/sorting/bucket-sort.js b/sorting/bucket-sort.js new file mode 100644 index 00000000..ec057f31 --- /dev/null +++ b/sorting/bucket-sort.js @@ -0,0 +1,51 @@ +function bucketSort(arr, bucketSize = 5) { + if (arr.length === 0) { + return arr; + } + + // Determine minimum and maximum values in the array + let minValue = arr[0]; + let maxValue = arr[0]; + for (let i = 1; i < arr.length; i++) { + if (arr[i] < minValue) { + minValue = arr[i]; + } else if (arr[i] > maxValue) { + maxValue = arr[i]; + } + } + + // Determine number of buckets needed and initialize empty buckets + const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; + const buckets = new Array(bucketCount); + for (let i = 0; i < bucketCount; i++) { + buckets[i] = []; + } + + // Assign array elements to buckets based on their value + for (let i = 0; i < arr.length; i++) { + const bucketIndex = Math.floor((arr[i] - minValue) / bucketSize); + buckets[bucketIndex].push(arr[i]); + } + + // Sort each bucket using another sorting algorithm (here, insertion sort) + const sortedArray = []; + for (let i = 0; i < buckets.length; i++) { + const bucket = buckets[i]; + insertionSort(bucket); + sortedArray.push(...bucket); + } + + return sortedArray; +} + +function insertionSort(arr) { + for (let i = 1; i < arr.length; i++) { + const currentValue = arr[i]; + let j = i - 1; + while (j >= 0 && arr[j] > currentValue) { + arr[j + 1] = arr[j]; + j--; + } + arr[j + 1] = currentValue; + } +} diff --git a/sorting/bucket_sort.cpp b/sorting/bucket_sort.cpp new file mode 100644 index 00000000..32879eb7 --- /dev/null +++ b/sorting/bucket_sort.cpp @@ -0,0 +1,53 @@ +/*The code implements the bucket sort algorithm in C++. It defines a bucketSort function that takes a vector of integers as input and sorts it using the bucket sort technique. The algorithm distributes the elements into separate buckets based on their values, sorts each bucket individually, and then concatenates the sorted buckets to obtain the final sorted array.*/ +/*Time Complexity: O(n + k) for best case and average case and O(n^2) for the worst case. +The space complexity of bucket sort is O(n + k)*/ +#include +#include +#include + +using namespace std; + +void bucketSort(vector& arr) { + // Find the maximum value in the array to determine the range of buckets + int max_value = *max_element(arr.begin(), arr.end()); + + // Calculate the number of buckets needed + int num_buckets = max_value + 1; + + // Create empty buckets + vector> buckets(num_buckets); + + // Distribute the elements into buckets + for (int num : arr) { + buckets[num].push_back(num); + } + + // Sort each bucket individually + for (auto& bucket : buckets) { + sort(bucket.begin(), bucket.end()); + } + + // Concatenate the sorted buckets to get the sorted array + int index = 0; + for (const auto& bucket : buckets) { + for (int num : bucket) { + arr[index++] = num; + } + } +} + +int main() { + // Create an array of integers + vector array = {5, 3, 9, 1, 8, 2, 7, 4, 6}; + + // Sort the array using bucket sort + bucketSort(array); + + // Print the sorted array + for (int num : array) { + cout << num << " "; + } + cout << endl; + + return 0; +} diff --git a/sorting/bucket_sort.go b/sorting/bucket_sort.go new file mode 100644 index 00000000..03a71b12 --- /dev/null +++ b/sorting/bucket_sort.go @@ -0,0 +1,82 @@ +/* + Bucket sort, or bin sort, is a sorting algorithm that works by distributing + the elements of an array into a number of buckets. Each bucket is then + sorted individually, either using a different sorting algorithm, + or by recursively applying the bucket sorting algorithm. + It is a distribution sort, a generalization of pigeonhole sort that + allows multiple keys per bucket, and is a cousin of + radix sort in the most-to-least significant digit flavor. + Bucket sort can be implemented with comparisons and therefore can also + be considered a comparison sort algorithm. + The computational complexity depends on the algorithm used to sort + each bucket, the number of buckets to use, + and whether the input is uniformly distributed. + + Bucket sort works as follows: + + Set up an array of initially empty "buckets". + Scatter: Go over the original array, putting each object in its bucket. + Sort each non-empty bucket. + Gather: Visit the buckets in order and put all elements back into the original array. + + Source(https://en.wikipedia.org/wiki/Bucket_sort) +*/ +// The average time complexity for Bucket Sort is O(n + k). +// The worst time complexity is O(n²). +// The space complexity for Bucket Sort is O(n+k). +package main + +import ( + "fmt" +) + +func InsertionSort(Array []int) { + for i := 0; i < len(Array); i++ { + temp := Array[i] + j := i - 1 + for ; j >= 0 && Array[j] > temp; j-- { + Array[j+1] = Array[j] + } + Array[j+1] = temp + } +} + +func BucketSort(Array []int, bucketSize int) []int { + var max, min int + for _, n := range Array { + if n < min { + min = n + } + if n > max { + max = n + } + } + nBuckets := int(max-min)/bucketSize + 1 + buckets := make([][]int, nBuckets) + for i := 0; i < nBuckets; i++ { + buckets[i] = make([]int, 0) + } + + for _, n := range Array { + idx := int(n-min) / bucketSize + buckets[idx] = append(buckets[idx], n) + } + + sorted := make([]int, 0) + for _, bucket := range buckets { + if len(bucket) > 0 { + InsertionSort(bucket) + sorted = append(sorted, bucket...) + } + } + + return sorted +} + +func main() { + Array := []int{3, 4, 5, 2, 1} + Array = BucketSort(Array, 2) + for _, val := range Array { + fmt.Println(val) + } +} \ No newline at end of file diff --git a/sorting/bucket_sort.java b/sorting/bucket_sort.java new file mode 100644 index 00000000..bd621847 --- /dev/null +++ b/sorting/bucket_sort.java @@ -0,0 +1,93 @@ +/* + +Here is a step by step process : +Set up an array of initially empty "buckets". +Scatter: Go over the original array, putting each object in its bucket. +Sort each non-empty bucket. +Gather: Visit the buckets in order and put all elements back into the original array. + +Bucket Sort time complexity +Best Case Time Complexity: O(n+k) +Average Case Time Complexity: O(n) +Worst Case Time Complexity: O(n^2^) +Best Case Time Complexity: +If the array elements are uniformly distributed, bucket size will almost be the same for all the buckets. Hence, this will be the best case which will take up the least amount of time. +Sorting time complexity will reduce even further if all the elements inside each bucket are already sorted. +To create n buckets and scatter each element from the array, time complexity = O(n). If we use Insertion sort to sort each bucket, time complexity = O(k). Hence, best case time complexity for bucket sort = O(n+k), where n = number of elements, and k = number of buckets +Worst Case Time Complexity +If the array elements are not uniformly distributed, i.e., elements are concentrated within specific ranges. +This will result in one or more buckets having more elements than other buckets, making bucket sort like any other sorting technique, where every element is compared to the other. Time complexity increases even further if the elements in the array are present in the reverse order. If insertion sort is used, the worst-case time complexity can go up to O(n^2^). + + +Bucket Sort Space Complexity +Space Complexity : O(n+k) +Space Complexity for bucket sort is O(n+k), where n = number of elements in the array, and k = number of buckets formed Space taken by each bucket is O(k), and inside each bucket, we have n elements scattered. Hence, the space complexity becomes O(n+k). + +*/ + +package util; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class BucketSort { + +static void bucketSort(int[] arr, int noOfBuckets) { + +boolean isNegativePresent = false; +int offset = Integer.MAX_VALUE; +for (int i : arr) { + if (i < offset) offset = i; + if (i < 0) isNegativePresent = true; +} + +int globalMax = Integer.MIN_VALUE; +int globalMin = Integer.MAX_VALUE; +for (int i = 0; i < arr.length; i++) { + arr[i] -= offset; + globalMin = Math.min(arr[i], globalMin); + globalMax = Math.max(arr[i], globalMax); +} + +int range = globalMax - globalMin; +int bucketRange = (int) Math.ceil((double) range / noOfBuckets); + +// Create bucket array +List[] buckets = new List[noOfBuckets]; + +// Associate a list with each index in the bucket array +for (int i = 0; i < noOfBuckets; i++) { + buckets[i] = new LinkedList<>(); +} + +// Assign numbers from array to the proper bucket +// by using hashing function +for (int num : arr) { + buckets[hash(num, bucketRange, noOfBuckets)].add(num); +} + +// sort buckets +for (List bucket : buckets) Collections.sort(bucket); + +int idx = 0; +// Merge buckets to get sorted array +for (List bucket : buckets) { + for (int num : bucket) { + arr[idx++] = num; + } +} + +if (isNegativePresent) { + for (int i = 0; i < arr.length; i++) { + arr[i] += offset; + } +} +} + +private static int hash(int num, int hashValue, int numberOfBuckets) { +int bucketNumber = num / hashValue; +if (bucketNumber == numberOfBuckets) +bucketNumber--; +return bucketNumber; +} +} \ No newline at end of file diff --git a/sorting/bucket_sort.js b/sorting/bucket_sort.js new file mode 100644 index 00000000..4c32f1e9 --- /dev/null +++ b/sorting/bucket_sort.js @@ -0,0 +1,38 @@ +Here's an implementation of bucket sort in JavaScript: + +function bucketSort(arr, numBuckets = 10) { + const n = arr.length; + const buckets = new Array(numBuckets); + + // Initialize the buckets + for (let i = 0; i < numBuckets; i++) { + buckets[i] = []; + } + + // Distribute the elements into the buckets + for (let i = 0; i < n; i++) { + const bucketIndex = Math.floor(arr[i] * numBuckets); + buckets[bucketIndex].push(arr[i]); + } + + // Sort the elements within each bucket + for (let i = 0; i < numBuckets; i++) { + buckets[i].sort((a, b) => a - b); + } + + // Concatenate the sorted buckets + let sortedArr = []; + for (let i = 0; i < numBuckets; i++) { + sortedArr = sortedArr.concat(buckets[i]); + } + + return sortedArr; +} + + +The bucketSort function takes an array arr of numbers and an optional parameter numBuckets that specifies the number of buckets to use (default is 10). +The algorithm works by distributing the elements of arr into numBuckets buckets based on their value. Each bucket contains elements within a range of values, +and since the buckets are sorted, the elements within each bucket are sorted as well. Finally, the sorted buckets are concatenated to obtain the final sorted +array. + +Note that the performance of bucket sort depends on the distribution of the elements in the input array. If the elements are evenly distributed, bucket sort can achieve a time complexity of O(n), which is faster than many other sorting algorithms. However, if the elements are concentrated in a small range of values, the buckets may become unbalanced and the performance may degrade. Therefore, bucket sort is often used as a sub-routine in other sorting algorithms, or when the distribution of the input is known to be relatively even. diff --git a/sorting/count_sort.cpp b/sorting/count_sort.cpp new file mode 100644 index 00000000..44351bff --- /dev/null +++ b/sorting/count_sort.cpp @@ -0,0 +1,101 @@ +/* + Count Sort +The Counting Sort algorithm is a non-comparative sorting algorithm that works by counting the occurrences of each distinct element in the input +list. It then uses this information to determine the correct position of each element in the sorted output. + +Here are the steps involved in the Counting Sort algorithm: + +1. Find the range: Determine the range of values in the input list. This range is necessary to create an array with the appropriate size to store the counts. + +2. Count the occurrences: Create a count array of size equal to the range determined in the previous step. Iterate through the input list and count +the occurrences of each element by incrementing the corresponding count in the count array. + +3. Calculate cumulative counts: Modify the count array such that each element represents the cumulative count of elements up to that index. This step +ensures that the count array contains the correct positions for each element in the sorted order. + +4. Generate the sorted output: Create an output array of the same size as the input list. Iterate through the input list and use the count array to +determine the correct position of each element in the output array. Place each element in its corresponding position and decrement the count +in the count array. + +5. Return the sorted list: The output array now contains the elements in sorted order. Return this sorted list as the result of the Counting Sort algorithm. + +Here's an example to illustrate the process: + +Sample Input: [4, 2, 9, 4, 6, 1] + +1. Find the range: The range of values in the input list is from 1 to 9. +2. Count the occurrences: Create the count array [0, 1, 1, 0, 2, 0, 1, 0, 0, 1], where the index represents the element and the value represents the count. +3. Calculate cumulative counts: Modify the count array to [0, 1, 2, 2, 4, 4, 5, 5, 5, 6]. Each element represents the cumulative count of elements up to that index. +4. Generate the sorted output: Create the output array [1, 2, 4, 4, 6, 9]. Iterate through the input list, use the count array to determine the correct +position of each element, place it in the output array, and decrement the count in the count array. +5. Return the sorted list: The sorted list is [1, 2, 4, 4, 6, 9]. + +Counting Sort is an efficient algorithm when the range of values in the input list is relatively small. It has a time complexity of O(n + k), where n is +the number of elements in the input list and k is the range of values. +*/ + +#include +using namespace std; + +int getMax(int a[], int n) +{ + int max = a[0]; + for (int i = 1; i < n; i++) + { + if (a[i] > max) + max = a[i]; + } + return max; // maximum element from the array +} + +void countSort(int a[], int n) // function to perform counting sort +{ + int output[n + 1]; + int max = getMax(a, n); + int count[max + 1]; // create count array with size [max+1] + + for (int i = 0; i <= max; ++i) + { + count[i] = 0; // Initialize count array with all zeros + } + + for (int i = 0; i < n; i++) // Store the count of each element + { + count[a[i]]++; + } + + for (int i = 1; i <= max; i++) + count[i] += count[i - 1]; // find cumulative frequency + + /* This loop will find the index of each element of the original array in count array, and + place the elements in output array*/ + for (int i = n - 1; i >= 0; i--) + { + output[count[a[i]] - 1] = a[i]; + count[a[i]]--; // decrease count for same numbers + } + + for (int i = 0; i < n; i++) + { + a[i] = output[i]; // store the sorted elements into main array + } +} + +void printArr(int a[], int n) /* function to print the array */ +{ + int i; + for (i = 0; i < n; i++) + cout << a[i] << " "; +} + +int main() +{ + int a[] = {31, 11, 42, 7, 30, 11}; + int n = sizeof(a) / sizeof(a[0]); + cout << "Before sorting array elements are - \n"; + printArr(a, n); + countSort(a, n); + cout << "\nAfter sorting array elements are - \n"; + printArr(a, n); + return 0; +} \ No newline at end of file diff --git a/sorting/count_sort.java b/sorting/count_sort.java new file mode 100644 index 00000000..92c5e709 --- /dev/null +++ b/sorting/count_sort.java @@ -0,0 +1,102 @@ +/* + Count Sort +The Counting Sort algorithm is a non-comparative sorting algorithm that works by counting the occurrences of each distinct element in the input +list. It then uses this information to determine the correct position of each element in the sorted output. + +Here are the steps involved in the Counting Sort algorithm: + +1. Find the range: Determine the range of values in the input list. This range is necessary to create an array with the appropriate size to store the counts. + +2. Count the occurrences: Create a count array of size equal to the range determined in the previous step. Iterate through the input list and count +the occurrences of each element by incrementing the corresponding count in the count array. + +3. Calculate cumulative counts: Modify the count array such that each element represents the cumulative count of elements up to that index. This step +ensures that the count array contains the correct positions for each element in the sorted order. + +4. Generate the sorted output: Create an output array of the same size as the input list. Iterate through the input list and use the count array to +determine the correct position of each element in the output array. Place each element in its corresponding position and decrement the count +in the count array. + +5. Return the sorted list: The output array now contains the elements in sorted order. Return this sorted list as the result of the Counting Sort algorithm. + +Here's an example to illustrate the process: + +Sample Input: [4, 2, 9, 4, 6, 1] + +1. Find the range: The range of values in the input list is from 1 to 9. +2. Count the occurrences: Create the count array [0, 1, 1, 0, 2, 0, 1, 0, 0, 1], where the index represents the element and the value represents the count. +3. Calculate cumulative counts: Modify the count array to [0, 1, 2, 2, 4, 4, 5, 5, 5, 6]. Each element represents the cumulative count of elements up to that index. +4. Generate the sorted output: Create the output array [1, 2, 4, 4, 6, 9]. Iterate through the input list, use the count array to determine the correct +position of each element, place it in the output array, and decrement the count in the count array. +5. Return the sorted list: The sorted list is [1, 2, 4, 4, 6, 9]. + +Counting Sort is an efficient algorithm when the range of values in the input list is relatively small. It has a time complexity of O(n + k), where n is +the number of elements in the input list and k is the range of values. +*/ + +class CountingSort { + +int getMax(int[] a, int n) { + int max = a[0]; + for(int i = 1; i max) + max = a[i]; + } + return max; //maximum element from the array +} + +void countSort(int[] a, int n) // function to perform counting sort +{ + int[] output = new int [n+1]; + int max = getMax(a, n); + //int max = 42; + int[] count = new int [max+1]; //create count array with size [max+1] + + for (int i = 0; i <= max; ++i) + { + count[i] = 0; // Initialize count array with all zeros + } + + for (int i = 0; i < n; i++) // Store the count of each element + { + count[a[i]]++; + } + + for(int i = 1; i<=max; i++) + count[i] += count[i-1]; //find cumulative frequency + + /* This loop will find the index of each element of the original array in + + count array, and + place the elements in output array*/ + for (int i = n - 1; i >= 0; i--) { + output[count[a[i]] - 1] = a[i]; + count[a[i]]--; // decrease count for same numbers + } + + for(int i = 0; i 0); + + // Create a count array to store count of individual + // characters and initialize count array as 0 + var count = Array.from({length: 256}, (_, i) => 0); + + + // store count of each character + for (var i = 0; i < n; ++i) + ++count[arr[i].charCodeAt(0)]; + // Change count[i] so that count[i] now contains actual + // position of this character in output array + for (var i = 1; i <= 255; ++i) + count[i] += count[i - 1]; + + // Build the output character array + // To make it stable we are operating in reverse order. + for (var i = n - 1; i >= 0; i--) { + output[count[arr[i].charCodeAt(0)] - 1] = arr[i]; + --count[arr[i].charCodeAt(0)]; + } + + // Copy the output array to arr, so that arr now + // contains sorted characters + for (var i = 0; i < n; ++i) + arr[i] = output[i]; + return arr; +} + +// Driver method + var arr = [ 'g', 'e', 'e', 'k', 's', 'f', 'o', + 'r', 'g', 'e', 'e', 'k', 's' ]; + + arr = sort(arr); + document.write("Sorted character array is "); + for (var i = 0; i < arr.length; ++i) + document.write(arr[i]); \ No newline at end of file diff --git a/sorting/count_sort.py b/sorting/count_sort.py new file mode 100644 index 00000000..3f0f2a36 --- /dev/null +++ b/sorting/count_sort.py @@ -0,0 +1,74 @@ +# Count Sort +# The Counting Sort algorithm is a non-comparative sorting algorithm that works by counting the occurrences of each distinct element in the input +# list. It then uses this information to determine the correct position of each element in the sorted output. + +# Here are the steps involved in the Counting Sort algorithm: + +# 1. Find the range: Determine the range of values in the input list. This range is necessary to create an array with the appropriate size to store the counts. + +# 2. Count the occurrences: Create a count array of size equal to the range determined in the previous step. Iterate through the input list and count +# the occurrences of each element by incrementing the corresponding count in the count array. + +# 3. Calculate cumulative counts: Modify the count array such that each element represents the cumulative count of elements up to that index. This step +# ensures that the count array contains the correct positions for each element in the sorted order. + +# 4. Generate the sorted output: Create an output array of the same size as the input list. Iterate through the input list and use the count array to +# determine the correct position of each element in the output array. Place each element in its corresponding position and decrement the count +# in the count array. + +# 5. Return the sorted list: The output array now contains the elements in sorted order. Return this sorted list as the result of the Counting Sort algorithm. + +# Here's an example to illustrate the process: + +# Sample Input: [4, 2, 9, 4, 6, 1] + +# 1. Find the range: The range of values in the input list is from 1 to 9. +# 2. Count the occurrences: Create the count array [0, 1, 1, 0, 2, 0, 1, 0, 0, 1], where the index represents the element and the value represents the count. +# 3. Calculate cumulative counts: Modify the count array to [0, 1, 2, 2, 4, 4, 5, 5, 5, 6]. Each element represents the cumulative count of elements up to that index. +# 4. Generate the sorted output: Create the output array [1, 2, 4, 4, 6, 9]. Iterate through the input list, use the count array to determine the correct +# position of each element, place it in the output array, and decrement the count in the count array. +# 5. Return the sorted list: The sorted list is [1, 2, 4, 4, 6, 9]. + +# Counting Sort is an efficient algorithm when the range of values in the input list is relatively small. It has a time complexity of O(n + k), where n is +# the number of elements in the input list and k is the range of values. + +# The main function that sort the given string arr[] inalphabetical order +def countSort(arr): + + # The output character array that will have sorted arr + output = [0 for i in range(len(arr))] + + # Create a count array to store count of individual + # characters and initialize count array as 0 + count = [0 for i in range(256)] + + # For storing the resulting answer since the + # string is immutable + ans = ["" for _ in arr] + + # Store count of each character + for i in arr: + count[ord(i)] += 1 + + # Change count[i] so that count[i] now contains actual + # position of this character in output array + for i in range(256): + count[i] += count[i-1] + + # Build the output character array + for i in range(len(arr)): + output[count[ord(arr[i])]-1] = arr[i] + count[ord(arr[i])] -= 1 + + # Copy the output array to arr, so that arr now + # contains sorted characters + for i in range(len(arr)): + ans[i] = output[i] + return ans + + +# Driver code +if __name__ == '__main__': + arr = "geeksforgeeks" + ans = countSort(arr) + print("Sorted character array is % s" % ("".join(ans))) \ No newline at end of file diff --git a/sorting/dnf.cpp b/sorting/dnf.cpp new file mode 100644 index 00000000..98f373f3 --- /dev/null +++ b/sorting/dnf.cpp @@ -0,0 +1,75 @@ +/* + +The Dutch National Flag algorithm is used to sort an array containing elements with values of 0, 1, and 2. The goal is to rearrange the elements in-place so that all the 0s are grouped at the beginning, followed by all the 1s, and finally all the 2s. + +The algorithm uses three pointers: low, mid, and high. The low pointer represents the boundary of the 0s section, the mid pointer scans the array, and the high pointer represents the boundary of the 2s section. + +The algorithm iterates through the array and performs the following operations: + + 1. If the element at the mid pointer is 0, it is swapped with the element at the low pointer, and both pointers are incremented. + 2. If the element at the mid pointer is 1, it is already in the correct section, so the mid pointer is simply incremented. + 3. If the element at the mid pointer is 2, it is swapped with the element at the high pointer, and the high pointer is decremented. + +The iteration continues until the mid pointer crosses the high pointer, indicating that all elements have been processed. + +After the algorithm finishes, the array will be sorted according to the Dutch National Flag problem requirements, with all 0s at the beginning, followed by 1s, and finally 2s. The sorting is done in-place, meaning it does not require any additional space. + +The time complexity of the Dutch National Flag algorithm is O(n), where n is the length of the array, as we only need to iterate through the array once. The space complexity is O(1) since no extra space is used apart from the input array. + +Consider an array: [1, 2, 0, 2, 1, 0]. + +The algorithm uses three pointers: low, mid, and high. Initially, low = 0, mid = 0, and high = 5. + + Iterate while mid <= high: + If the element at mid is 0, swap it with the element at low, increment both low and mid. + If the element at mid is 1, increment mid. + If the element at mid is 2, swap it with the element at high, decrement high. + +After applying the algorithm, the sorted array will be: [0, 0, 1, 1, 2, 2]. + +In this example, the algorithm moves all the 0s to the beginning, followed by the 1s, and finally the 2s, achieving the desired sorting according to the Dutch National Flag problem requirements. +*/ + +#include +#include + +std::vector DutchNationalFlag(std::vector& array) { + int low = 0; // Initialize the low pointer to the beginning of the array + int mid = 0; // Initialize the mid pointer to the beginning of the array + int high = array.size() - 1; // Initialize the high pointer to the end of the array + + while (mid <= high) { + switch (array[mid]) { + case 0: + // If the value at mid is 0, swap it with the value at low + std::swap(array[low], array[mid]); + low++; // Increment low to move forward + mid++; // Increment mid to move forward + break; + case 1: + // If the value at mid is 1, no swapping needed, just move mid forward + mid++; + break; + case 2: + // If the value at mid is 2, swap it with the value at high + std::swap(array[mid], array[high]); + high--; // Decrement high to move backward + break; + } + } + + return array; +} + +int main() { + std::vector array = {2, 0, 1, 1, 0, 2, 2, 1, 0}; + std::vector sortedArray = DutchNationalFlag(array); + + std::cout << "Sorted Array: "; + for (int64_t num : sortedArray) { + std::cout << num << " "; + } + std::cout << std::endl; + + return 0; +} diff --git a/sorting/dnf.go b/sorting/dnf.go new file mode 100644 index 00000000..151df7f7 --- /dev/null +++ b/sorting/dnf.go @@ -0,0 +1,74 @@ +/* + +The Dutch National Flag algorithm is used to sort an array containing elements with values of 0, 1, and 2. The goal is to rearrange the elements in-place so that all the 0s are grouped at the beginning, followed by all the 1s, and finally all the 2s. + +The algorithm uses three pointers: low, mid, and high. The low pointer represents the boundary of the 0s section, the mid pointer scans the array, and the high pointer represents the boundary of the 2s section. + +The algorithm iterates through the array and performs the following operations: + + 1. If the element at the mid pointer is 0, it is swapped with the element at the low pointer, and both pointers are incremented. + 2. If the element at the mid pointer is 1, it is already in the correct section, so the mid pointer is simply incremented. + 3. If the element at the mid pointer is 2, it is swapped with the element at the high pointer, and the high pointer is decremented. + +The iteration continues until the mid pointer crosses the high pointer, indicating that all elements have been processed. + +After the algorithm finishes, the array will be sorted according to the Dutch National Flag problem requirements, with all 0s at the beginning, followed by 1s, and finally 2s. The sorting is done in-place, meaning it does not require any additional space. + +The time complexity of the Dutch National Flag algorithm is O(n), where n is the length of the array, as we only need to iterate through the array once. The space complexity is O(1) since no extra space is used apart from the input array. + +Consider an array: [1, 2, 0, 2, 1, 0]. + +The algorithm uses three pointers: low, mid, and high. Initially, low = 0, mid = 0, and high = 5. + + Iterate while mid <= high: + If the element at mid is 0, swap it with the element at low, increment both low and mid. + If the element at mid is 1, increment mid. + If the element at mid is 2, swap it with the element at high, decrement high. + +After applying the algorithm, the sorted array will be: [0, 0, 1, 1, 2, 2]. + +In this example, the algorithm moves all the 0s to the beginning, followed by the 1s, and finally the 2s, achieving the desired sorting according to the Dutch National Flag problem requirements. +*/ +package main + +import "fmt" + +func DutchNationalFlag(array []int64) []int64 { + + // Initialize Low, Mid, and High pointers + var ( + Low = 0 + Mid = 0 + High = len(array) - 1 + ) + + // Iterate while Mid pointer is less than or equal to High pointer + for Mid <= High { + + // Check the value at Mid pointer + switch array[Mid] { + + // Case 0: Value is 0, so swap it with the value at Low pointer + // Increment both Low and Mid pointers to move forward + case 0: + array[Low], array[Mid] = array[Mid], array[Low] + Low++ + Mid++ + + // Case 1: Value is 1, no swapping needed + // Increment Mid pointer to move forward + case 1: + Mid++ + + // Case 2: Value is 2, so swap it with the value at High pointer + // Decrement High pointer to move backward + case 2: + array[Mid], array[High] = array[High], array[Mid] + High-- + } + } + + // Return the sorted array + return array +} + diff --git a/sorting/dnf.java b/sorting/dnf.java new file mode 100644 index 00000000..8601dddf --- /dev/null +++ b/sorting/dnf.java @@ -0,0 +1,74 @@ +package sorting; + +/* + *Explanation: +The code above solves the Dutch National Flag problem. It takes an array of integers and a pivot element as input, and sorts the array in the Dutch National Flag order. The Dutch National Flag problem involves partitioning the array into three sections based on the pivot element. + +The dutchNationalFlagSort method uses three pointers: low, mid, and high. The low pointer represents the boundary for elements less than the pivot, the mid pointer represents the boundary for elements equal to the pivot, and the high pointer represents the boundary for elements greater than the pivot. + +The method uses a while loop that continues until the mid pointer surpasses the high pointer. Inside the loop, it compares the element at the mid index with the pivot. If the element is less than the pivot, it swaps it with the element at the low index and increments both low and mid. If the element is greater than the pivot, it swaps it with the element at the high index and decrements high. If the element is equal to the pivot, it increments mid. + +The swap method is a helper function that swaps two elements in the array. + +In the main method, an example array and pivot value are provided. The dutchNationalFlagSort method is called with these values, and the sorted array is printed. + +Time Complexity: The time complexity of the Dutch National Flag algorithm is O(n), where n is the length of the input array. + +Space Complexity: The space complexity is O(1) as the algorithm sorts the array in-place without using any additional data structures. +**/ +import java.util.Arrays; + +public class DutchNationalFlag { + + /** + * Sorts an array of integers in the Dutch National Flag order. + * The Dutch National Flag problem partitions the array into three sections: + * - All elements less than the pivot are placed before it. + * - All elements greater than the pivot are placed after it. + * - All elements equal to the pivot are placed in the middle. + * + * @param array the array of integers to be sorted + * @param pivot the pivot element + */ + public static void main(String[] args) { + int[] array = {2, 2, 1, 1, 0, 0, 2, 1, 0}; + int pivot = 1; + + dutchNationalFlagSort(array, pivot); + + System.out.println("Sorted Array: " + Arrays.toString(array)); + } + + public static void dutchNationalFlagSort(int[] array, int pivot) { + int low = 0; // Pointer for elements less than the pivot + int mid = 0; // Pointer for elements equal to the pivot + int high = array.length - 1; // Pointer for elements greater than the pivot + + while (mid <= high) { + if (array[mid] < pivot) { // Current element is less than the pivot + swap(array, low, mid); // Swap current element with element at the low index + low++; // Increment low pointer + mid++; // Increment mid pointer + } else if (array[mid] > pivot) { // Current element is greater than the pivot + swap(array, mid, high); // Swap current element with element at the high index + high--; // Decrement high pointer + } else { // Current element is equal to the pivot + mid++; // Increment mid pointer + } + } + } + + /** + * Swaps two elements in an array. + * + * @param array the array + * @param i the index of the first element + * @param j the index of the second element + */ + private static void swap(int[] array, int i, int j) { + int temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } +} + diff --git a/sorting/dnf.js b/sorting/dnf.js new file mode 100644 index 00000000..e30836e3 --- /dev/null +++ b/sorting/dnf.js @@ -0,0 +1,57 @@ +/* + Dutch National Flag Problem + + Given an array containing only 0s, 1s, and 2s, sort the array in a single traversal. + + Sample Input: [0, 2, 1, 2, 0] + Sample Output: [0, 0, 1, 2, 2] + + Approach: + - We can use three pointers, low, mid, and high, to divide the array into three regions: + 1. 0s region: elements before the low pointer (excluding low) are 0s + 2. 1s region: elements between the low and mid pointers (excluding mid) are 1s + 3. 2s region: elements after the high pointer (excluding high) are 2s + - Initialize low and mid pointers to the start of the array (0 index) and high pointer to the end of the array (array.length - 1). + - Iterate while the mid pointer is less than or equal to the high pointer: + - If the current element at mid is 0, swap it with the element at low and increment both low and mid pointers. + - If the current element at mid is 1, it is already in the correct region, so we just increment the mid pointer. + - If the current element at mid is 2, swap it with the element at high and decrement the high pointer. + - Repeat the above steps until the mid pointer crosses the high pointer. + - At the end, the array will be sorted in place. + + Time Complexity: O(n), where n is the length of the array. + Space Complexity: O(1), no additional space is used. + + Further Reading: https://en.wikipedia.org/wiki/Dutch_national_flag_problem +*/ + +function dutchNationalFlagProblem(arr) { + let low = 0; + let mid = 0; + let high = arr.length - 1; + + while (mid <= high) { + if (arr[mid] === 0) { + // Swap current element at mid with element at low + [arr[mid], arr[low]] = [arr[low], arr[mid]]; + // Increment both low and mid pointers + low++; + mid++; + } else if (arr[mid] === 1) { + // Move to the next element in the 1s region + mid++; + } else { + // Swap current element at mid with element at high + [arr[mid], arr[high]] = [arr[high], arr[mid]]; + // Decrement the high pointer + high--; + } + } + + return arr; +} + +// Test the function +const input = [0, 2, 1, 2, 0]; +const output = dutchNationalFlagProblem(input); +console.log(output); diff --git a/sorting/dnf.py b/sorting/dnf.py new file mode 100644 index 00000000..b12c42d5 --- /dev/null +++ b/sorting/dnf.py @@ -0,0 +1,57 @@ +# Approach Explanation: +# The code implements the Dutch National Flag problem, which is a sorting problem where we need to sort an array consisting of 0s, 1s, and 2s in ascending order. The approach used is called the "Three Pointers" approach. + +# We initialize three pointers, low, mid, and high, which represent the boundaries of three sections in the array: + +# All elements before low are 0s (left section). +# All elements between low and mid are 1s (middle section). +# All elements after high are 2s (right section). +# We iterate through the array using the mid pointer: + +# If the element at mid is 0, we swap it with the element at low and increment both low and mid pointers. +# If the element at mid is 1, we increment the mid pointer. +# If the element at mid is 2, we swap it with the element at high and decrement the high pointer. +# By doing this, we move all the 0s to the left section, 1s to the middle section, and 2s to the right section, effectively sorting the array. + +# Time Complexity: O(n), where n is the length of the input array. +# Space Complexity: O(1) (constant space), as we are sorting the array in-place + +# Sample Input: +# arr = [2, 0, 1, 2, 1, 0] + +# Sample Output: +# Input Array: [2, 0, 1, 2, 1, 0] +# Output Array: [0, 0, 1, 1, 2, 2] + + +def dutch_national_flag_problem(arr): + # Initialize variables for the three pointers + low = 0 + mid = 0 + high = len(arr) - 1 + + # Loop through the array until mid and high pointers meet + while mid <= high: + if arr[mid] == 0: + # Swap the element at mid with the element at low + arr[mid], arr[low] = arr[low], arr[mid] + # Move the low and mid pointers to the right + low += 1 + mid += 1 + elif arr[mid] == 1: + # Increment the mid pointer + mid += 1 + else: + # Swap the element at mid with the element at high + arr[mid], arr[high] = arr[high], arr[mid] + # Move the high pointer to the left + high -= 1 + + return arr + + +# Test the function +arr = [2, 0, 1, 2, 1, 0] +result = dutch_national_flag_problem(arr) +print("Input Array:", arr) +print("Output Array:", result) \ No newline at end of file diff --git a/sorting/heap_sort.cpp b/sorting/heap_sort.cpp new file mode 100644 index 00000000..0070532e --- /dev/null +++ b/sorting/heap_sort.cpp @@ -0,0 +1,79 @@ +/* +Heap sort is a sorting technique based on comparison based on binary heap data. +Similar to sorting, it finds the largest number first and then puts the largest number last. + + +This sorting algorithm uses a tree structure called the stack, where the stack is a kind of binary tree. +A binary decision tree in which the value of the root of a tree is less than or equal to the value of one of its roots is called a min-heap. +A decision binary tree is called maximum heap when the value of the root of a tree is greater than or equal to the value of one of its trees. +In this post, we'll learn more about C++ Stack Sorting. + +Working of heap sort in C++ +To sort any list into a logical order following steps are followed:- + +Convert the list into a heap. +Now convert this heap into a max heap. +As the heap is converted to max heap largest element in the list is stored in the root of the heap, replace it with the last item of the heap. +Now delete this node and reduce the size of the heap by 1. +Follow these steps until the list is sorted. +*/ + +#include +using namespace std; +void heapify(int arr[], int n, int i){ + int largest = i; + int l = 2*i + 1; + int r = 2*i + 2; + + //If left child is larger than root + if (l < n && arr[l] > arr[largest]) + largest = l; + //If right child largest + if (r < n && arr[r] > arr[largest]) + largest = r; + //If root is nor largest + if (largest != i){ + swap(arr[i], arr[largest]); + //Recursively heapifying the sub-tree + heapify(arr, n, largest); + } +} + +void heapSort(int arr[], int n){ + for (int i = n / 2 - 1; i >= 0; i--) + heapify(arr, n, i); + //One by one extract an element from heap + for (int i=n-1; i>=0; i--){ + //Moving current root to end + swap(arr[0], arr[i]); + //Calling max heapify on the reduced heap + heapify(arr, i, 0); + } +} + //Function to print array +void display(int arr[], int n){ + for (int i = 0; i < n; i++){ + cout << arr[i] << "\t"; + } + cout << "\n"; +} +int main(){ + int arr[] = {1, 14, 3, 7, 0}; + int n = sizeof(arr)/sizeof(arr[0]); + cout << "Unsorted array \n"; + display(arr, n); + heapSort(arr, n); + cout << "Sorted array \n"; + display(arr, n); +} + +/*Time Complexcity +Best +O(nlog n) + +Average +O(nlog n) + +Worst +O(nlog n) +*/ diff --git a/sorting/heap_sort.java b/sorting/heap_sort.java new file mode 100644 index 00000000..9495f23f --- /dev/null +++ b/sorting/heap_sort.java @@ -0,0 +1,74 @@ +/*Since the tree satisfies Max-Heap property, then the largest item is stored at the root node. + +Swap: Remove the root element and put at the end of the array (nth position) Put the last item of the tree (heap) at the vacant place. + +Remove: Reduce the size of the heap by 1. + +Heapify: Heapify the root element again so that we have the highest element at root. + +The process is repeated until all the items of the list are sorted.*/ + +// Heap Sort in Java language + +public class heap_sort { + + public void sort(int arr[]) { + int n = arr.length; + + // Build max heap + for (int i = n / 2 - 1; i >= 0; i--) { + heapify(arr, n, i); + } + + // Heap sort + for (int i = n - 1; i >= 0; i--) { + int temp = arr[0]; + arr[0] = arr[i]; + arr[i] = temp; + + // Heapify root element + heapify(arr, i, 0); + } + } + + void heapify(int arr[], int n, int i) { + // Find largest among root, left child and right child + int largest = i; + int l = 2 * i + 1; + int r = 2 * i + 2; + + if (l < n && arr[l] > arr[largest]) + largest = l; + + if (r < n && arr[r] > arr[largest]) + largest = r; + + // Swap and continue heapifying if root is not largest + if (largest != i) { + int swap = arr[i]; + arr[i] = arr[largest]; + arr[largest] = swap; + + heapify(arr, n, largest); + } + } + + // Function to print an array + static void printArray(int arr[]) { + int n = arr.length; + for (int i = 0; i < n; ++i) + System.out.print(arr[i] + " "); + System.out.println(); + } + + // Driver code + public static void main(String args[]) { + int arr[] = { 1, 12, 9, 5, 6, 10 }; + + heap_sort hs = new heap_sort(); + hs.sort(arr); + + System.out.println("Sorted array is"); + printArray(arr); + } + } diff --git a/sorting/heap_sort.js b/sorting/heap_sort.js new file mode 100644 index 00000000..c89fa285 --- /dev/null +++ b/sorting/heap_sort.js @@ -0,0 +1,77 @@ +/** +Heap Sort is a sorting algorithm that works by building a binary heap out of the input array and then repeatedly extracting the maximum element and restoring the heap property. The time complexity of Heap Sort is O(n log n) in the worst, average, and best cases, making it an efficient algorithm for sorting large data sets. The space complexity of the implementation is O(1) because the algorithm sorts the input array in place and does not use any additional data structures other than a few variables used for index tracking and swapping. Therefore, the space used by the algorithm does not depend on the size of the input array. +*/ + +/** + * The heapSort function takes an array as input and returns a sorted array using the Heap Sort algorithm. The function first builds a max heap from the input array using the buildMaxHeap function. It then repeatedly extracts the maximum element from the heap and restores the heap property using the siftDown function + * @param {Array} array The array to be sorted. + * @returns {Array} The sorted array. + * time complexity is O(n log n) because it calls buildMaxHeap function once and then the siftDown function n times in the worst case, where n is the length of the input array. + */ + function heapSort(array) { + // Build a max heap from the input array + buildMaxHeap(array); + + // Extract the max element from the heap and restore the heap property + for (let i = array.length - 1; i > 0; i--) { + // Move the root element to the end of the array + swap(array, 0, i); + + // Restore the heap property by sifting down the root element + siftDown(array, 0, i); + } + + return array; + } + + /** + * The buildMaxHeap function takes an array as input and transforms it into a max heap by calling siftDown on each non-leaf node in the tree. + * @param {Array} array The array to be transformed into a max heap. + * time complexity is O(n) because it calls the siftDown function on each non-leaf node in the tree + */ + function buildMaxHeap(array) { + const startIndex = Math.floor((array.length - 1) / 2); + + for (let i = startIndex; i >= 0; i--) { + siftDown(array, i, array.length); + } + } + + /** + * The siftDown function takes an array, an index, and an endIndex as input. It sifts down the element at the given index in the heap until it reaches its correct position. + * @param {Array} array The array representing the heap. + * @param {number} index The index of the element to be sifted down. + * @param {number} endIndex The index at which to stop sifting down. + * time complexity is O(log n) because it repeatedly swaps the element at the given index with its child elements until it reaches its correct position in the heap. + */ + function siftDown(array, index, endIndex) { + let childIndex1 = index * 2 + 1; + while (childIndex1 < endIndex) { + const childIndex2 = index * 2 + 2 < endIndex ? index * 2 + 2 : null; + const swapIndex = + childIndex2 !== null && array[childIndex2] > array[childIndex1] + ? childIndex2 + : childIndex1; + + if (array[swapIndex] > array[index]) { + swap(array, index, swapIndex); + index = swapIndex; + childIndex1 = index * 2 + 1; + } else { + return; + } + } + } + + /** + * The swap function takes an array and two indices as input and swaps the elements at those indices. It is used in the heapSort function to move the root element of the heap to the end of the array during each iteration of the main loop. This function is also used within the siftDown function to swap the parent element with its child element when restoring the heap property. + * @param {Array} array The array containing the elements to be swapped. + * @param {number} i The index of the first element. + * @param {number} j The index of the second element. + */ + function swap(array, i, j) { + const temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + \ No newline at end of file diff --git a/sorting/heap_sort.py b/sorting/heap_sort.py new file mode 100644 index 00000000..3edf50b1 --- /dev/null +++ b/sorting/heap_sort.py @@ -0,0 +1,51 @@ + +''' + Heap sort is a sorting algorithm that works by transforming an unsorted list into a heap data structure, + which is a binary tree where each parent node is greater than or equal to its children if any. + then it repeatedly extracts the maximum element from the heap and puts it into its correct sorted position until the whole list is sorted. + + Sample_input : [5,16,8,14,20,1,26] + Sample_output : [1,5,8,14,16,20,26] + + Here's how the algorithm works: + + The heap_sort function takes an unsorted list arr as input. + It starts by building the initial heap by calling the heapify function on each parent node in the tree. + It does this by iterating over the parent nodes in reverse order starting from the last parent node, + and calling heapify on each of them. This builds a heap where each parent node is greater than or equal to its children. + It then repeatedly extracts the maximum element from the heap and puts it into its correct sorted position. + It does this by swapping the maximum element which is always at the root of the heap with the last element in the heap, + and then calling heapify on the root node to restore the heap property. + Finally, it returns the sorted list. + + ''' +def heap_sort(arr): + # Build the initial heap + n = len(arr) + for i in range(n // 2 - 1, -1, -1): + heapify(arr, n, i) + + # Extract the maximum element repeatedly + for i in range(n - 1, 0, -1): + arr[0], arr[i] = arr[i], arr[0] # Swap + heapify(arr, i, 0) + + return arr + +def heapify(arr, n, i): + largest = i + left = 2 * i + 1 + right = 2 * i + 2 + + # Check if left child is larger than root + if left < n and arr[left] > arr[largest]: + largest = left + + # Check if right child is larger than root + if right < n and arr[right] > arr[largest]: + largest = right + + # Swap if necessary and heapify the affected subtree + if largest != i: + arr[i], arr[largest] = arr[largest], arr[i] + heapify(arr, n, largest) diff --git a/sorting/insertion_sort.cpp b/sorting/insertion_sort.cpp new file mode 100644 index 00000000..83b6250e --- /dev/null +++ b/sorting/insertion_sort.cpp @@ -0,0 +1,73 @@ +/* + Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. + It starts with the second element of the array, compares it with the first element, and swaps them + if necessary. It then continues to the third element, compares it with the first and second elements, + and swaps it into the correct position. This process continues until the last element is compared and + sorted into its correct position in the sorted array. + + At each iteration, the algorithm assumes that the subarray to the left of the current element is already + sorted, and it searches for the correct position of the current element within that subarray by comparing + it to each element from right to left until it finds the correct position. Once it finds the correct + position, it shifts all the larger elements to the right to make space for the current element and + inserts it in its correct position. + + Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) + algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm + for small data sets or partially sorted data sets. + + In this implementation, we define a function called InsertionSort that takes an array of integers and sorts + it in ascending order using the Insertion Sort algorithm. + + The algorithm works by iterating over the array from the second element to the end. + + For each element, it compares it with the previous elements in the array and inserts it in the correct position. + + The current variable holds the value of the current element being compared. + + The j variable holds the index of the previous element being compared. + + The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. + + Once the correct position is found, the current value is inserted into the array. + + Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. + + Sample input: [0, 2, 1,-1, 10, 3, 4] + Output: [-1 0 1 2 3 4 10] +*/ + +#include +#include + +using namespace std; + + +// InsertionSort is a function that takes an array of integers and sorts it in +// ascending order using the Insertion Sort algorithm. +void insertionSort(vector& arr) { + int n = arr.size(); + for(int i=1; i=0 && arr[j]>key) { + arr[j+1] = arr[j]; + j--; + } + arr[j+1] = key; // insert the key at its sorted position + } +} + +int main() { + vector arr = {3, 5, 1, 4, 2}; + insertionSort(arr); + + cout << "Sorted Array: "; + for(int num: arr) { + cout << num << " "; + } + cout << endl; + return 0; +} diff --git a/sorting/insertion_sort.go b/sorting/insertion_sort.go index 7d6f24b2..bd64ae5a 100644 --- a/sorting/insertion_sort.go +++ b/sorting/insertion_sort.go @@ -1,40 +1,71 @@ /* -Implementation of insertion sort in go. -Insertion sort is a simple sorting algorith that iterates through -the list starting at the second element. We compare each element -to the preceding elements, slide over the preceding larger (or smaller) -elements, and insert the original element into the empty position. + Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. + It starts with the second element of the array, compares it with the first element, and swaps them + if necessary. It then continues to the third element, compares it with the first and second elements, + and swaps it into the correct position. This process continues until the last element is compared and + sorted into its correct position in the sorted array. -Time Complexity worst-case and average complexity O(n^{2}) + At each iteration, the algorithm assumes that the subarray to the left of the current element is already + sorted, and it searches for the correct position of the current element within that subarray by comparing + it to each element from right to left until it finds the correct position. Once it finds the correct + position, it shifts all the larger elements to the right to make space for the current element and + inserts it in its correct position. -Insertion sort is inefficient for large arrays. However it is fast for -small arrays and typically more efficient than bubble sort and selection -sort due to not making as many comparisons on average. + Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) + algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm + for small data sets or partially sorted data sets. + + In this implementation, we define a function called InsertionSort that takes an array of integers and sorts + it in ascending order using the Insertion Sort algorithm. -Source: https://en.wikipedia.org/wiki/Insertion_sort + The algorithm works by iterating over the array from the second element to the end. -Sample input: [0, 2, 1,-1, 10, 3, 4] -Output: [-1 0 1 2 3 4 10] + For each element, it compares it with the previous elements in the array and inserts it in the correct position. + + The current variable holds the value of the current element being compared. + + The j variable holds the index of the previous element being compared. + + The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. + + Once the correct position is found, the current value is inserted into the array. + + Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. + + Sample input: [0, 2, 1,-1, 10, 3, 4] + Output: [-1 0 1 2 3 4 10] */ package main import "fmt" -func main() { - a_lst := []int{0, 2, 1, -1, 10, 3, 4} - insertion_sort(a_lst) - fmt.Print(a_lst) +// InsertionSort is a function that takes an array of integers and sorts it in +// ascending order using the Insertion Sort algorithm. +func InsertionSort(arr []int) []int { + // Iterate over the array from the second element to the end + for i := 1; i < len(arr); i++ { + // Set the current value and the previous index + current := arr[i] + j := i - 1 + + // Compare the current value with the previous values in the array + for j >= 0 && arr[j] > current { + // Shift the values to the right to make space for the current value + arr[j+1] = arr[j] + j-- + } + + // Insert the current value in the correct position + arr[j+1] = current + } + + // Return the sorted array + return arr } -func insertion_sort(a_lst []int) { - for index := 1; index < len(a_lst); index++ { - val := a_lst[index] - position := index - 1 - for 0 <= position && val < a_lst[position] { - a_lst[position+1] = a_lst[position] - position-- - } - a_lst[position+1] = val - } +func main() { + arr := []int{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5} + sortedArr := InsertionSort(arr) + fmt.Println(sortedArr) } diff --git a/sorting/insertion_sort.java b/sorting/insertion_sort.java new file mode 100644 index 00000000..f0f5fd68 --- /dev/null +++ b/sorting/insertion_sort.java @@ -0,0 +1,63 @@ +/* + Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. + It starts with the second element of the array, compares it with the first element, and swaps them + if necessary. It then continues to the third element, compares it with the first and second elements, + and swaps it into the correct position. This process continues until the last element is compared and + sorted into its correct position in the sorted array. + + At each iteration, the algorithm assumes that the subarray to the left of the current element is already + sorted, and it searches for the correct position of the current element within that subarray by comparing + it to each element from right to left until it finds the correct position. Once it finds the correct + position, it shifts all the larger elements to the right to make space for the current element and + inserts it in its correct position. + + Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) + algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm + for small data sets or partially sorted data sets. + + In this implementation, we define a function called InsertionSort that takes an array of integers and sorts + it in ascending order using the Insertion Sort algorithm. + + The algorithm works by iterating over the array from the second element to the end. + + For each element, it compares it with the previous elements in the array and inserts it in the correct position. + + The current variable holds the value of the current element being compared. + + The j variable holds the index of the previous element being compared. + + The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. + + Once the correct position is found, the current value is inserted into the array. + + Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. + + Sample input: [0, 2, 1,-1, 10, 3, 4] + Output: [-1 0 1 2 3 4 10] +*/ + +// InsertionSort is a function that takes an array of integers and sorts it in +// ascending order using the Insertion Sort algorithm. +public class InsertionSort { + public static void insertionSort(int[] arr) { + int n = arr.length; + for (int i = 1; i < n; ++i) { + int key = arr[i]; + int j = i - 1; + + /* Move elements of arr[0..i-1], that are greater than key, to one + position ahead of their current position */ + while (j >= 0 && arr[j] > key) { + arr[j + 1] = arr[j]; + j = j - 1; + } + arr[j + 1] = key; + } + } + + public static void main(String[] args) { + int[] arr = { 12, 11, 13, 5, 6 }; + insertionSort(arr); + System.out.println(Arrays.toString(arr)); + } +} diff --git a/sorting/insertion_sort.js b/sorting/insertion_sort.js new file mode 100644 index 00000000..89e5128c --- /dev/null +++ b/sorting/insertion_sort.js @@ -0,0 +1,62 @@ +/* + Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. + It starts with the second element of the array, compares it with the first element, and swaps them + if necessary. It then continues to the third element, compares it with the first and second elements, + and swaps it into the correct position. This process continues until the last element is compared and + sorted into its correct position in the sorted array. + + At each iteration, the algorithm assumes that the subarray to the left of the current element is already + sorted, and it searches for the correct position of the current element within that subarray by comparing + it to each element from right to left until it finds the correct position. Once it finds the correct + position, it shifts all the larger elements to the right to make space for the current element and + inserts it in its correct position. + + Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) + algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm + for small data sets or partially sorted data sets. + + In this implementation, we define a function called InsertionSort that takes an array of integers and sorts + it in ascending order using the Insertion Sort algorithm. + + The algorithm works by iterating over the array from the second element to the end. + + For each element, it compares it with the previous elements in the array and inserts it in the correct position. + + The current variable holds the value of the current element being compared. + + The j variable holds the index of the previous element being compared. + + The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. + + Once the correct position is found, the current value is inserted into the array. + + Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. + + Sample input: [0, 2, 1,-1, 10, 3, 4] + Output: [-1 0 1 2 3 4 10] +*/ + +/** + * Perform insertion sort on an array of integers in non-decreasing order. + * @param {number[]} arr - The input array to sort. + * @returns {number[]} The sorted array in non-decreasing order. + */ +function insertionSort(arr) { + // Loop through each element of the array, starting with the second. + for (let i = 1; i < arr.length; i++) { + // Save the current element to be inserted later. + let current = arr[i]; + // Loop through the sorted portion of the array backwards. + for (let j = i - 1; j >= 0 && arr[j] > current; j--) { + // Shift each element that is greater than the current element up one position. + arr[j + 1] = arr[j]; + } + // Insert the current element in its proper place. + arr[j + 1] = current; + } + // Return the sorted array. + return arr; +} + +const inputArr = [4, 5, 67, 56, 3, 35, 45]; +console.log(insertionSort(inputArr)); diff --git a/sorting/insertion_sort.py b/sorting/insertion_sort.py index 05ea303c..c4b61a02 100644 --- a/sorting/insertion_sort.py +++ b/sorting/insertion_sort.py @@ -1,29 +1,61 @@ """ -Implementation of insertion sort in python. + Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. + It starts with the second element of the array, compares it with the first element, and swaps them + if necessary. It then continues to the third element, compares it with the first and second elements, + and swaps it into the correct position. This process continues until the last element is compared and + sorted into its correct position in the sorted array. -Time Complexity worst-case and average complexity O(n^{2}) + At each iteration, the algorithm assumes that the subarray to the left of the current element is already + sorted, and it searches for the correct position of the current element within that subarray by comparing + it to each element from right to left until it finds the correct position. Once it finds the correct + position, it shifts all the larger elements to the right to make space for the current element and + inserts it in its correct position. -Source: https://en.wikipedia.org/wiki/Insertion_sort + Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) + algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm + for small data sets or partially sorted data sets. + + In this implementation, we define a function called InsertionSort that takes an array of integers and sorts + it in ascending order using the Insertion Sort algorithm. -Sample input: [0, 2, 1,-1, 10, 3, 4] -Output: [-1 0 1 2 3 4 10] + The algorithm works by iterating over the array from the second element to the end. + + For each element, it compares it with the previous elements in the array and inserts it in the correct position. + + The current variable holds the value of the current element being compared. + + The j variable holds the index of the previous element being compared. + + The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. + + Once the correct position is found, the current value is inserted into the array. + + Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. + + Sample input: [0, 2, 1,-1, 10, 3, 4] + Output: [-1 0 1 2 3 4 10] """ -def main(): - a_lst = [0, 2, 1,-1, 10, 3, 4] - insertion_sort(a_lst) - print(a_lst) - -# Typing can be changed if needed as python supports comparison -# of types other than int (float, strings, etc.) -def insertion_sort(a_lst: list[int]) -> None: - for idx in range(1, len(a_lst)): - value = a_lst[idx] - position = idx - 1 - while 0 <= position and value < a_lst[position]: - a_lst[position + 1] = a_lst[position] - position -= 1 - a_lst[position + 1] = value - -if __name__ == "__main__": - main() +def insertion_sort(arr): + """ + Sorts an array in ascending order using the insertion sort algorithm. + + @param arr: list of integers to be sorted + @return: sorted list of integers + """ + # iterate through every element of the array + for i in range(1, len(arr)): + # store the current element and its index + current = arr[i] + j = i - 1 + + # move all elements greater than the current element to the right + while j >= 0 and arr[j] > current: + arr[j + 1] = arr[j] + j -= 1 + + # insert the current element in its correct position + arr[j + 1] = current + + # return the sorted array + return arr diff --git a/sorting/insertionsort.cpp b/sorting/insertionsort.cpp deleted file mode 100644 index 3dfc5ce2..00000000 --- a/sorting/insertionsort.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -using namespace std; - -void InsertionSort(int A[], int n){ - int flag = 0; - for(int i = 1; i < n; i++){ - int hole = i; - int value = A[i]; - while(hole > 0 && A[hole-1] > value){ - A[hole] = A[hole-1]; - hole = hole - 1; - } - A[hole] = value; - } -} -int main(){ - int A[] = {5,9,77,1,2,3,4,5}; - InsertionSort(A,6); - for(int i = 0; i < 6; i++){ - cout << A[i] << " "; - } - -return 0; -} diff --git a/sorting/insertionsort.js b/sorting/insertionsort.js deleted file mode 100644 index 12dedd54..00000000 --- a/sorting/insertionsort.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -Insertion sort is a sorting algorithm that places an unsorted element -at its suitable place in each iteration. -*/ -const inputArr = [4,5,67,56,3,35,45]; - -const insertionSort= (arr) => { - - //loop to access each array element - for (let i = 1; i < arr.length; i++) { - let currentValue = arr[i]; - const j = 0; - - // loop to Compare key with each element on the left of it until an element smaller than is found - for (let j = i - 1; j >= 0 && arr[j] > currentValue; j--) { - arr[j + 1] = arr[j]; - } - // Place key at after the element just smaller than it. - arr[j + 1] = currentValue; - } - return arr; - } - console.log(insertionSort(inputArr)); - \ No newline at end of file diff --git a/sorting/merge_sort.cpp b/sorting/merge_sort.cpp new file mode 100644 index 00000000..23f733e3 --- /dev/null +++ b/sorting/merge_sort.cpp @@ -0,0 +1,68 @@ +// Merge Sort +/* + Here's how the merge sort algorithm works: + + 1. It divides the input array into two halves, recursively sorts them, and then merges the sorted halves. + 2. To merge two sorted sub-arrays, we need to create a temporary array and then compare the elements of the two sub-arrays, + one by one, and add the smaller element to the temporary array. + 3. After we have exhausted one of the sub-arrays, we simply copy the remaining elements of the other sub-array to the temporary array. + 4. Finally, we copy the elements of the temporary array back to the original array. + + The time complexity of merge sort is O(n log n), where n is the number of elements in the array. + The space complexity is O(n), because we create a temporary array of size n during the merging process. +*/ +#include +#include + +using namespace std; + +void merge(vector& arr, int start, int mid, int end) { + vector temp(end - start + 1); + int i = start, j = mid + 1, k = 0; + + while (i <= mid && j <= end) { + if (arr[i] <= arr[j]) { + temp[k] = arr[i]; + ++i; + } else { + temp[k] = arr[j]; + ++j; + } + ++k; + } + + while (i <= mid) { + temp[k] = arr[i]; + ++i; + ++k; + } + + while (j <= end) { + temp[k] = arr[j]; + ++j; + ++k; + } + + for (int l = start; l <= end; ++l) { + arr[l] = temp[l - start]; + } +} + +void merge_sort(vector& arr, int start, int end) { + if (start < end) { + int mid = (start + end) / 2; + merge_sort(arr, start, mid); + merge_sort(arr, mid + 1, end); + merge(arr, start, mid, end); + } +} + +int main() { + vector arr = {5, 2, 8, 1, 9, 4}; + merge_sort(arr, 0, arr.size() - 1); + for (int num : arr) { + cout << num << " "; + } + cout << endl; + return 0; +} \ No newline at end of file diff --git a/sorting/merge_sort.go b/sorting/merge_sort.go new file mode 100644 index 00000000..c70dc001 --- /dev/null +++ b/sorting/merge_sort.go @@ -0,0 +1,93 @@ +// Merge Sort +/* + The MergeSort function is the main function that takes an integer array as input and sorts it using the Merge Sort algorithm. + + In the MergeSort function, if the length of the input array is only one element, it is already sorted, so we return it as is. + Otherwise, we find the middle point of the array and split it into two halves. We then recursively call the MergeSort + function on the left half and the right half of the array. Finally, we merge the two sorted halves using the merge function. + + The merge function takes two sorted arrays as input and merges them into one sorted array. We initialize a new array to hold + the merged result and three index variables for the left, right, and result arrays. We then iterate over the left and right + arrays and compare their elements. We add the smaller element to the result array and move the corresponding index variable. + Finally, we append the remaining elements of the left or right array to the result array and return it. + + The time complexity of Merge Sort is O(n*log n), where n is the number of elements in the array, + and the space complexity is O(n) due to the use of the temporary arrays during the merging phase. + +*/ +package main + +import "fmt" + +// MergeSort is the main function that takes an integer array as input +// and sorts it using the Merge Sort algorithm. +func MergeSort(arr []int) []int { + // If the array has only one element, return it. + if len(arr) == 1 { + return arr + } + + // Find the middle point to divide the array into two halves. + mid := len(arr) / 2 + + // Split the array into two halves. + left := arr[:mid] + right := arr[mid:] + + // Recursively sort the left half of the array. + left = MergeSort(left) + + // Recursively sort the right half of the array. + right = MergeSort(right) + + // Merge the two sorted halves. + return merge(left, right) +} + +// Merge is a helper function that takes two sorted arrays and merges them into one sorted array. +func merge(left, right []int) []int { + // Initialize a new array to hold the merged result. + result := make([]int, len(left)+len(right)) + + // Initialize the index variables for the left, right, and result arrays. + i := 0 // Index for left array + j := 0 // Index for right array + k := 0 // Index for result array + + // Iterate over the left and right arrays and compare their elements. + // Add the smaller element to the result array and move the corresponding index variable. + for i < len(left) && j < len(right) { + if left[i] < right[j] { + result[k] = left[i] + i++ + } else { + result[k] = right[j] + j++ + } + k++ + } + + // Append the remaining elements of the left or right array to the result array. + for i < len(left) { + result[k] = left[i] + i++ + k++ + } + + for j < len(right) { + result[k] = right[j] + j++ + k++ + } + + // Return the merged and sorted result array. + return result +} + +func main() { + // Example usage + arr := []int{3, 2, 1, 5, 4} + fmt.Println("Unsorted array:", arr) + arr = MergeSort(arr) + fmt.Println("Sorted array:", arr) +} \ No newline at end of file diff --git a/sorting/mergesort.go b/sorting/merge_sort.java similarity index 52% rename from sorting/mergesort.go rename to sorting/merge_sort.java index 44d4e577..71c34e40 100644 --- a/sorting/mergesort.go +++ b/sorting/merge_sort.java @@ -9,41 +9,61 @@ // Repeatedly merge sublists to produce new sorted sublists until there is only one sublist remaining. This will be the sorted list // Source(https://en.wikipedia.org/wiki/Merge_sort) -package main - // Approach: Divide by finding the number mid of the position midway between left and right. Do this step the same // way we found the midpoint in binary search // Conquer by recursively sorting the subarrays in each of the two subproblems created by the divide step. // That is, recursively sort the subarray Arr[left. . mid] and recursively sort the subarray Arr[mid + 1. . right]. // Combine by merging the two sorted subarrays back into the single sorted subarray Arr[left. . right]. -func MergeSort(Arr []int) []int { - if len(Arr) <= 1 { - return Arr - } - middle := len(Arr) / 2 - left := MergeSort(Arr[:middle]) - right := MergeSort(Arr[middle:]) - return Merge(left, right) -} -func Merge(left, right []int) []int { - result := make([]int, len(left) + len(right)) - for i:=0; len(left) > 0 || len(right) > - 0; i++ { - if len(left) > 0 && len(right) > 0 { - if left[0] < right[0] { - result[i] = left[0] - left = left[1:] - } else { - result[i] = right[0] - right = right[1:] - } - } else if len(left) > 0 { - result[i] = left[0] - left = left[1:] - } else if len(right) > 0 { - result[i] = right[0] - right = right[1:] - } - } - return result -} \ No newline at end of file +class MergeSort { + + void mergeSort(int ar[], int p,int r){ + if(p None: + if start < n: + mid: int = (start + n) // 2 + merge_sort(a_lst, start, mid) + merge_sort(a_lst, mid + 1, n) + merge(a_lst, start, mid, n) + + +def merge(a_lst: list[int], start: int, mid: int, n: int): + n_left: int = mid - start + 1 # length of left array + n_right: int = n - mid # length of right array + + left: list[int] = [0] * n_left # Initialize left and right arrays + right: list[int] = [0] * n_right + + for idx in range(n_left): # Fill left and right arrays + left[idx] = a_lst[start + idx] + for idx in range(n_right): + right[idx] = a_lst[mid + idx + 1] + + # Fill the orignal array with the smallest element from either + # left or right until left or right is empty + l_idx: int = 0 + r_idx: int = 0 + idx: int = start + while l_idx < len(left) and r_idx < len(right): + if left[l_idx] < right[r_idx]: + a_lst[idx] = left[l_idx] + l_idx += 1 + else: + a_lst[idx] = right[r_idx] + r_idx += 1 + idx += 1 + + # Fill the original array with the rest of the elements + # from the half array that is nonempty + + while l_idx < len(left): + a_lst[idx] = left[l_idx] + l_idx += 1 + idx += 1 + while r_idx < len(right): + a_lst[idx] = right[r_idx] + r_idx += 1 + idx += 1 + +if __name__ == "__main__": + main() diff --git a/sorting/mergesort.cpp b/sorting/mergesort.cpp deleted file mode 100644 index 617d33eb..00000000 --- a/sorting/mergesort.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// In computer science, merge sort (also commonly spelled as mergesort) is an efficient, general-purpose, -// and comparison-based sorting algorithm. Most implementations produce a stable sort, -// which means that the order of equal elements is the same in the input and output. -// Merge sort is a divide-and-conquer algorithm that was invented by John von Neumann in 1945. -// A detailed description and analysis of bottom-up merge sort appeared in a report by Goldstine and von Neumann as early as 1948. -// Conceptually, a merge sort works as follows: - -// Divide the unsorted list into n sublists, each containing one element (a list of one element is considered sorted). -// Repeatedly merge sublists to produce new sorted sublists until there is only one sublist remaining. This will be the sorted list -// Source(https://en.wikipedia.org/wiki/Merge_sort) -#include -using namespace std; -void Merge(int *A, int *L, int leftCount, int *R, int rightCount){ - int i, j, k; - i = 0; j = 0; k = 0; - while(i < leftCount && j < rightCount){ - if(L[i] < R[j]) - A[k++] = L[i++]; - else - A[k++] = R[j++]; - } - while(i < leftCount) A[k++] = L[i++]; - while(j < rightCount) A[k++] = R[j++]; -} - -void MergeSort(int *A, int n){ - int mid, *L, *R; - if(n < 2) return; // base condition if array has less then 2 elements do nothing - mid = n / 2; - L = (int*)malloc(mid * sizeof(int)); - R = (int*)malloc((n - mid) * sizeof(int)); - for(int i = 0; i < mid; i++) L[i] = A[i]; - for(int i = mid; i < n; i++) R[i - mid] = A[i]; - MergeSort(L, mid); - MergeSort(R, n - mid); - Merge(A, L, mid, R, n - mid); - free(L); - free(R); -} -int main(){ - int A[] = {2,1,4,3,5,99,32,47,106,1,4,6,8,7,0,9}; - MergeSort(A,16); - for(int i = 0; i < 16; i++) - cout << A[i] << " "; - return 0; -} diff --git a/sorting/mergesort.js b/sorting/mergesort.js deleted file mode 100644 index c768e95d..00000000 --- a/sorting/mergesort.js +++ /dev/null @@ -1,59 +0,0 @@ -/* -Merge sort is a sorting algorithm that takes a list and divides the list into two smaller lists recursively until we are down to the pairs of arrays containing individual elements. - -At that point, the pairs are compared and are *merged* back together to form a sorted list, recursively popping off the stack until we're back to our final sorted array. -*/ - -const inputArr = [4, 5, 67, 56, 3, 35, 45]; - -function mergeSort(arr) { - - if (arr.length > 1) { - - // splitting the list into two smaller lists - const mid = Math.floor(arr.length / 2); - const left = arr.slice(0, mid); - const right = arr.slice(mid); - - // recurses over the two smaller lists - mergeSort(left); - mergeSort(right); - - // define counters for our left, right and arr arrays - let i = 0; - let j = 0; - let k = 0; - - // compares elements in left and right arrays - // and places them back in arr from least to greatest - while (i < left.length && j < right.length) { - if (left[i] <= right[j]) { - arr[k] = left[i]; - i = i + 1; - } else { - arr[k] = right[j]; - j = j + 1; - } - k = k + 1; - } - - // if there are any elements remaining in our left array, put them in our arr array - while (i < left.length) { - arr[k] = left[i]; - i = i + 1; - k = k + 1; - } - - // if there are any elements remaining in our right array, put them in our arr array - while (j < right.length) { - arr[k] = right[j]; - j = j + 1; - k = k + 1; - } - } - - return arr; -} - -mergeSort(inputArr); -console.log(inputArr); \ No newline at end of file diff --git a/sorting/quick_sort.cpp b/sorting/quick_sort.cpp new file mode 100644 index 00000000..9ee64530 --- /dev/null +++ b/sorting/quick_sort.cpp @@ -0,0 +1,53 @@ +#include +using namespace std; + +int Hoare_partition(int arr[],int l,int h) +{ + // Here p is the Index of pivot element + // Here We consider first element as pivot, + // But if we Want to consider any element as pivot then just swap that index with the first element + int pivot=arr[l]; + int i=l-1,j=h+1; + + while(true) + { + do{ + i++; + }while(arr[i]pivot); + + if(i>=j) + return j; + swap(arr[i],arr[j]); + } +} + +void Quick_Sort(int arr[],int low,int high) +{ + + if(low pivot { + j-- + } + // Swap elements at i and j if they are in the wrong subarray + if i <= j { + arr[i], arr[j] = arr[j], arr[i] + i++ + j-- + } + } + + // Recursively sort left and right subarrays + if left < j { + quicksort(arr, left, j) + } + if i < right { + quicksort(arr, i, right) + } +} + +func main() { + // Example usage + arr := []int{3, 7, 1, 8, 4, 2, 9, 5, 6} + quicksort(arr, 0, len(arr)-1) + fmt.Println(arr) // Output: [1 2 3 4 5 6 7 8 9] +} diff --git a/sorting/quick_sort.java b/sorting/quick_sort.java new file mode 100644 index 00000000..dade98c8 --- /dev/null +++ b/sorting/quick_sort.java @@ -0,0 +1,69 @@ +/* +Quick sort: +Quicksort picks an element as pivot, and then it partitions the given array around the picked pivot element. +In quick sort, a large array is divided into two arrays in which one holds values that are smaller than the +specified value (Pivot), and another array holds the values that are greater than the pivot. + +After that, left and right sub-arrays are also partitioned using the same approach. +It will continue until the single element remains in the sub-array. + +For more information: +--> https://www.geeksforgeeks.org/java-program-for-quicksort/ +--> https://www.javatpoint.com/quick-sort + */ + +package sorting; + +public class quick_sort { + public static void main(String[] args) { + int[] a = {15,4,3,7,13,2,6,7,3,2,1,5,6,3,2}; + int n = a.length; + + sort(a, 0, n-1); + + System.out.println("sorted array"); + for (int j : a) System.out.print(j + " "); + System.out.println(); + } + + /** + * + * @param a to be sorted + * @param start starting index + * @param end ending index + */ + private static void sort(int[] a, int start, int end){ + if (start < end) { + int p = partition(a, start, end); + sort(a, start, p-1); + sort(a, p+1, end); + } + } + + /** + * smaller elements to the left of the pivot, greater elements to the right + * @param a to be sorted + * @param start starting index + * @param end ending index + * @return index + */ + private static int partition(int[] a, int start, int end) { + int pivot = a[end]; + int i = (start-1); // index of smaller element + for (int j=start; j +using namespace std; + +// Function to get maximum value in arr[] +int getMax(int arr[], int n) +{ + int mx = arr[0]; + for (int i = 1; i < n; i++) + if (arr[i] > mx) + mx = arr[i]; + return mx; +} + +// A function to do counting sort of arr[] according to +// the digit represented by exp. +void countSort(int arr[], int n, int exp) +{ + int output[n]; // output array + int i, count[10] = { 0 }; + + // Store count of occurrences in count[] + for (i = 0; i < n; i++) + count[(arr[i] / exp) % 10]++; + + // Change count[i] so that count[i] now contains actual + // position of this digit in output[] + for (i = 1; i < 10; i++) + count[i] += count[i - 1]; + + // Build the output array + for (i = n - 1; i >= 0; i--) { + output[count[(arr[i] / exp) % 10] - 1] = arr[i]; + count[(arr[i] / exp) % 10]--; + } + + // Copy the output array to arr[], so that arr[] now + // contains sorted numbers according to current digit + for (i = 0; i < n; i++) + arr[i] = output[i]; +} + +// The main function to that sorts arr[] of size n using +// Radix Sort +void radixsort(int arr[], int n) +{ + // Find the maximum number to know number of digits + int m = getMax(arr, n); + + // Do counting sort for every digit. Note that instead + // of passing digit number, exp is passed. exp is 10^i + // where i is current digit number + for (int exp = 1; m / exp > 0; exp *= 10) + countSort(arr, n, exp); +} + +// Function to print an array +void print(int arr[], int n) +{ + for (int i = 0; i < n; i++) + cout << arr[i] << " "; +} + + +int main() +{ + int arr[] = { 170, 45, 75, 90, 802, 24, 2, 66 }; + int n = sizeof(arr) / sizeof(arr[0]); + + // Function Call + radixsort(arr, n); + print(arr, n); + return 0; +} + +// Time Compexity -> O((n+k)*d) +// Space Complexity -> O(n+k) \ No newline at end of file diff --git a/sorting/radix_sort.go b/sorting/radix_sort.go new file mode 100644 index 00000000..dc436f0b --- /dev/null +++ b/sorting/radix_sort.go @@ -0,0 +1,99 @@ +/* +Radix sort is a sorting Algorithm that sorts elements by grouping them based on their individual digits or by the position of their digits. +It is a linear time sorting Algorithm that has a time complexity of O(nk). + +Using the Least Significant Bit +In this method, we will write a go language program to implement the radix sort by using the least significant bit. LSD sorts the elements +from right to left by comparing their individual digits. + +Algorithm +Step 1 − First, we need to import the fmt package. Then create a function named radixSortMSD() to sort the array. + +Step 2 − Inside the function create a new array. Use the for loop to iterate over the array and store the maximum element of the array in a variable. + +Step 3 − Now, initialize a count array to keep track of the number of elements that have a particular digit. + +Step 4 − Traverse the array and increment the count for the corresponding digit for each element. + +Step 5 − Modify the count array to store the actual position of each element in the Output array. Copy the elements from the input array to the +Output array in the order specified by the count array. + +Step 6 − Update the input array to the sorted Output array and return the sorted array. + +Step 7 − Start the main() function. Inside the main() initialize an array and store value to it. further, call the radixSort() function and pass +the array as an argument to the function. + +Step 8 − Store the result obtained by the function in a variable and print It on the screen. + +The code implements Radix Sort using the LSD (Least Significant Digit) approach to sort an array of integers. It iterates through each digit from +the least significant to the most significant, performing counting sort for each digit. Counting sort is used to determine the correct positions +of elements based on the current digit. The sorted elements are then copied back to the original array. This process is repeated until all digits +have been processed, resulting in a sorted array. +*/ + +package main + +import "fmt" + +// radixSortLSD performs Radix Sort using the LSD (Least Significant Digit) algorithm +func radixSortLSD(arr []int) []int { + // Find the maximum element in the array + max := arr[0] + for i := 1; i < len(arr); i++ { + if arr[i] > max { + max = arr[i] + } + } + + exp := 1 + // Perform counting sort for each digit from LSD to MSD + for max/exp > 0 { + // Create a count array to store the frequency of each digit (0-9) + count := make([]int, 10) + + // Count the frequency of each digit at the current exponent + for i := 0; i < len(arr); i++ { + count[(arr[i]/exp)%10]++ + } + + // Calculate the cumulative sum of count array to determine the correct positions of each digit + for i := 1; i < 10; i++ { + count[i] += count[i-1] + } + + // Create an output array to store the sorted elements based on the current digit + output := make([]int, len(arr)) + + // Build the output array by placing each element in its correct position based on the current digit + for i := len(arr) - 1; i >= 0; i-- { + output[count[(arr[i]/exp)%10]-1] = arr[i] + count[(arr[i]/exp)%10]-- + } + + // Copy the sorted elements from the output array back to the original array + for i := 0; i < len(arr); i++ { + arr[i] = output[i] + } + + // Move to the next digit by multiplying the exponent by 10 + exp *= 10 + } + + // Return the sorted array + return arr +} + +func main() { + // Test the radixSortLSD function + arr := []int{15, 31, 42, 20, 9} + fmt.Println("The unsorted array is:", arr) + result := radixSortLSD(arr) + fmt.Println("The sorted array is:", result) +} + +// Output +// The unsorted array is: [15 31 42 20 9] +// The sorted array is: [9 15 20 31 42] + +// Time Compexity -> O((n+k)*d) +// Space Complexity -> O(n+k) \ No newline at end of file diff --git a/sorting/radix_sort.java b/sorting/radix_sort.java new file mode 100644 index 00000000..925a3978 --- /dev/null +++ b/sorting/radix_sort.java @@ -0,0 +1,137 @@ +/* + Radix Sort +1. Identify the maximum number: Find the maximum number in the given list. This is necessary to determine the number of digits we need to consider +during the sorting process. + +2. Perform counting sort for each digit position: Starting from the least significant digit (rightmost digit), perform the following steps for each +digit position, moving towards the most significant digit (leftmost digit): +a. Create a count array: Create a count array of size 10 (to represent digits 0-9) and initialize all elements to 0. This count array will be used +to store the frequency of each digit at the current position. +b. Count the frequencies: Iterate through the list of numbers and count the frequency of each digit at the current position. For example, if the +current digit position is the units place, count the frequency of each digit from 0 to 9. +c. Update the count array: Modify the count array such that each element represents the cumulative count of digits up to that index. This step +ensures that the count array contains the correct positions for each digit in the sorted order. +d. Distribute the numbers: Iterate through the list of numbers again, and for each number, find its digit at the current position. Use the count +array to determine the correct position of the number in the output array and place it there. After placing the number, decrement the count for +that digit in the count array. +e. Collect the numbers: After distributing all the numbers, collect them back into the original array. The array will now be partially sorted +based on the current digit position. + +3. Repeat the counting sort for the next digit position: After collecting the numbers based on the least significant digit, move to the next digit +position (towards the left) and repeat steps 2a to 2e for that digit. Continue this process until all the digits have been processed, from the least +significant digit to the most significant digit. + +4. Final sorted list: After completing the counting sort process for all digit positions, you will have a fully sorted list of numbers. + +Here's an example to illustrate the process: + +Sample Input: [170, 45, 75, 90, 802, 24, 2, 66] +Maximum number: 802 + +# 1. First iteration (Least significant digit - rightmost digit): + # Create the count array: [0, 2, 1, 1, 0, 1, 0, 0, 0, 1] + # Update the count array: [0, 2, 3, 4, 4, 5, 5, 5, 5, 6] + # Distribute the numbers: [802, 2, 24, 45, 75, 170, 90, 66] + # Collect the numbers: [802, 2, 24, 45, 75, 170, 90, 66] + +# 2. Second iteration (Next least significant digit): + # Create the count array: [1, 2, 1, 1, 1, 1, 0, 0, 0, 1] + # Update the count array: [1, 3, 4, 5, 6, 7, 7, 7, 7, 8] + # Distribute the numbers: [802, 2, 24, 45, 66, 75, 90, 170] + # Collect the numbers: [802, 2, 24, 45, 66, 75, 90, 170] + +# 3. Third iteration (Most significant digit): + # Create the count array: [1, 1, 1, 1, 2, 2, 1, 0, 0, 0] + # Update the count array: [1, 2, 3, 4, 6, 8, 9, 9, 9, 9] + # Distribute the numbers: [2, 24, 45, 66, 75, 90, 170, 802] + # Collect the numbers: [2, 24, 45, 66, 75, 90, 170, 802] + +# The final sorted list is [2, 24, 45, 66, 75, 90, 170, 802]. + +# Radix sort using counting sort works by sorting the numbers digit by digit, from the least significant digit to the most significant digit. +# The counting sort process distributes and collects the numbers based on each digit position, ensuring that the numbers are correctly ordered at +# each iteration. By repeating this process for each digit, the algorithm achieves a fully sorted list without the need for explicit element comparisons. + +*/ + +import java.io.*; +import java.util.*; + +class Radix { + + // A utility function to get maximum value in arr[] + static int getMax(int arr[], int n) + { + int mx = arr[0]; + for (int i = 1; i < n; i++) + if (arr[i] > mx) + mx = arr[i]; + return mx; + } + + // A function to do counting sort of arr[] according to + // the digit represented by exp. + static void countSort(int arr[], int n, int exp) + { + int output[] = new int[n]; // output array + int i; + int count[] = new int[10]; + Arrays.fill(count, 0); + + // Store count of occurrences in count[] + for (i = 0; i < n; i++) + count[(arr[i] / exp) % 10]++; + + // Change count[i] so that count[i] now contains + // actual position of this digit in output[] + for (i = 1; i < 10; i++) + count[i] += count[i - 1]; + + // Build the output array + for (i = n - 1; i >= 0; i--) { + output[count[(arr[i] / exp) % 10] - 1] = arr[i]; + count[(arr[i] / exp) % 10]--; + } + + // Copy the output array to arr[], so that arr[] now + // contains sorted numbers according to current + // digit + for (i = 0; i < n; i++) + arr[i] = output[i]; + } + + // The main function to that sorts arr[] of + // size n using Radix Sort + static void radixsort(int arr[], int n) + { + // Find the maximum number to know number of digits + int m = getMax(arr, n); + + // Do counting sort for every digit. Note that + // instead of passing digit number, exp is passed. + // exp is 10^i where i is current digit number + for (int exp = 1; m / exp > 0; exp *= 10) + countSort(arr, n, exp); + } + + // A utility function to print an array + static void print(int arr[], int n) + { + for (int i = 0; i < n; i++) + System.out.print(arr[i] + " "); + } + + // Main driver method + public static void main(String[] args) + { + int arr[] = { 170, 45, 75, 90, 802, 24, 2, 66 }; + int n = arr.length; + + // Function Call + radixsort(arr, n); + print(arr, n); + } +} + +// Time Compexity -> O((n+k)*d) +// Space Complexity -> O(n+k) \ No newline at end of file diff --git a/sorting/radix_sort.js b/sorting/radix_sort.js new file mode 100644 index 00000000..de6ec097 --- /dev/null +++ b/sorting/radix_sort.js @@ -0,0 +1,130 @@ +/* + Radix Sort +1. Identify the maximum number: Find the maximum number in the given list. This is necessary to determine the number of digits we need to consider +during the sorting process. + +2. Perform counting sort for each digit position: Starting from the least significant digit (rightmost digit), perform the following steps for each +digit position, moving towards the most significant digit (leftmost digit): +a. Create a count array: Create a count array of size 10 (to represent digits 0-9) and initialize all elements to 0. This count array will be used +to store the frequency of each digit at the current position. +b. Count the frequencies: Iterate through the list of numbers and count the frequency of each digit at the current position. For example, if the +current digit position is the units place, count the frequency of each digit from 0 to 9. +c. Update the count array: Modify the count array such that each element represents the cumulative count of digits up to that index. This step +ensures that the count array contains the correct positions for each digit in the sorted order. +d. Distribute the numbers: Iterate through the list of numbers again, and for each number, find its digit at the current position. Use the count +array to determine the correct position of the number in the output array and place it there. After placing the number, decrement the count for +that digit in the count array. +e. Collect the numbers: After distributing all the numbers, collect them back into the original array. The array will now be partially sorted +based on the current digit position. + +3. Repeat the counting sort for the next digit position: After collecting the numbers based on the least significant digit, move to the next digit +position (towards the left) and repeat steps 2a to 2e for that digit. Continue this process until all the digits have been processed, from the least +significant digit to the most significant digit. + +4. Final sorted list: After completing the counting sort process for all digit positions, you will have a fully sorted list of numbers. + +Here's an example to illustrate the process: + +Sample Input: [170, 45, 75, 90, 802, 24, 2, 66] +Maximum number: 802 + +# 1. First iteration (Least significant digit - rightmost digit): + # Create the count array: [0, 2, 1, 1, 0, 1, 0, 0, 0, 1] + # Update the count array: [0, 2, 3, 4, 4, 5, 5, 5, 5, 6] + # Distribute the numbers: [802, 2, 24, 45, 75, 170, 90, 66] + # Collect the numbers: [802, 2, 24, 45, 75, 170, 90, 66] + +# 2. Second iteration (Next least significant digit): + # Create the count array: [1, 2, 1, 1, 1, 1, 0, 0, 0, 1] + # Update the count array: [1, 3, 4, 5, 6, 7, 7, 7, 7, 8] + # Distribute the numbers: [802, 2, 24, 45, 66, 75, 90, 170] + # Collect the numbers: [802, 2, 24, 45, 66, 75, 90, 170] + +# 3. Third iteration (Most significant digit): + # Create the count array: [1, 1, 1, 1, 2, 2, 1, 0, 0, 0] + # Update the count array: [1, 2, 3, 4, 6, 8, 9, 9, 9, 9] + # Distribute the numbers: [2, 24, 45, 66, 75, 90, 170, 802] + # Collect the numbers: [2, 24, 45, 66, 75, 90, 170, 802] + +# The final sorted list is [2, 24, 45, 66, 75, 90, 170, 802]. + +# Radix sort using counting sort works by sorting the numbers digit by digit, from the least significant digit to the most significant digit. +# The counting sort process distributes and collects the numbers based on each digit position, ensuring that the numbers are correctly ordered at +# each iteration. By repeating this process for each digit, the algorithm achieves a fully sorted list without the need for explicit element comparisons. + +*/ + +// A utility function to get maximum value in arr[] +function getMax(arr,n) +{ + let mx = arr[0]; + for (let i = 1; i < n; i++) + if (arr[i] > mx) + mx = arr[i]; + return mx; +} + +// A function to do counting sort of arr[] according to + // the digit represented by exp. +function countSort(arr,n,exp) +{ + let output = new Array(n); // output array + let i; + let count = new Array(10); + for(let i=0;i<10;i++) + count[i]=0; + + // Store count of occurrences in count[] + for (i = 0; i < n; i++) { + let x = Math.floor(arr[i] / exp) % 10; + count[x]++; + } + + // Change count[i] so that count[i] now contains + // actual position of this digit in output[] + for (i = 1; i < 10; i++) + count[i] += count[i - 1]; + + // Build the output array + for (i = n - 1; i >= 0; i--) { + output[count[x] - 1] = arr[i]; + count[x]--; + } + + // Copy the output array to arr[], so that arr[] now + // contains sorted numbers according to current digit + for (i = 0; i < n; i++) + arr[i] = output[i]; +} + +// The main function to that sorts arr[] of size n using + // Radix Sort +function radixsort(arr,n) +{ + // Find the maximum number to know number of digits + let m = getMax(arr, n); + + // Do counting sort for every digit. Note that + // instead of passing digit number, exp is passed. + // exp is 10^i where i is current digit number + for (let exp = 1; Math.floor(m / exp) > 0; exp *= 10) + countSort(arr, n, exp); +} + +// A utility function to print an array +function print(arr,n) +{ + for (let i = 0; i < n; i++) + document.write(arr[i] + " "); +} + +/*Driver Code*/ +let arr=[170, 45, 75, 90, 802, 24, 2, 66]; +let n = arr.length; + +// Function Call +radixsort(arr, n); +print(arr, n); + +// Time Compexity -> O((n+k)*d) +// Space Complexity -> O(n+k) \ No newline at end of file diff --git a/sorting/radix_sort.py b/sorting/radix_sort.py new file mode 100644 index 00000000..b9e96bbe --- /dev/null +++ b/sorting/radix_sort.py @@ -0,0 +1,115 @@ + +# Radix Sort +# 1. Identify the maximum number: Find the maximum number in the given list. This is necessary to determine the number of digits we need to consider +# during the sorting process. + +# 2. Perform counting sort for each digit position: Starting from the least significant digit (rightmost digit), perform the following steps for each +# digit position, moving towards the most significant digit (leftmost digit): +# a. Create a count array: Create a count array of size 10 (to represent digits 0-9) and initialize all elements to 0. This count array will be used +# to store the frequency of each digit at the current position. +# b. Count the frequencies: Iterate through the list of numbers and count the frequency of each digit at the current position. For example, if the +# current digit position is the units place, count the frequency of each digit from 0 to 9. +# c. Update the count array: Modify the count array such that each element represents the cumulative count of digits up to that index. This step +# ensures that the count array contains the correct positions for each digit in the sorted order. +# d. Distribute the numbers: Iterate through the list of numbers again, and for each number, find its digit at the current position. Use the count +# array to determine the correct position of the number in the output array and place it there. After placing the number, decrement the count for +# that digit in the count array. +# e. Collect the numbers: After distributing all the numbers, collect them back into the original array. The array will now be partially sorted +# based on the current digit position. + +# 3. Repeat the counting sort for the next digit position: After collecting the numbers based on the least significant digit, move to the next digit +# position (towards the left) and repeat steps 2a to 2e for that digit. Continue this process until all the digits have been processed, from the least +# significant digit to the most significant digit. + +# 4. Final sorted list: After completing the counting sort process for all digit positions, you will have a fully sorted list of numbers. + +# Here's an example to illustrate the process: + +# Sample Input: [170, 45, 75, 90, 802, 24, 2, 66] +# Maximum number: 802 + +# 1. First iteration (Least significant digit - rightmost digit): + # Create the count array: [0, 2, 1, 1, 0, 1, 0, 0, 0, 1] + # Update the count array: [0, 2, 3, 4, 4, 5, 5, 5, 5, 6] + # Distribute the numbers: [802, 2, 24, 45, 75, 170, 90, 66] + # Collect the numbers: [802, 2, 24, 45, 75, 170, 90, 66] + +# 2. Second iteration (Next least significant digit): + # Create the count array: [1, 2, 1, 1, 1, 1, 0, 0, 0, 1] + # Update the count array: [1, 3, 4, 5, 6, 7, 7, 7, 7, 8] + # Distribute the numbers: [802, 2, 24, 45, 66, 75, 90, 170] + # Collect the numbers: [802, 2, 24, 45, 66, 75, 90, 170] + +# 3. Third iteration (Most significant digit): + # Create the count array: [1, 1, 1, 1, 2, 2, 1, 0, 0, 0] + # Update the count array: [1, 2, 3, 4, 6, 8, 9, 9, 9, 9] + # Distribute the numbers: [2, 24, 45, 66, 75, 90, 170, 802] + # Collect the numbers: [2, 24, 45, 66, 75, 90, 170, 802] + +# The final sorted list is [2, 24, 45, 66, 75, 90, 170, 802]. + +# Radix sort using counting sort works by sorting the numbers digit by digit, from the least significant digit to the most significant digit. +# The counting sort process distributes and collects the numbers based on each digit position, ensuring that the numbers are correctly ordered at +# each iteration. By repeating this process for each digit, the algorithm achieves a fully sorted list without the need for explicit element comparisons. + +# Method to do Radix Sort +def countingSort(arr, exp1): + + n = len(arr) + + # The output array elements that will have sorted arr + output = [0] * (n) + + # initialize count array as 0 + count = [0] * (10) + + # Store count of occurrences in count[] + for i in range(0, n): + index = arr[i] // exp1 + count[index % 10] += 1 + + # Change count[i] so that count[i] now contains actual + # position of this digit in output array + for i in range(1, 10): + count[i] += count[i - 1] + + # Build the output array + i = n - 1 + while i >= 0: + index = arr[i] // exp1 + output[count[index % 10] - 1] = arr[i] + count[index % 10] -= 1 + i -= 1 + + # Copying the output array to arr[], + # so that arr now contains sorted numbers + i = 0 + for i in range(0, len(arr)): + arr[i] = output[i] + +# Method to do Radix Sort +def radixSort(arr): + + # Find the maximum number to know number of digits + max1 = max(arr) + + # Do counting sort for every digit. Note that instead + # of passing digit number, exp is passed. exp is 10^i + # where i is current digit number + exp = 1 + while max1 / exp >= 1: + countingSort(arr, exp) + exp *= 10 + + +# Driver code +arr = [170, 45, 75, 90, 802, 24, 2, 66] + +# Function Call +radixSort(arr) + +for i in range(len(arr)): + print(arr[i],end=" ") + +# Time Compexity -> O((n+k)*d) +# Space Complexity -> O(n+k) \ No newline at end of file diff --git a/sorting/selection_sort.cpp b/sorting/selection_sort.cpp new file mode 100644 index 00000000..0edf6b9b --- /dev/null +++ b/sorting/selection_sort.cpp @@ -0,0 +1,59 @@ +/* + Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an + unsorted part of the array and putting it at the beginning of the array. + + In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum + element, and if a smaller element is found, it becomes the new minimum element. + + Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. + + This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide + the array into two parts: a sorted part and an unsorted part. + + Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from + the unsorted part and added to the end of the sorted part. + + The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number + of elements in the array. This is because for each element in the array, it needs to compare it with + every other element to find the smallest one, which takes O(n) time. Since this process needs to be + repeated for each element, the overall time complexity becomes O(n^2). + + The space complexity of selection sort is O(1) because it does not require any extra space other than + the input array itself. It performs the sorting in place by swapping the elements within the array. +*/ +// Sample Input : [2, 1, 9, 3, 5, 4, 0] +// Output : [0 1 2 3 4 5 9] +#include +#include + +using namespace std; + +// Function to perform selection sort on a vector +void selectionSort(vector& arr) { + int n = arr.size(); + + // Iterate through the array + for (int i = 0; i < n - 1; i++) { + int minIdx = i; + + // Find the index of the minimum element in the unsorted part of the array + for (int j = i + 1; j < n; j++) { + if (arr[j] < arr[minIdx]) { + minIdx = j; + } + } + + // Swap the minimum element with the first element of the unsorted part of the array + swap(arr[i], arr[minIdx]); + } +} + +int main() { + // Example usage + vector arr = {64, 25, 12, 22, 11}; + selectionSort(arr); + for (int i = 0; i < arr.size(); i++) { + cout << arr[i] << " "; + } + return 0; +} diff --git a/sorting/selection_sort.go b/sorting/selection_sort.go new file mode 100644 index 00000000..24c45f6d --- /dev/null +++ b/sorting/selection_sort.go @@ -0,0 +1,56 @@ +/* + Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an + unsorted part of the array and putting it at the beginning of the array. + + In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum + element, and if a smaller element is found, it becomes the new minimum element. + + Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. + + This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide + the array into two parts: a sorted part and an unsorted part. + + Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from + the unsorted part and added to the end of the sorted part. + + The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number + of elements in the array. This is because for each element in the array, it needs to compare it with + every other element to find the smallest one, which takes O(n) time. Since this process needs to be + repeated for each element, the overall time complexity becomes O(n^2). + + The space complexity of selection sort is O(1) because it does not require any extra space other than + the input array itself. It performs the sorting in place by swapping the elements within the array. +*/ +// Sample Input : [2, 1, 9, 3, 5, 4, 0] +// Output : [0 1 2 3 4 5 9] +package main + +import "fmt" + +// selectionSort sorts an array of integers in ascending order +// using the selection sort algorithm. +func selectionSort(arr []int) { + // Loop over the array from start to end + for i := 0; i < len(arr); i++ { + // Assume the current index contains the minimum value + minIdx := i + // Loop over the rest of the array to find the minimum value + for j := i + 1; j < len(arr); j++ { + // If a value is found that is smaller than the current minimum, + // update the minimum index + if arr[j] < arr[minIdx] { + minIdx = j + } + } + // Swap the minimum value with the current index + arr[i], arr[minIdx] = arr[minIdx], arr[i] + } +} + +func main() { + // Example usage + arr := []int{5, 3, 6, 2, 10} + fmt.Println("Before sorting:", arr) + selectionSort(arr) + fmt.Println("After sorting:", arr) +} diff --git a/sorting/selection_sort.java b/sorting/selection_sort.java new file mode 100644 index 00000000..3c816761 --- /dev/null +++ b/sorting/selection_sort.java @@ -0,0 +1,49 @@ +/* + Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an + unsorted part of the array and putting it at the beginning of the array. + + In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum + element, and if a smaller element is found, it becomes the new minimum element. + + Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. + + This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide + the array into two parts: a sorted part and an unsorted part. + + Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from + the unsorted part and added to the end of the sorted part. + + The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number + of elements in the array. This is because for each element in the array, it needs to compare it with + every other element to find the smallest one, which takes O(n) time. Since this process needs to be + repeated for each element, the overall time complexity becomes O(n^2). + + The space complexity of selection sort is O(1) because it does not require any extra space other than + the input array itself. It performs the sorting in place by swapping the elements within the array. +*/ +// Sample Input : [2, 1, 9, 3, 5, 4, 0] +// Output : [0 1 2 3 4 5 9] + +/** + * Sorts an array of integers in ascending order using the Selection Sort algorithm. + * + * @param arr the array to be sorted + * @return the sorted array + */ +public static int[] selectionSort(int[] arr) { + // iterate through the array + for (int i = 0; i < arr.length - 1; i++) { + int minIdx = i; + // find the index of the smallest element in the unsorted portion of the array + for (int j = i + 1; j < arr.length; j++) { + if (arr[j] < arr[minIdx]) { + minIdx = j; + } + } + // swap the smallest element with the first unsorted element + int temp = arr[minIdx]; + arr[minIdx] = arr[i]; + arr[i] = temp; + } + return arr; +} diff --git a/sorting/selection_sort.js b/sorting/selection_sort.js new file mode 100644 index 00000000..89d6c950 --- /dev/null +++ b/sorting/selection_sort.js @@ -0,0 +1,53 @@ +/* + Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an + unsorted part of the array and putting it at the beginning of the array. + + In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum + element, and if a smaller element is found, it becomes the new minimum element. + + Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. + + This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide + the array into two parts: a sorted part and an unsorted part. + + Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from + the unsorted part and added to the end of the sorted part. + + The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number + of elements in the array. This is because for each element in the array, it needs to compare it with + every other element to find the smallest one, which takes O(n) time. Since this process needs to be + repeated for each element, the overall time complexity becomes O(n^2). + + The space complexity of selection sort is O(1) because it does not require any extra space other than + the input array itself. It performs the sorting in place by swapping the elements within the array. +*/ +// Sample Input : [2, 1, 9, 3, 5, 4, 0] +// Output : [0 1 2 3 4 5 9] +const inputArr = [4, 5, 67, 56, 3, 35, 45]; +/** + * Sorts an array using selection sort algorithm. + * @param {number[]} array - The array to be sorted. + * @returns {number[]} - The sorted array. + */ +function selectionSort(array) { + // Loop through the array from the start to the second last element + for (let i = 0; i < array.length - 1; i++) { + // Assume that the current element is the minimum + let minIndex = i; + // Loop through the rest of the array to find the minimum element + for (let j = i + 1; j < array.length; j++) { + if (array[j] < array[minIndex]) { + // If we find a smaller element, update the minimum index + minIndex = j; + } + } + // Swap the current element with the minimum element + let temp = array[i]; + array[i] = array[minIndex]; + array[minIndex] = temp; + } + // Return the sorted array + return array; +} + +console.log(selectionSort(inputArr)); diff --git a/sorting/selection_sort.py b/sorting/selection_sort.py new file mode 100644 index 00000000..f04c3c1e --- /dev/null +++ b/sorting/selection_sort.py @@ -0,0 +1,54 @@ +''' + Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an + unsorted part of the array and putting it at the beginning of the array. + + In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum + element, and if a smaller element is found, it becomes the new minimum element. + + Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. + + This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide + the array into two parts: a sorted part and an unsorted part. + + Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from + the unsorted part and added to the end of the sorted part. + + The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number + of elements in the array. This is because for each element in the array, it needs to compare it with + every other element to find the smallest one, which takes O(n) time. Since this process needs to be + repeated for each element, the overall time complexity becomes O(n^2). + + The space complexity of selection sort is O(1) because it does not require any extra space other than + the input array itself. It performs the sorting in place by swapping the elements within the array. +''' +# Sample Input : [2, 1, 9, 3, 5, 4, 0] +# Output : [0 1 2 3 4 5 9] + +def selection_sort(arr): + """ + Sorts an array in ascending order using the selection sort algorithm. + + Args: + arr (list): An array of integers. + + Returns: + list: The sorted array in ascending order. + """ + # Loop through the array + for i in range(len(arr)): + # Find the minimum element in the unsorted portion of the array + min_idx = i + for j in range(i+1, len(arr)): + if arr[j] < arr[min_idx]: + min_idx = j + + # Swap the minimum element with the first element in the unsorted portion of the array + arr[i], arr[min_idx] = arr[min_idx], arr[i] + + return arr + +data = [-2, 45, 0, 11, -9] +size = len(data) +selection_sort(data, size) +print('Sorted Array in Ascending Order:') +print(data) \ No newline at end of file diff --git a/sorting/selectionsort.cpp b/sorting/selectionsort.cpp deleted file mode 100644 index 984e3e24..00000000 --- a/sorting/selectionsort.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Implementation of selection sort. -// Selection sort is an in-place comparison sorting algorithm. -// It has an O(n^{2}), time complexity which makes it inefficient on large lists, -// and generally performs worse than the similar insertion sort. -// Selection sort is noted for its simplicity and has performance advantages -// over more complicated algorithms in certain situations, -// particularly where auxiliary memory is limited. (Source wiki) https://en.wikipedia.org/wiki/Selection_sort - -// Sample Input : [2, 1, 9, 3, 5, 4, 0] -// Output : [0 1 2 3 4 5 9] -#include -using namespace std; - -void SelectionSort(int A[], int n){ - for(int i = 0; i < n - 1; i++){ - int imin = i; - for(int j = i + 1; j < n; j++){ - if(A[j] < A[imin]) - imin = j; - } - int temp = A[i]; - A[i] = A[imin]; - A[imin] = temp; - } -} -int main(){ - int A[] = {5,4,3,6,7,8,9,1,2,10}; - SelectionSort(A,10); - for(int i = 0; i < 10; i++){ - cout << A[i] << " "; - } - -return 0; -} diff --git a/sorting/selectionsort.go b/sorting/selectionsort.go deleted file mode 100644 index f17be560..00000000 --- a/sorting/selectionsort.go +++ /dev/null @@ -1,34 +0,0 @@ -// Implementation of selection sort. -// Selection sort is an in-place comparison sorting algorithm. -// It has an O(n^{2}) time complexity which makes it inefficient on large lists, -// and generally performs worse than the similar insertion sort. -// Selection sort is noted for its simplicity and has performance advantages -// over more complicated algorithms in certain situations, -// particularly where auxiliary memory is limited. (Source wiki) https://en.wikipedia.org/wiki/Selection_sort - -// Sample Input : [2, 1, 9, 3, 5, 4, 0] -// Output : [0 1 2 3 4 5 9] -package main - -import "fmt" - -func SelectionSort(arr []int, length int) []int { - for i := 0; i < length-1; i++ { - imin := i - for j := i + 1; j < length; j++ { - // find minumim element's index - if arr[j] < arr[imin] { - imin = j - } - } - // bring min element to front - arr[i], arr[imin] = arr[imin], arr[i] - } - return arr -} - -func main() { - arr := []int{2, 1, 9, 3, 5, 4, 0} - msg := SelectionSort(arr, 7) - fmt.Println(msg) -} \ No newline at end of file diff --git a/sorting/selectionsort.js b/sorting/selectionsort.js deleted file mode 100644 index ce5d2bcd..00000000 --- a/sorting/selectionsort.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -Selection sort is a sorting algorithm that selects the smallest element from an unsorted list in each iteration - and places that element at the beginning of the unsorted list. -*/ - -const inputArr = [4,5,67,56,3,35,45]; - -const selectionSort= (inputArr) => { - let n = inputArr.length; - - //loop to access each array element - for(let i = 0; i < n; i++) { - - //assuming first array element as current minimum - let min= i; - - //loop to compare current minimum element to other array element - for(let j= i+1; j< n; j++){ - - //comparing and updating cureent minimum element - if(inputArr[j] < inputArr[min]) { - min=j; - } - } - - //swapping array elements if current minimum changes - if (min!= i) { - let temp= inputArr[i]; - inputArr[i] = inputArr[min]; - inputArr[min] = temp; - } - } - return inputArr; -} - -console.log(selectionSort(inputArr)); \ No newline at end of file diff --git a/sorting/tim_sort.cpp b/sorting/tim_sort.cpp new file mode 100644 index 00000000..a9796d84 --- /dev/null +++ b/sorting/tim_sort.cpp @@ -0,0 +1,85 @@ +//Implementation of Tim Sort +//Tim Sort makes use of insertionSort and Merge function of MergeSort +//The basic idea is to divide the input array into blocks called as runs +//The size of runs varies from 32 to 64, and perform insertion sort on the runs +//Then, The runs(blocks) are merged to form the final sorted array +// +//Time Complexity of this algorithm in Best case is O(n),Average case is O(n*log(n)) +//Time Complexity in Worst case is O(n*log(n)). +//This is a stable sorting algorithm +//This algorithm uses Space of O(n) +//This algorithm is used in languages like Java and Python for sorting. + +#include +using namespace std; +const int run=32; +void insertionSort(vector&array,int l,int r) +{ + for(int i = l + 1; i <= r; i++){ + int temp = array[i]; + int j = i - 1; + while(j >= l and array[j] > temp) { + array[j + 1] = array[j]; + j--; + } + array[j + 1] = temp; + } +} +void merge(vector&array,int l,int m,int r) { + int len1 = m - l + 1, len2 = r - m; + vectorleft(len1), right(len2); + + for(int i = 0; i < len1; i++){ + left[i] = array[l + i]; + } + for(int i = 0; i < len2; i++){ + right[i] = array[m + 1 + i]; + } + + int i = 0, j = 0, k = l; + + while(i < len1 and j < len2) { + if(left[i] <= right[j]) + array[k] = left[i++]; + else array[k] = right[j++]; + k++; + } + + while(i&array, int n) { + // calling insertion sort on blocks of size equal to run=32 + // if the size of array is less than defined run size, then the insertion sort would have sorted the array and there is no need of merging. + for(int i = 0; i < n; i += run) { + insertionSort(array, i, min((i + run - 1), n - 1)); + } + //we merge from size run,the array merges to form sizes 64,128,... + for(int size = run; size < n; size = 2 * size) { + //we merge from array[left,left+size-1] + //and array[left+size,left+2*size] + //after every merge,we increase left by 2*size + for(int left = 0; left < n; left += 2 * size) { + int mid = left + size - 1;//ending point of left subarray + int right = min((left + 2 * size - 1), n - 1);//ending point of right subarray + if(mid < right) + merge(array, left, mid, right); //calling the merge function + } + } + for(int i: array)//printing the sorted array + cout << i <<" "; + //sample output 1,2,3,4,5,6,7,8,9 +} +int main() +{ + vector array = {9,8,7,6,5,4,3,2,1}; + int n = array.size(); + timSort(array, n);//call to TimSort function + return 0; +} diff --git a/sorting/tim_sort.go b/sorting/tim_sort.go new file mode 100644 index 00000000..7c30ead6 --- /dev/null +++ b/sorting/tim_sort.go @@ -0,0 +1,118 @@ +// File: tim_sort.go +//Implementation of Tim Sort +//Tim Sort makes use of insertionSort and Merge function of MergeSort +//The basic idea is to divide the input array into blocks called as runs +//The size of runs varies from 32 to 64, and perform insertion sort on the runs +//Then, The runs(blocks) are merged to form the final sorted array +// +//Time Complexity of this algorithm in Best case is O(n),Average case is O(n*log(n)) +//Time Complexity in Worst case is O(n*log(n)). +//This is a stable sorting algorithm +//This algorithm uses Space of O(n) +//This algorithm is used in languages like Java and Python for sorting. + +package main + +import ( + "fmt" + "math" +) + +func main() { + + arr := []int{43, 56, 2, 99, 1, 64, 23, 78, 34, 11, 90, 45, 32, 67, 88, 12, 9, 10, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + n := len(arr) + fmt.Printf("Before Sorting\n") + printArray(arr, n) + timSort(arr, n) + fmt.Printf("Sorted Array is\n") + printArray(arr, n) +} + +func timSort(arr []int, n int) { + + run := 32 + // you can take any value between 32 to 64, it actually is some empirical result from insertion s + for i := 0; i < n; i += run { + insertionSort(arr, i, int(math.Min(float64(i + 31), float64(n - 1)))) + } + + for size := run; size < n; size = 2 * size { + for left := 0; left < n; left += 2 * size { + mid := left + size - 1 + right := int(math.Min(float64(left + 2 * size - 1), float64(n - 1))) + merge(arr, left, mid, right) + } + } +} + +func insertionSort(arr []int, left int, right int) { + for i := left + 1; i <= right; i++ { + j := i + for j > left && arr[j] < arr[j - 1] { + arr[j], arr[j - 1] = arr[j - 1], arr[j] + j-- + } + } +} + +func merge(arr []int, left int, mid int, right int) { + + // function to merge the two sorted arrays + len1 := mid - left + 1 + len2 := right - mid + leftArr := make([]int, len1) + rightArr := make([]int, len2) + + for i := 0; i < len1; i++ { + leftArr[i] = arr[left + i] + } + for j := 0; j < len2; j++ { + rightArr[j] = arr[mid + 1 + j] + } + + i := 0 + j := 0 + k := left + + for i < len1 && j < len2 { + if leftArr[i] <= rightArr[j] { + arr[k] = leftArr[i] + i++ + } else { + arr[k] = rightArr[j] + j++ + } + k++ + } + + for i < len1 { + arr[k] = leftArr[i] + i++ + k++ + } + + for j < len2 { + arr[k] = rightArr[j] + j++ + k++ + } +} + + +// Function to print the array +func printArray(arr []int, n int) { + for i := 0; i < n; i++ { + fmt.Printf("%d ", arr[i]) + } + fmt.Printf("\n") +} + +// to run in terminal +// $ go run tim_sort.go + +// Output +// Before Sorting +// 43 56 2 99 1 64 23 78 34 11 90 45 32 67 88 12 9 10 3 4 5 6 7 8 9 10 11 12 13 14 15 +// Sorted Array is +// 1 2 3 4 5 6 7 8 9 9 10 10 11 11 12 12 13 14 15 23 32 34 43 45 56 64 67 78 88 90 99 diff --git a/sorting/tim_sort.java b/sorting/tim_sort.java new file mode 100644 index 00000000..ea891b8d --- /dev/null +++ b/sorting/tim_sort.java @@ -0,0 +1,106 @@ +//Implementation of Tim Sort +//Tim Sort makes use of insertionSort and Merge function of MergeSort +//The basic idea is to divide the input array into blocks called as runs +//The size of runs varies from 32 to 64, and perform insertion sort on the runs +//Then, The runs(blocks) are merged to form the final sorted array +// +//Time Complexity of this algorithm in Best case is O(n),Average case is O(n*log(n)) +//Time Complexity in Worst case is O(n*log(n)). +//This is a stable sorting algorithm +//This algorithm uses Space of O(n) +//This algorithm is used in languages like Java and Python for sorting. + +import java.util.Arrays; +class tim_sort +{ + static int run=32; // you can take any value between 32 to 64, it actually is some empirical result from insertion sort + public static void main(String[] args) + { + int[] arr={43,56,2,99,1,64,23,78,34,11,90,45,32,67,88,12,9,10,3,4,5,6,7,8,9,10,11,12,13,14,15}; + System.out.println("Before Sorting: "+Arrays.toString(arr)); + timSort(arr,arr.length); + System.out.println("After Sorting: "+Arrays.toString(arr)); + } + public static void timSort(int[] arr,int n) + { + for(int i=0;i=left && arr[j]>temp) + { + arr[j+1]=arr[j]; + j--; + } + arr[j+1]=temp; + } + } + public static void merge(int[] arr,int left,int mid,int right) + { + int len1=mid-left+1; + int len2=right-mid; + int[] leftArr=new int[len1]; + int[] rightArr=new int[len2]; + for(int i=0;i= MINIMUM: + r |= n & 1 + n >>= 1 + + return n + r + + +# Sorts an array from left to right index (Insertion Sort) +def insertion_sort(array, left, right): + for i in range(left + 1, right + 1): + j = i + while j > left and array[j] < array[j - 1]: + array[j], array[j - 1] = array[j - 1], array[j] + j -= 1 + + +# Merges the sorted runs +def merge(array, l, m, r): + + # original array is broken in two parts: left - right + + array_len1 = m - l + 1 + array_len2 = r - m + + left = [] + right = [] + + for i in range(0, array_len1): + left.append(array[l + i]) + + for i in range(0, array_len2): + right.append(array[m + 1 + i]) + + i = 0 + j = 0 + k = l + + # merge the two arrays in a larger sub array + while i < array_len1 and j < array_len2: + if left[i] <= right[j]: + array[k] = left[i] + i += 1 + + else: + array[k] = right[j] + j += 1 + + k += 1 + + # Copy remaining elements of the left part, if any + while i < array_len1: + array[k] = left[i] + k += 1 + i += 1 + + # Copy remaining element of the right part, if any + while j + +/* +Wave sort, also known as "odd-even sort," is a simple sorting algorithm that works by repeatedly +comparing and swapping adjacent elements in an array until the array is sorted. +The algorithm gets its name from the pattern of element swaps, which resembles a wave-like motion. +In this sorting method we arrange array in +Arr[0] >=arr[1]<=arr[2]>=arr[3]<=arr[4]………. Like a wave +*/ + +using namespace std; + +void wavesort(int arr[], int n) +{ + for (int i = 1; i < n; i += 2) + { + if (arr[i] > arr[i - 1]) + { + swap(arr[i], arr[i - 1]); + } + if (arr[i] > arr[i + 1] && i <= n - 2) + { + swap(arr[i], arr[i + 1]); + } + } +} + +int main() +{ + int arr[9] = {1, 3, 4, 7, 5, 6, 2}; + wavesort(arr, 7); + for (int i = 0; i < 7; i++) + { + cout << arr[i] << " "; + } + return 0; +} + +/* +Time Complexity: O(N) +Auxiliary Space: O(1) +*/ + +/* +The idea is based on the fact that if we make sure that all even positioned (at index 0, 2, 4, ..) +elements are greater than their adjacent odd elements, we don’t need to worry about oddly positioned elements. + +- Traverse all even positioned elements of the input array, and do the following. + - If the current element is smaller than the previous odd element, swap the previous and current. + - If the current element is smaller than the next odd element, swap next and current. +*/ + +/* +Dry-Run +Consider the array: [7, 2, 5, 1, 8, 3] + +Step 1: + +Start with the initial array: [7, 2, 5, 1, 8, 3] +In the first iteration, we compare and swap adjacent elements at even indices (0, 2, 4). +Compare 7 and 2: 7 > 2, so we swap them. Array becomes: [2, 7, 5, 1, 8, 3] +Compare 7 and 8: 7 < 8, so no swap is needed. +After the first phase, the array becomes: [2, 7, 5, 1, 8, 3] +Step 2: + +In the second iteration, we compare and swap adjacent elements at odd indices (1, 3, 5). +Compare 7 and 5: 7 > 5, so we swap them. Array becomes: [2, 5, 7, 1, 8, 3] +Compare 7 and 1: 7 > 1, so we swap them. Array becomes: [2, 5, 1, 7, 8, 3] +Compare 7 and 3: 7 > 3, so we swap them. Array becomes: [2, 5, 1, 3, 8, 7] +After the second phase, the array becomes: [2, 5, 1, 3, 8, 7] +Step 3: + +In the third iteration, we again compare and swap adjacent elements at even indices (0, 2, 4). +Compare 2 and 5: 2 < 5, so no swap is needed. +Compare 5 and 1: 5 > 1, so we swap them. Array becomes: [1, 5, 2, 3, 8, 7] +Compare 5 and 3: 5 > 3, so we swap them. Array becomes: [1, 3, 2, 5, 8, 7] +After the third phase, the array becomes: [1, 3, 2, 5, 8, 7] +Step 4: + +In the fourth iteration, we compare and swap adjacent elements at odd indices (1, 3, 5). +Compare 3 and 2: 3 > 2, so we swap them. Array becomes: [1, 2, 3, 5, 8, 7] +Compare 3 and 5: 3 < 5, so no swap is needed. +Compare 5 and 8: 5 < 8, so no swap is needed. +After the fourth phase, the array remains the same: [1, 2, 3, 5, 8, 7] +Step 5: + +In the fifth iteration, we again compare and swap adjacent elements at even indices (0, 2, 4). +Compare 1 and 2: 1 < 2, so no swap is needed. +Compare 2 and 3: 2 < 3, so no swap is needed. +Compare 3 and 5: 3 < 5, so no swap is needed. +After the fifth phase, the array remains the same: [1, 2, 3, 5, 8, 7] +Step 6: + +In the sixth iteration, we compare and swap adjacent elements at odd indices (1, 3, 5). +Compare 2 and 3: 2 < 3, so no swap is needed. +Compare 3 and 5: 3 < 5, so no swap is needed. +Compare 5 and 7: 5 < 7, so no swap is needed. +After the sixth phase, the array remains the same: [1, 2, 3, 5, 8, 7] +Since no more swaps occur in the array, the algorithm terminates, and the array is considered sorted. + +Final sorted array: [1, 2, 3, 5, 7, 8] +*/ \ No newline at end of file